Unterschiedliche Farbpaletten mit Gutenberg erstellen

In diesem Beitrag zeige ich, wie sich unterschiedliche Farbpaletten in Gutenberg einsetzen lassen, sodass beispielsweise für die Textfarbe andere Farben gewählt werden können als für die Hintergrundfarbe.

Update vom 18. November 2018: Mit einem der letzten Gutenberg-Updates wurde PanelColor entfernt. Ich habe den Code mit der stattdessen zu verwendenden PanelColorSettings-Komponenten angepasst. Damit ist die Lösung auch einfacher geworden, da diese Komponente direkt vorsieht, dass die globalen Farben überschrieben werden können.

Update vom 11. Juli 2018: Damit die Markierung der gewählten Farbe nach dem Neuladen des Editors funktioniert, müssen im letzten Code-Block noch die beiden Farb-Attribute als attributes angegeben werden.

Die Zielsetzung

Ziel war, quasi die Farbwähler nachzubauen, die bei dem Absatz-Block vorhanden sind:

Screenshot der beiden Farbwähler-Komponenten für Text- und Hintergrundfarbe in der Sidebar des Gutenberg-Editors bei ausgewähltem Absatz-Block.

Dabei befinden sich die Farben innerhalb eines auf- und zuklappbaren Bereichs und die ausgewählte Farbe wird neben dem Titel dieses Bereichs angezeigt. Wird eine Farbe ausgewählt, wird für das Absatz-Element eine entsprechende Klasse eingefügt und ein Inline-Style gesetzt.

Die Lösung

In der ersten Version meiner Lösung habe ich die PanelColor-Komponente genutzt, der keine eigenen Farben mitgegeben werden konnten, weshalb hier der Aufwand noch etwas größer war als mit PanelColorSettings. Dieser können eigene Farben übergeben werden – es muss aber darauf geachtet werden, dass alle genutzten Farben über add_theme_support() angelegt sind, weil Gutenberg diese Farben zur Erstellung der Farb-Klassennamen nutzt.

Die Lösung besteht damit quasi aus zwei Teilen:

  • Gutenberg die gewünschten Farben über add_theme_support( 'editor-color-palette' ) beibringen.
  • Den Farb-Teil aus dem Absatz-Block nachbauen.

Ich gehe davon aus, dass ihr Gutenberg installiert und eine JavaScript-Datei im Editor eingebunden habt und bereits eine package.json nutzt. Ich nutze hier moderne JavaScript-Syntax, die mit Babel in ES5 konvertiert wird.

Farbpalette definieren

Zunächst definieren wir unsere Farben, die wir später nutzen wollen:

add_action( 'after_setup_theme', 'setup_theme_supported_features' );

function setup_theme_supported_features() {
	add_theme_support( 'editor-color-palette',
		[
			[
				'name' => 'Puerto Rico',
				'slug' => 'puerto-rico',
				'color' => '#53c4ab',
			],
			[
				'name' => 'Tundora',
				'slug' => 'tundora',
				'color' => '#454545',
			],
			[
				'name' => 'Butterfly Bush',
				'slug' => 'butterfly-bush',
				'color' => '#5151a0',
			],
			[
				'name' => 'White',
				'slug' => 'white',
				'color' => '#ffffff',
			],
		]
	);

	add_theme_support( 'disable-custom-colors' );
}

Der JavaScript-Part

Neben Komponenten von Gutenberg brauchen wir noch die classnames-Bibliothek. Um die zu installieren, führen wir folgenden Befehl in der Kommandozeile aus:

npm install classnames

Kommen wir jetzt zunächst zu den ganzen externen Dingen, die wir in unsere JavaScript-Datei importieren müssen.

import classnames from 'classnames';

const {
	registerBlockType,
} = wp.blocks;
const {
	InspectorControls,
	InnerBlocks,
	withColors,
	getColorClass
} = wp.editor;
const {
	PanelBody,
	withFallbackStyles,
	PanelColor,
	ColorPalette,
} = wp.components;

const {
	compose,
	Component,
} = wp.element;

const {getComputedStyle} = window;

const FallbackStyles = withFallbackStyles((node, ownProps) => {
	const {textColor, backgroundColor} = ownProps.attributes;
	const editableNode = node.querySelector('[contenteditable="true"]');
	//verify if editableNode is available, before using getComputedStyle.
	const computedStyles = editableNode ? getComputedStyle(editableNode) : null;
	return {
		fallbackBackgroundColor: backgroundColor || !computedStyles ? undefined : computedStyles.backgroundColor,
		fallbackTextColor: textColor || !computedStyles ? undefined : computedStyles.color,
	};
});

Wie bereits geschrieben, ist die Lösung ja aus bestehenden Gutenberg-Komponenten zusammengesucht – dementsprechend lassen sich diese Definitionen und die withFallbackStyles()-Funktion im Absatz-Block und der PanelColor-Komponente finden.

Wie im Absatz-Block zu sehen, werden wir den Edit-Teil unseres Blocks als Unterklasse von Component anlegen:

class OneColumnBlock extends Component {
	constructor() {
		super(...arguments);
	}

	render() {
		const {
			attributes,
			setAttributes,
			mergeBlocks,
			onReplace,
			className,
			backgroundColor,
			textColor,
			setBackgroundColor,
			setTextColor,
			fallbackBackgroundColor,
			fallbackTextColor,
			fallbackFontSize,
		} = this.props;

		const textColors = [
			{
				name: 'Tundora',
				slug: 'tundora',
				color: '#454545'
			},
		];
		const backgroundColors = [
			{
				name: 'Puerto Rico',
				slug: 'puerto-rico',
				color: '#53c4ab'
			},
			{
				name: 'Butterfly Bush',
				slug: 'butterfly-bush',
				color: '#5151a0'
			},
			{
				name: 'White',
				slug: 'white',
				color: '#ffffff'
			}
		];

		return (
			<div
				className={classnames(className, {
					'has-background': backgroundColor.value,
					[backgroundColor.class]: backgroundColor.class,
					[textColor.class]: textColor.class,
				})}
				
				style={{
					backgroundColor: backgroundColor.value,
					color: textColor.value,
				}}
			>
				<InnerBlocks/>
				<InspectorControls>
					<PanelColorSettings
						title="'Farbschema"
						colorSettings={[
							{
								colors: textColors,
								label: 'Textfarbe',
								onChange: setTextColor,
								value: textColor.color,
								disableCustomColors: true,
							},
							{
								colors: backgroundColors,
								label: 'Hintergrundfarbe',
								onChange: setBackgroundColor,
								value: backgroundColor.color,
								disableCustomColors: true,
							}
						]}
					/>
				</InspectorControls>
			</div>
		);
	}
}

Interessant ist der render-Part. Die Konstanten vom Anfang sind aus dem Absatz-Block übernommen – textColors und backgroundColors sind unsere Untermengen der über add_theme_support( 'editor-color-palette' ) definierten Farben.

Im return-Teil wird zunächst ein div geöffnet, in dem die Klassennamen und die Inline-Styles ausgegeben werden. Auch das ist aus der Absatz-Komponente übernommen. InnerBlocks ist eine Komponente, die es ermöglicht, andere Blöcke in diesem Block zu verwenden, und danach geht es innerhalb von InspectorControls an unsere Farbwähler, die wir über eine PanelColorSettings-Komponente erstellen.

Hier geben wir zunächst mit title den Titel an und übergeben mit colorSettings ein Array von Objekten, wobei jedes Objekt einen Farbwähler erzeugt. Mit colors definieren wir die verfügbaren Farben (Standard wären die, die über add_theme_support( 'editor-color-palette' ) angegeben wurden) – für den ersten Farbwähler mit textColors die Textfarbe und mit backgroundColors für den zweiten die Hintergrundfarben.

Über label bekommt jeder Farbwähler eine Beschriftung und an onChange über geben wir die Funktionen zum Speichern der Farbe (aus dem Absatz-Block entnommen). Als Wert wird der Hexadezimal-Wert genutzt und wir möchten deaktivieren, dass der Nutzer eine eigene Farbe definieren kann.

Ein letztes Stück Code haben wir jetzt noch – ebenfalls wieder an die Absatz-Komponente angelehnt:

export default registerBlockType('slug/one-column', {
	title: 'Eine Spalte',
	icon: 'admin-post',
	category: 'layout',
	attributes: {
		backgroundColor: {
			type: 'string',
		},
		textColor: {
			type: 'string',
		},
	},
	edit: compose([
		withColors('backgroundColor', {textColor: 'color'}),
		FallbackStyles,
	])(OneColumnBlock),
	save: props => {
		const {
			backgroundColor,
			textColor,
			customBackgroundColor,
			customTextColor,
		} = props.attributes;

		const textClass = getColorClass( 'color', textColor );
		const backgroundClass = getColorClass( 'background-color', backgroundColor );
		
		const className = classnames( {
			'has-background': backgroundColor || customBackgroundColor,
			[ textClass ]: textClass,
			[ backgroundClass ]: backgroundClass,
		} );
		return (
			<div className={className}>
				<InnerBlocks.Content/>
			</div>
		);
	},
});

Hier registrieren wir jetzt unseren konkreten Block slug/one-column und geben ein paar Metadaten an. Als edit legen wir mit compose den Teil aus OneColumnBlock fest und in save bilden wir die eventuell notwendigen Farbklassen und weisen sie dem div zu, das den Inhalt von unserem InnerBlocks-Block umschließt.

Und so sieht das Ergebnis aus (ich habe bei dem Projekt ein paar Farben mehr als im Beispiel):

Screenshot of two color pickers with different color palettes.

Den kompletten Code aus dem Beispiel könnt ihr als Gist anschauen.

Das könnte auch interessant sein

Schreib einen Kommentar

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