Eigentlich wollte ich einen Artikel darüber schreiben, wie einzelne Patterns in einem Block zur Auswahl dargestellt werden könnten, wie die Layout-Optionen im Spalten-Block.

Letztlich wurde es jetzt aber erst mal ein Block, der alle Block-Vorlagen zur Auswahl anbietet und gleichzeitig für jede Block-Vorlage eine Variation beinhaltet, sodass Patterns auch direkt über den Block-Inserter eingefügt werden können. So können sich Benutzerinnen und Benutzer, die häufig Patterns verwenden und nicht mehr die visuelle Vorschau des Pattern-Inserters benötigen, einige Zeit beim Einfügen der Patterns sparen. 🎉

Es ist einiges etwas experimentell

Ein paar wesentliche Dinge, die ich in dem Block einsetze, sind noch als experimentell gekennzeichnet. Es könnte also zum Einen sein, dass sich da noch Funktionen ändern, zum Andern werden sich mit Sicherheit irgendwann die Funktionsnamen dieser experimentellen Features ändern.

Dieser experimentelle Status betrifft einmal die Möglichkeit, ein Array aller Block-Patterns zu ermitteln, und andererseits das Interface zum Auswählen einer Variation, wie sie beispielsweise beim Spalten-Block bereits im Einsatz ist. Ich gehe mal optimistisch davon aus, dass weder das eine noch das andere komplett gelöscht werden, sondern sich maximal noch Details ändern werden.

Jetzt aber zum interessanten Teil:

Patterns-Poweruser-Plugin-Programmierung

Ich habe den Block als kleines Plugin geschrieben und werde im Folgenden den gesamten Code Schritt für Schritt durchgehen, und nur Dinge wie den Plugin-Header-Kommentar auslassen.

Der PHP-Code

Wir beginnen in der PHP-Datei mit dem Setzen eines Namespaces, dem Importieren zweier Klassen und der Prüfung, ob wir uns im WordPress-Kontext befinden:

namespace KrautPress\PatternsBlock;

use WP_Block_Patterns_Registry;
use Error;

if ( ! defined( 'WPINC' ) ) {
    die;
}Code-Sprache: PHP (php)

Jetzt werden wir die Script-Datei registrieren, die den Block darstellt, sowie ein Stylesheet mit ein paar Design-Anpassungen für den Editor:

/**
 * Register script and style.
 */
add_action( 'init'function() {
    $script_asset_path = __DIR__ . '/build/index.asset.php';
    if ( ! file_exists( $script_asset_path ) ) {
        throw new Error(
            'You need to run `npm start` or `npm run build` first.'
        );
    }

    $script_asset = require( $script_asset_path );
    wp_register_script(
        'krautpress-patterns-block-editor',
        plugins_url( 'build/index.js'__FILE__ ),
        $script_asset['dependencies'],
        $script_asset['version']
    );
    
    wp_register_style(
        'krautpress-patterns-block-editor-style',
        plugins_url( 'build/index.css'__FILE__ ),
        [],
        $script_asset['version']
    );
} );Code-Sprache: PHP (php)

Wir hängen uns für die Registrierung an den init-Hook und holen uns zunächst in $script_asset Informationen zu dem JavaScript, wie die benötigten Abhängigkeiten. Die Generierung dieser Datei ist eine Funktion, die das NPM-Paket @wordpress/scripts mitbringt.

Danach registrieren wir das Script über wp_register_script() und geben als Abhängigkeiten $script_asset['dependencies'] und als Version $script_asset['version'] an. Abschließend folgt die Registrierung des Stylesheets über wp_register_style().

Jetzt kümmern wir uns um die Einbindung des Skripts und Stylesheets sowie notwendiger zusätzlicher Daten:

/**
 * Enqueue block script and style.
 */
add_action( 'enqueue_block_editor_assets'function() {
    wp_enqueue_script( 'krautpress-patterns-block-editor' );

    wp_enqueue_style( 'krautpress-patterns-block-editor-style' );

    // Get all patterns.
    $block_patterns = WP_Block_Patterns_Registry::get_instance()
        ->get_all_registered();

    if ( empty( $block_patterns ) ) {
        return;
    }

    // Build variation array.
    $variation_data = [];

    /**
     * Filter that allows to set a default variation.
     * With that, the patterns block cannot be selected from the inserter,
     * but only the variations.
     * 
     * @param string Name of the variation to use as default.
     */
    $default_variation_name = (string) apply_filters(
        'krautpress_patterns_block_default_variation_name',
        ''
    );

    foreach ( $block_patterns as $pattern ) {
        $variation_data[] = (object) [
            'name' => $pattern['name'],
            'title' => $pattern['title'],
            'isDefault' => $default_variation_name === $pattern['name'] ? true : false,
            'attributes' => (object) [
                'pattern' => $pattern['name'],
            ],
        ];
    }

    /**
     * Filter the variations to show/use in the block.
     * 
     * @param array $variation_data {
     *      Array of variation data objects. Object looks like that:
     *      (object) [
     *          'name' => $pattern['name'], 
     *          'title' => $pattern['title'],
     *          'attributes' => (object) [
     *              'pattern' => $pattern['name'],
     *          ],
     *      ];
     *      
     *      $pattern['name'] must match the `name` of a pattern.
     *      $pattern['title'] is displayed to the user.
     * }
     */
    $variation_data = apply_filters(
        'krautpress_patterns_block_variations_data',
        $variation_data
    );

    wp_localize_script(
        'krautpress-patterns-block-editor',
        'krautpressPatternsBlockVariationData',
        $variation_data
    );

    wp_localize_script(
        'krautpress-patterns-block-editor',
        'krautpressPatternsBlockStrings',
        [
            'title' => __( 'Patterns''krautpress-patterns-block' ),
            'variationPickerLabel' => __( 'Select pattern''krautpress-patterns-block' ),
            'variationPickerInstructions' => __( 'Choose the pattern you want to insert (it will replace this block).''krautpress-patterns-block' )
        ]
    );
} );
Code-Sprache: PHP (php)

Direkt am Anfang der Funktion binden wir das Skript und das Stylesheet ein. Damit wir die Variationen für die Block-Vorlagen erstellen können, brauchen wir die Daten der vorhandenen Vorlagen bei der Block-Registrierung und ermitteln sie deshalb mit PHP und übergeben sie dann an das Skript.

In Zeile 8 holen wir uns ein Array mit den registrierten Block-Patterns und beenden die Funktion vorzeitig, wenn kein Pattern registriert sein sollte. Danach bauen wir ein Array aus Variation-Objekten zusammen, wobei jedes Objekt die folgenden Daten enthält:

Es folgt ein Filter, um die verfügbaren Variationen anzupassen, und abschließend machen wir das Array mittels wp_localize_script() als krautpressPatternsBlockVariationData-Objekt im Editor verfügbar und binden zusätzlich noch die übersetzbaren Strings ein, die im Block-Editor angezeigt werden.

Der JavaScript-Code

Kommen wir nun zum JavaScript-Code, um den Block umzusetzen, und beginnen mit dem Teil bis zur edit-Funktion, die den Block darstellt:

/* global krautpressBlockPatterns */
// WordPress dependencies.
import {
    registerBlockType,
} from '@wordpress/blocks';
import {
    __experimentalBlockVariationPicker,
} from '@wordpress/block-editor';

registerBlockType( 'krautpress/patterns', {
    title: krautpressPatternsBlockStrings.title,
    category: 'layout',
    attributes: {
        pattern: {
            type: 'string',
            default: '',
        }
    },
    variations: krautpressPatternsBlockVariationData,
    // hier kommt die edit-Funktion hin.
} );
Code-Sprache: JavaScript (javascript)

Wir importieren zunächst die Funktion registerBlockType() und die Komponente __experimentalBlockVariationPicker. In Zeile 10 rufen wir registerBlockType() auf, um den krautpress/patterns-Block zu registrieren.

Wir geben ihm den Titel Patterns und platzieren ihn in der layout-Kategorie. Der Block bekommt nur das pattern-Attribut, das standardmäßig leer ist. Wir erinnern uns: in unseren Variation-Objekten haben wir das Attribut immer auf den Bezeichner des Patterns gesetzt. So können wir nachher in der edit-Funktion prüfen, ob direkt eine Variation im Inserter ausgewählt wurde.

Als letzten Schritt vor dem edit-Teil geben wir unser krautpressPatternsBlockVariationData als Wert für variations an, um die Block-Variationen festzulegen.

registerBlockType( 'krautpress/patterns', {
    // hier kommen die Einträge aus dem vorherigen Code-Block.
    edit: ( props )  => {
        const {
            attributes: {
                pattern
            }
        } = props;

        function replaceBlockWithPattern( patternName, clientId ) {
            // Get all block patterns.
            const { __experimentalBlockPatterns } = wp.data.select( 'core/block-editor' ).getSettings();
            
            // Get block pattern object.
            const blockPattern = __experimentalBlockPatterns.find( blockPattern => blockPattern.name === patternName );
        
            // Parse blocks.
            const blocks = wp.blocks.parse( blockPattern.content );
        
            // Replace current block with blocks from pattern.
            wp.data.dispatch( 'core/block-editor' ).replaceBlock( clientId, blocks );
        }

        if ( pattern !== '' ) {
            replaceBlockWithPattern( pattern, props.clientId );
        }

        return (
            <div>
                <__experimentalBlockVariationPicker
                    label={ krautpressPatternsBlockStrings.variationPickerLabel }
                    instructions={ krautpressPatternsBlockStrings.variationPickerInstructions }
                    variations={ krautpressPatternsBlockVariationData }
                    onSelect={ ( variation ) => {
                        replaceBlockWithPattern( variation.name, props.clientId );
                    } }
                />
            </div>
        )
    }
} );
Code-Sprache: JavaScript (javascript)

Zunächst speichern wir in der edit-Funktion den Wert des Pattern-Attributs in der Konstante pattern. Dann erstellen wir eine replaceBlockWithPattern()-Funktion, die den Namen eines Patterns sowie die clientId des Blocks erwartet, der mit dem Pattern ersetzt werden soll (in unserem Fall die ID des Pattern-Blocks.

Innerhalb der Funktion holen wir uns die Daten zu den Patterns über __experimentalBlockPatterns, das in den Block-Editor-Settings gespeichert ist. Auch hier ist unschwer zu erkennen: zumindest der Name dieses Objekts wird sich in Zukunft noch mal ändern.

In Zeile 15 ermitteln wir aus diesem Array von Pattern-Objekten das zum übergebenen patternName und wandeln das Markup der Pattern-Blöcke mit wp.blocks.parse() in Block-Objekte um. Das Ende der Funktion bildet ein Aufruf der replaceBlock()-Funktion, um den Block mit der übergebenen clientId mit den Pattern-Blöcken zu ersetzen.

In Zeile 24 wird geprüft, ob das pattern-Attribut einen Wert enthält. Das ist der Fall, wenn direkt eine Variation im Block-Inserter gewählt wurde. Wenn die Bedingung erfüllt ist, rufen wir direkt unsere Funktion zum Ersetzen des Blocks auf und übergeben den Namen des Patterns, das im pattern-Attribut gespeichert ist, sowie die clientId unseres gerade vom User eingefügten Blocks.

Wenn kein Pattern-Name vorhanden ist, geben wir die __experimentalBlockVariationPicker-Komponente zurück, die folgende Parameter erhält:

Und der Block sieht dann in WordPress 5.6 mit dem Twenty-Twenty-One-Theme so aus:

Screenshot des User-Interface zur Auswahl eines Patterns.

Wenn direkt aus dem Inserter ein Pattern gewählt wird, dann ist der Block überhaupt nicht sichtbar:

Das Plugin nutzen

Es gibt ein GitHub-Repo zu dem Plugin, in dem ihr den gesamten Code finden könnt. Eine direkt installierbare Version findet ihr auf der Releases-Seite – an jeden Release wird eine krautpress-patterns-block.zip angehängt.

Außerdem wurde das Plugin im WordPress.org-Plugin-Verzeichnis veröffentlicht.