'primary', 'secondary' => 'secondary', 'success' => 'success', 'danger' => 'danger', 'warning' => 'warning', 'info' => 'info', 'light' => 'light', 'dark' => 'dark', ]; /** * Register render_block filters for each supported block type. */ public function __construct() { if ( is_admin() || wp_doing_ajax() ) { return; } if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) { return; } $blocks = $this->getBlockHandlers(); /** * Filters the map of block names to handler method names. * * Child themes can remove blocks or add new ones. * * @since 1.1.0 * * @param array $blocks Map of 'core/block-name' => 'methodName'. */ $blocks = apply_filters( 'wp_bootstrap_block_renderer_blocks', $blocks ); foreach ( $blocks as $blockName => $method ) { if ( method_exists( $this, $method ) ) { add_filter( "render_block_{$blockName}", [ $this, $method ], 10, 3 ); } } } /** * Get the default map of block names to handler methods. * * @return array */ private function getBlockHandlers(): array { return [ 'core/table' => 'renderTable', 'core/button' => 'renderButton', 'core/buttons' => 'renderButtons', 'core/image' => 'renderImage', 'core/search' => 'renderSearch', 'core/quote' => 'renderQuote', 'core/pullquote' => 'renderPullquote', 'core/list' => 'renderList', ]; } /** * Add Bootstrap table classes. * * Injects .table on ; adds .table-striped when WP stripes style is active. */ public function renderTable( string $content, array $block, ?WP_Block $instance = null ): string { if ( empty( $content ) ) { return $content; } $processor = new WP_HTML_Tag_Processor( $content ); if ( ! $processor->next_tag( 'table' ) ) { return $content; } $processor->add_class( 'table' ); $className = $block['attrs']['className'] ?? ''; if ( str_contains( $className, 'is-style-stripes' ) ) { $processor->add_class( 'table-striped' ); } return $processor->get_updated_html(); } /** * Add Bootstrap button classes. * * Injects .btn + color variant on .wp-block-button__link. * Maps WP preset color slugs to Bootstrap btn-{variant} / btn-outline-{variant}. */ public function renderButton( string $content, array $block, ?WP_Block $instance = null ): string { if ( empty( $content ) ) { return $content; } $processor = new WP_HTML_Tag_Processor( $content ); if ( ! $processor->next_tag( [ 'class_name' => 'wp-block-button__link' ] ) ) { return $content; } $processor->add_class( 'btn' ); $attrs = $block['attrs'] ?? []; // Gradient buttons: just .btn, inline style handles the color. if ( ! empty( $attrs['gradient'] ) ) { return $processor->get_updated_html(); } $className = $attrs['className'] ?? ''; $isOutline = str_contains( $className, 'is-style-outline' ); if ( $isOutline ) { $colorSlug = $attrs['textColor'] ?? 'primary'; $variant = self::COLOR_VARIANTS[ $colorSlug ] ?? 'primary'; $processor->add_class( 'btn-outline-' . $variant ); } else { $colorSlug = $attrs['backgroundColor'] ?? 'primary'; $variant = self::COLOR_VARIANTS[ $colorSlug ] ?? 'primary'; $processor->add_class( 'btn-' . $variant ); } return $processor->get_updated_html(); } /** * Add Bootstrap flex utilities to button group wrapper. */ public function renderButtons( string $content, array $block, ?WP_Block $instance = null ): string { if ( empty( $content ) ) { return $content; } $processor = new WP_HTML_Tag_Processor( $content ); if ( ! $processor->next_tag( [ 'class_name' => 'wp-block-buttons' ] ) ) { return $content; } $processor->add_class( 'd-flex' ); $processor->add_class( 'flex-wrap' ); $processor->add_class( 'gap-2' ); return $processor->get_updated_html(); } /** * Add .img-fluid to block images. */ public function renderImage( string $content, array $block, ?WP_Block $instance = null ): string { if ( empty( $content ) ) { return $content; } $processor = new WP_HTML_Tag_Processor( $content ); if ( ! $processor->next_tag( 'img' ) ) { return $content; } $processor->add_class( 'img-fluid' ); return $processor->get_updated_html(); } /** * Add Bootstrap form-control and button classes to search block. */ public function renderSearch( string $content, array $block, ?WP_Block $instance = null ): string { if ( empty( $content ) ) { return $content; } $processor = new WP_HTML_Tag_Processor( $content ); // Add .input-group to the inner wrapper for seamless input + button. if ( $processor->next_tag( [ 'class_name' => 'wp-block-search__inside-wrapper' ] ) ) { $processor->add_class( 'input-group' ); } if ( $processor->next_tag( [ 'class_name' => 'wp-block-search__input' ] ) ) { $processor->add_class( 'form-control' ); } if ( $processor->next_tag( [ 'class_name' => 'wp-block-search__button' ] ) ) { $processor->add_class( 'btn' ); $processor->add_class( 'btn-primary' ); } return $processor->get_updated_html(); } /** * Add Bootstrap blockquote classes to quote block. */ public function renderQuote( string $content, array $block, ?WP_Block $instance = null ): string { if ( empty( $content ) ) { return $content; } $processor = new WP_HTML_Tag_Processor( $content ); if ( ! $processor->next_tag( 'blockquote' ) ) { return $content; } $processor->add_class( 'blockquote' ); if ( $processor->next_tag( 'cite' ) ) { $processor->add_class( 'blockquote-footer' ); } return $processor->get_updated_html(); } /** * Add Bootstrap blockquote classes to pullquote block. */ public function renderPullquote( string $content, array $block, ?WP_Block $instance = null ): string { if ( empty( $content ) ) { return $content; } $processor = new WP_HTML_Tag_Processor( $content ); if ( ! $processor->next_tag( 'blockquote' ) ) { return $content; } $processor->add_class( 'blockquote' ); if ( $processor->next_tag( 'cite' ) ) { $processor->add_class( 'blockquote-footer' ); } return $processor->get_updated_html(); } /** * Add Bootstrap list-group classes when list-group style is selected. * * Only modifies lists with the is-style-list-group block style. */ public function renderList( string $content, array $block, ?WP_Block $instance = null ): string { if ( empty( $content ) ) { return $content; } $className = $block['attrs']['className'] ?? ''; if ( ! str_contains( $className, 'is-style-list-group' ) ) { return $content; } $processor = new WP_HTML_Tag_Processor( $content ); $listTag = ! empty( $block['attrs']['ordered'] ) ? 'ol' : 'ul'; if ( $processor->next_tag( $listTag ) ) { $processor->add_class( 'list-group' ); } while ( $processor->next_tag( 'li' ) ) { $processor->add_class( 'list-group-item' ); } return $processor->get_updated_html(); } }