HeadstartWP – Einen benutzerdefinierten Gutenberg-Block erstellen

Wie immer schwimmen wir gegen den Strom und beginnen mit der Dokumentation https://developer.wordpress.org/block-editor/. Man kann über WordPress sagen, was man will, aber die Dokumentation ist zweifellos gut gemacht und eine wahre Fundgrube an Informationen.

Nachdem wir uns die Dokumentation angesehen haben, können wir nun zu diesem Artikel übergehen. Wir werden uns ansehen, wie man Gutenberg-Blöcke innerhalb eines Themes erstellt, und wir werden einen Block erstellen, der in diesem Blog zur Präsentation von Highlights auf der Projektseite verwendet werden soll.

Wie in einem früheren Artikel erläutert, habe ich für meine WordPress-Installation ein Parent-Theme ausgewählt und mein eigenes Child-Theme erstellt. Lassen Sie uns kurz einen Schritt zurückgehen: Alle Tutorials, die ich bisher zum Erstellen von Gutenberg-Blöcken gesehen habe, begannen mit der Erstellung eines Plugins, aber in meinem Fall ziehe ich es vor, direkt in der Vorlage fortzufahren, da diese Blöcke nur in diesem speziellen Theme verwendet werden und sehr einfach sein werden.

Ich habe meine IDE geöffnet und über das Terminal @wordpress/create-block ( https://www.npmjs.com/package/@wordpress/create-block ) aus, um einen Startblock zu erstellen, den ich in den src/blocks/ Ordner meines Child-Themes verschoben habe.

Ich habe die automatisch generierte .php-Datei gelöscht und die Blockregistrierung in die functions.php des Themes verschoben, da ich für diese Blöcke keinen PHP-Code hinzufügen muss.

1 2 3 4 5 6 7 8 function prefix_blocks_init() { $blocks = glob(__DIR__ . '/build/blocks/*'); foreach ( $blocks as $block ) { register_block_type( $block ); } } add_action( 'init', 'prefix_blocks_init' );

Mit dieser einfachen Funktion werde ich alle Blöcke registrieren, die ich im Ordner „blocks“ hinzufügen werde. Ich weise noch einmal darauf hin, dass ihr die einzelnen index.php-Dateien beibehalten könnt, falls PHP-Code für die Blöcke hinzugefügt werden muss.

Außerdem habe ich die Dateien „package.json“ und „package-lock.json“ aus dem jeweiligen Blockordner gelöscht, um Skripte und Abhängigkeiten in die „package.json“ der Vorlage zu verschieben. Da es zwei Skripte gibt, die auf den Befehl „build“ reagieren, habe ich den Skripten der Blöcke einen Suffix hinzugefügt und sie [Befehl]-blocks genannt.

Um zu überprüfen, ob alles korrekt ist, führen Sie einen einfachen npm run build-blocks aus – Sie werden sehen, dass der kompilierte Code im Ordner „build“ erscheint und Ihr Basisblock im Backend der Website verfügbar ist.

Passen wir den Standardblock
an Der Block, den Sie mit @wordpress/create-block

, ist sehr einfach, und wir müssen einige Felder hinzufügen: Ausrichtung, Titel, Medien, Beschreibung, CTA und Link. Fügen wir also diesen Codeausschnitt zu unserer block.json.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 "attributes": { "alignment": { "type": "string", "default": "none" }, "title": { "type": "string", "selector": "h2" }, "mediaID": { "type": "number" }, "mediaURL": { "type": "string" }, "description": { "type": "string", "selector": ".description" }, "ctaLabel": { "type": "string", "selector": ".button" }, "sectionLink": { "type": "object" } },

Um die Eingabefelder in die Datei „edit.js“ einzufügen, empfehle ich, die aus der Block-Bibliothek (https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src) von Gutenberg als Vorlage zu verwenden, da du sicher sein kannst, dass dort stets die aktuellste API verwendet wird. Gutenberg ist kein neues Projekt, wird aber bis heute ständig aktualisiert und erweitert, weshalb es immer wichtig ist, einen Blick auf die neuesten Funktionen zu werfen.

Wir fahren direkt damit fort, die benötigten Attribute hinzuzufügen, indem wir den Code in edit.js:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 /** * Retrieves the translation of text. * * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-i18n/ */ import { __ } from '@wordpress/i18n'; /** * React hook that is used to mark the block wrapper element. * It provides all the necessary props like the class name. * * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor/#useblockprops */ import { __experimentalLinkControl as LinkControl, AlignmentToolbar, BlockControls, MediaUpload, RichText, useBlockProps, } from '@wordpress/block-editor'; import { Button, ToolbarButton, Popover } from '@wordpress/components'; import { useRef, useState } from '@wordpress/element'; import { link } from '@wordpress/icons'; /** * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files. * Those files can contain any CSS code that gets applied to the editor. * * @see https://www.npmjs.com/package/@wordpress/scripts#using-css */ import './editor.scss'; /** * The edit function describes the structure of your block in the context of the * editor. This represents what the editor will render when the block is used. * * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#edit * * @return {WPElement} Element to render. */ export default function Edit({ attributes, setAttributes, isSelected }) { const { alignment, title, mediaID, mediaURL, description, ctaLabel, sectionLink } = attributes; const isURLSet = !! sectionLink; const blockProps = useBlockProps(); const [ isEditingURL, setIsEditingURL ] = useState( false ); const [ popoverAnchor, setPopoverAnchor ] = useState( null ); const richTextRef = useRef(); const onChangeAlignment = ( newAlignment ) => { setAttributes( { alignment: newAlignment === undefined ? 'none' : newAlignment, } ); }; const onChangeTitle = ( newTitle ) => { setAttributes( { title: newTitle } ); }; const onSelectImage = ( newMedia ) => { setAttributes( { mediaURL: newMedia.url, mediaID: newMedia.id, } ); }; const onChangeDescription = ( value ) => { setAttributes( { description: value } ); }; const onChangeCTALabel = ( newCtaLabel ) => { setAttributes( { ctaLabel: newCtaLabel } ); }; function startEditing( event ) { event.preventDefault(); setIsEditingURL( true ); } const onChangeSectionLink = ( newSectionLink ) => { setAttributes( { sectionLink: newSectionLink } ); }; console.log( 'sectionLink: ', sectionLink ); return ( <div { ...blockProps }> { attributes.title && ! isSelected ? ( <> <p>{ __( 'Project alignment', 'headstartwp' ) + ': ' + alignment }</p> <p>{ __( 'Project title', 'headstartwp' ) + ': ' + title }</p> <p>{ __( 'Project mediaID', 'headstartwp' ) + ': ' + mediaID }</p> <p>{ __( 'Project mediaURL', 'headstartwp' ) + ': ' + mediaURL }</p> <p>{ __( 'Project description', 'headstartwp' ) + ': ' + description }</p> <p>{ __( 'Project CTA label', 'headstartwp' ) + ': ' + ctaLabel }</p> <p>{ __( 'Project section link', 'headstartwp' ) + ': ' + sectionLink?.url || '' }</p> </> ) : ( <> <BlockControls> <AlignmentToolbar value={ alignment } onChange={ onChangeAlignment } /> <ToolbarButton name="link" icon={ link } title={ __( 'Link' ) } onClick={ startEditing } /> </BlockControls> { isSelected && ( isEditingURL || isURLSet ) && ( <Popover placement="bottom" onClose={ () => { setIsEditingURL( false ); richTextRef.current?.focus(); } } anchor={ popoverAnchor } focusOnMount={ isEditingURL ? 'firstElement' : false } __unstableSlotName={ '__unstable-block-tools-after' } shift > <LinkControl className="wp-block-navigation-link__inline-link-input" value={ sectionLink } onChange={ onChangeSectionLink } onRemove={ () => { unlink(); richTextRef.current?.focus(); } } forceIsEditingLink={ isEditingURL } /> </Popover> ) } <div className="title"> <h3>{ __( 'Title', 'headstartwp' ) }</h3> <RichText placeholder={ __( 'Project title', 'headstartwp' ) } value={ title } onChange={ onChangeTitle } /> </div> <div className="image"> <h3>{ __( 'Image', 'headstartwp' ) }</h3> <MediaUpload onSelect={ onSelectImage } allowedTypes="image" value={ mediaID } render={ ( { open } ) => ( <Button className={ mediaID ? 'image-button' : 'button button-large' } onClick={ open } > { ! mediaID ? ( __( 'Upload Image', 'headstartwp' ) ) : ( <img src={ mediaURL } alt={ __( 'Upload Project Image', 'headstartwp' ) } /> ) } </Button> ) } /> </div> <div className="description"> <h3>{ __( 'Description', 'headstartwp' ) }</h3> <RichText placeholder={ __( 'Project description', 'headstartwp' ) } value={ description } onChange={ onChangeDescription } className="description" /> </div> <div className="ctaLabel"> <h3>{ __( 'CTA label', 'headstartwp' ) }</h3> <RichText placeholder={ __( 'Project CTA label', 'headstartwp' ) } value={ ctaLabel } onChange={ onChangeCTALabel } className="ctaLabel" /> </div> </> ) } </div> ); }

Die Datei `save.js` hingegen wird äußerst einfach sein:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 /** * React hook that is used to mark the block wrapper element. * It provides all the necessary props like the class name. * * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor/#useblockprops */ import { useBlockProps } from '@wordpress/block-editor'; /** * The save function defines the way in which the different attributes should * be combined into the final markup, which is then serialized by the block * editor into `post_content`. * * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#save * * @return {WPElement} Element to render. */ export default function save( { attributes } ) { const blockProps = useBlockProps.save( { attributes: attributes }); return ( <div { ...blockProps }></div> ); }

Eines der schönsten Dinge an Gutenberg ist, dass die Blöcke ästhetisch so formatiert sind, wie wir sie in der Produktion haben möchten. Das wird im Moment noch nicht der Fall sein, aber sobald wir den Code für das Frontend geschrieben haben, werden wir ihn auch ins Backend übernehmen, um diesen Mangel auszugleichen. Öffnen wir gleich eine Seite, wählen wir unseren neuen Block aus und füllen wir alle Eingabefelder aus.

Nach dem Speichern sieht das Ergebnis so aus:

Wenn wir die Seite im Frontend betrachten, finden wir unseren Block mit allen Daten, die wir im Backend eingegeben haben, bereit zur Verwendung im `data-wp-block`:

Wenn Sie wissen möchten, wie es weitergeht, und die Arbeit abschließen möchten, lesen Sie diesen anderen Artikel https://blog.riccardodicurti.it/headstartwp-add-a-form-with-contact-form-7, in dem wir eine Komponente für HeadstartWP erstellen, oder warten Sie auf die Veröffentlichung von Teil zwei, in dem wir gemeinsam fortfahren werden.