I'm trying to add a custom color control to the group and cover blocks, and if a color is selected, construct a custom className for the block. This custom class is used for a section separator.
What I've done:
- I have filtered
blocks.registerBlockType
and added the custom attribute. - I've made the function to filter
editor.BlockEdit
properly and add aPanelColorSettings
control to the group and cover blocks only. - I have filtered
blocks.getSaveContent.extraProps
to add the custom class on save. - I have filtered
editor.BlockListBlock
to add this same custom class into the block wrapped while editing, so I can see the changes on the fly.
I've been able to get the control to appear, and the className to save to the block when saving the post properly. On the front end, it works. And the first time you edit, it works.
What's missing:
The problem I'm now having is that these custom classNames don't get replaced when you select a different color in the color panel and publish changes. If you select 1 color, hit publish, refresh, and select the 2nd color, publish, refresh, the block will have the custom classes for both colors.
When you first select a color on a block with no previous color selected, you can see the changes on the fly in the editor. When you hit publish and refresh, and then cycle through the colors, you won't see changes on the fly, because the className for your first color is still present on the block.
What I think the problem is:
I need these filters to remove the custom classes they've previously applied? Or an abstraction between "classes typed in the advanced box by a user" vs "classes added to the block by my filter". Nothing in the docs seems to satisfy that.
My code (updated):
/**
* External Dependencies
*/
import classnames from 'classnames';
/**
* WordPress Dependencies
*/
const { __ } = wp.i18n;
const { addFilter } = wp.hooks;
const { Fragment } = wp.element;
const { createHigherOrderComponent } = wp.compose;
const { InspectorControls, getColorObjectByColorValue, PanelColorSettings } = wp.blockEditor;
const { select } = wp.data;
// Restrict to specific block names
const allowedBlocks = ['core/group', 'core/cover'];
/**
* Add custom attribute for section separator.
*
* @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, {
dividerColor: {
type: 'string',
default: null,
}
});
}
return settings;
}
/**
* Add section divider controls on Advanced Block Panel.
*
* @param {function} BlockEdit Block edit component.
*
* @return {function} BlockEdit Modified block edit component.
*/
const withAdvancedControls = createHigherOrderComponent((BlockEdit) => {
return (props) => {
if (!allowedBlocks.includes(props.name)) {
return(<BlockEdit {...props} />);
}
const {
name,
attributes,
setAttributes,
isSelected,
} = props;
const {
dividerColor
} = attributes;
const changeColor = (value) => {
setAttributes({ dividerColor: value });
console.log(props);
}
return (
<Fragment>
<BlockEdit {...props} />
{isSelected && allowedBlocks.includes(name) &&
<InspectorControls>
<PanelColorSettings
title={__('Section Divider')}
colorSettings={[
{
value: dividerColor,
onChange: changeColor,
label: __('Choose a color to enable the divider.')
}
]}
/>
</InspectorControls>
}
</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 { dividerColor } = attributes;
//check if attribute exists for old Gutenberg version compatibility
//add class only when dividerColor is not empty
//add allowedBlocks restriction
if (typeof dividerColor !== 'undefined'
&& dividerColor
&& allowedBlocks.includes(blockType.name)) {
const settings = select('core/editor').getEditorSettings();
const colorObject = getColorObjectByColorValue(settings.colors, dividerColor);
if (colorObject) {
extraProps.className = classnames(extraProps.className, 'divider divider-' + colorObject.slug);
}
}
return extraProps;
}
/**
* Add size class to the block in the editor
*
* @param {Object} BlockListBlock BlockListBlock object.
*
* @return {Object} Modified BlockListBlock object.
*/
const addDividerClass = createHigherOrderComponent((BlockListBlock) => {
return (props) => {
const {
attributes,
className,
name,
} = props;
const { dividerColor } = attributes;
if (!allowedBlocks.includes(name)) {
return <BlockListBlock {...props} />;
}
const settings = select('core/editor').getEditorSettings();
const colorObject = getColorObjectByColorValue(settings.colors, dividerColor);
if (!colorObject) {
return <BlockListBlock {...props} />;
}
return (
<BlockListBlock
{...props}
className={classnames(className, 'divider divider-' + colorObject.slug)}
/>
);
};
}, 'addDividerClass');
//add filters
addFilter(
'blocks.registerBlockType',
'ursa6/custom-attributes',
addAttributes
);
addFilter(
'editor.BlockEdit',
'ursa6/custom-advanced-control',
withAdvancedControls
);
addFilter(
'blocks.getSaveContent.extraProps',
'ursa6/applyExtraClass',
applyExtraClass
);
addFilter(
'editor.BlockListBlock',
'ursa6/addDividerClass',
addDividerClass
);
In my theme's functions, I have a custom color palette set for the above getColorObjectByColorValue
to find a match.
add_theme_support( 'editor-color-palette', array(
array(
'name' => __( 'Dark 1', 'textdomain' ),
'slug' => 'dk1',
'color' => '#282828',
),
array(
'name' => __( 'Light 1', 'textdomain' ),
'slug' => 'lt1',
'color' => '#FCFCF8',
),
array(
'name' => __( 'Mid', 'textdomain' ),
'slug' => 'mid',
'color' => '#F7F7F7',
),
array(
'name' => __( 'Accent 1', 'textdomain' ),
'slug' => 'accent1',
'color' => '#E6A225',
),
array(
'name' => __( 'Accent 2', 'textdomain' ),
'slug' => 'accent2',
'color' => '#048081',
),
array(
'name' => __( 'Accent 3', 'textdomain' ),
'slug' => 'accent3',
'color' => '#882437',
),
)
);
Can someone tell me where I went wrong?
'editor.BlockListBlock'
filter as well. The block wrapper now gets the proper class in the editor, the class saves to the block on the front end. It seems the last issue is the duplication of the custom class. When the block is saved, the custom class is added to the class field in the advanced tab, and when the page is reloaded and the block color changed, it appends the new color class to the contents of the class field in the advanced tab.