Skip to content

Add a guide for extending a dynamic block using the HTMLTagProcessor #134

@fabiankaegy

Description

@fabiankaegy

For static blocks we can use the registerBlockExtension to easily add additional classnames. Technically it also works for adding inline styles but we should use that sparingly because it may introduce block validation issues and deprecations.

Another issue is that the block extensions only apply to static blocks. When the rendering happens on the server in PHP it doesn't do anything.

To solve for all these usecases we can use the HTMLTagProcessor and the block render callbacks.

Here is a quick example for adding an animation extension:

JS that deals with the editor:

/**
 * additional block attributes object
 */
const ANIMATION_ATTRIBUTES = {
	animation: {
		type: 'object',
		default: {},
	},
};

/**
 * generateClassNames
 *
 * a function to generate the new className string that should get added to
 * the wrapping element of the block.
 *
 * @param {object} attributes block attributes
 * @returns {string} className string
 */
function generateClassNames(attributes) {
	const { animation } = attributes;

	if (!animation?.name) {
		return '';
	}

	const classNames = [
		'has-block-animation',
		animation.name,
		`duration-${animation?.duration || 300}`,
	];

	if (animation?.delay) {
		classNames.push(`delay-${animation.delay}`);
	}

	if (animation?.easing) {
		classNames.push(`timing-${animation.easing}`);
	}

	const classNameString = classNames.join(' ');

	return classNameString;
}

registerBlockExtension(window.AnimateBlocks.blocks, {
	extensionName: 'tenup/animate-blocks',
	attributes: ANIMATION_ATTRIBUTES,
	classNameGenerator: generateClassNames,
	Edit: BlockEdit,
});

PHP to register the additional attribute for all blocks

/**
 * Register animation attribute to blocks
 */
function register_animation_attribute_for_blocks() {
	$registered_blocks = \WP_Block_Type_Registry::get_instance()->get_all_registered();

	foreach ( $registered_blocks as $name => $block ) {
		$block->attributes['animation'] = array( 'type' => 'object' );
	}
}

add_filter( 'wp_loaded', __NAMESPACE__ . '\\register_animation_attribute_for_blocks', 999 );

PHP to add the actual output to the block on the server:

/**
 * Append custom classes to the block frontend content.
 *
 * @since 1.0.0
 *
 * @param string $block_content   The block frontend output.
 * @param array  $content_classes Custom classes to be added in array form.
 * @return string                 Return the $block_content with the custom classes added.
 */
function append_content_classes( $block_content, $content_classes ) {

	// If there are no content classes, return the original block content.
	if ( empty( $content_classes ) ) {
		return $block_content;
	}

	// Remove duplicate classes and turn into string.
	$classes = array_unique( $content_classes );
	$classes = array_map( 'sanitize_html_class', $classes );

	$walker = new \WP_HTML_Tag_Processor( $block_content );
	$walker->next_tag();

	foreach ( $classes as $class ) {
		$walker->add_class( $class );
	}

	return $walker->get_updated_html();
}

/**
 * Check if the given block has animation settings.
 *
 * @since 1.0.0
 *
 * @param string $block_content The block frontend output.
 * @param array  $block         The block info and attributes.
 * @return mixed                Return either the $block_content or nothing depending on animation settings.
 */
function render_with_animation( $block_content, $block ) {

	$attributes = isset( $block['attrs']['animation'] )
		? $block['attrs']['animation']
		: null;

	// Ensure the block actually has animation settings set. Otherwise, return
	// the block content.
	if ( ! isset( $attributes ) ) {
		return $block_content;
	}

	// the add_custom_classes function here just generates an array of classes to be added based on the attributes 
	$content_classes = add_custom_classes( $attributes );

	if ( ! empty( $content_classes ) ) {
		$block_content = append_content_classes( $block_content, $content_classes );
	}

	return $block_content;

}
add_filter( 'render_block', __NAMESPACE__ . '\\render_with_animation', 10, 3 );

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions