From 4ec21e40e52ba4013ba24b5f04cc1698aca25b94 Mon Sep 17 00:00:00 2001 From: Milan Ricoul Date: Tue, 20 Oct 2026 10:42:59 +0200 Subject: [PATCH] feat(Icon): register default bff icons collection for core/icon block --- config/svgo.config.js | 1 + inc/Framework.php | 2 + inc/Services/Editor.php | 1 - inc/Services/Icon.php | 222 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 225 insertions(+), 1 deletion(-) create mode 100644 inc/Services/Icon.php diff --git a/config/svgo.config.js b/config/svgo.config.js index 6b5802f3..42288fcb 100644 --- a/config/svgo.config.js +++ b/config/svgo.config.js @@ -7,6 +7,7 @@ module.exports = { name: 'removeViewBox', active: false, }, + // Plugins that are not in the "preset-default" and that you want to activate 'removeTitle', 'convertStyleToAttrs', ], diff --git a/inc/Framework.php b/inc/Framework.php index ce7a9044..4daa4182 100644 --- a/inc/Framework.php +++ b/inc/Framework.php @@ -9,6 +9,7 @@ use BEA\Theme\Framework\Services\Editor; use BEA\Theme\Framework\Services\Editor_Patterns; use BEA\Theme\Framework\Services\Gravity_Forms; +use BEA\Theme\Framework\Services\Icon; use BEA\Theme\Framework\Services\Menu; use BEA\Theme\Framework\Services\Svg; use BEA\Theme\Framework\Services\Theme; @@ -38,6 +39,7 @@ class Framework { Editor_Patterns::class, Gravity_Forms::class, Svg::class, + Icon::class, Acf::class, Menu::class, Breadcrumb::class, diff --git a/inc/Services/Editor.php b/inc/Services/Editor.php index 6f90d306..a7b9cf5e 100644 --- a/inc/Services/Editor.php +++ b/inc/Services/Editor.php @@ -244,7 +244,6 @@ public function gutenberg_blocks_allowed( $allowed_blocks, \WP_Block_Editor_Cont 'core/accordion-heading', 'core/accordion-item', 'core/accordion-panel', - 'core/icon', // Theme & site (unused). 'core/archives', 'core/calendar', diff --git a/inc/Services/Icon.php b/inc/Services/Icon.php new file mode 100644 index 00000000..63b719e5 --- /dev/null +++ b/inc/Services/Icon.php @@ -0,0 +1,222 @@ + [ + 'source_subdir' => 'sprite', + 'label' => __( 'General', 'beapi-frontend-framework' ), + 'description' => __( 'Theme UI icons.', 'beapi-frontend-framework' ), + ], + self::COLLECTION_SOCIAL => [ + 'source_subdir' => 'social', + 'label' => __( 'Social', 'beapi-frontend-framework' ), + 'description' => __( 'Social network icons.', 'beapi-frontend-framework' ), + ], + ] + ); + + foreach ( $collections as $collection_slug => $collection_args ) { + $this->register_collection( (string) $collection_slug, $collection_args ); + } + } + + /** + * Unregisters the default WordPress core icon collection from the Icon block library. + * + * Runs on init at priority 20 so core icons are registered first (priority 10). + * + * @return void + */ + public function unregister_core_icon_collection(): void { + if ( ! function_exists( 'wp_unregister_icon_collection' ) ) { + return; + } + + if ( ! apply_filters( 'bff_unregister_core_icon_collection', true ) ) { + return; + } + + wp_unregister_icon_collection( 'core' ); + } + + /** + * @param string $collection_slug Collection slug passed to wp_register_icon_collection(). + * @param array $collection_args Collection configuration. + * + * @return void + */ + private function register_collection( string $collection_slug, array $collection_args ): void { + if ( ! function_exists( 'wp_register_icon_collection' ) ) { + return; + } + + $source_subdir = isset( $collection_args['source_subdir'] ) ? (string) $collection_args['source_subdir'] : ''; + + if ( '' === $source_subdir ) { + return; + } + + wp_register_icon_collection( + $collection_slug, + [ + 'label' => $collection_args['label'] ?? $collection_slug, + 'description' => $collection_args['description'] ?? '', + ] + ); + + $icons_directory = $this->resolve_icons_directory( $source_subdir ); + + if ( null === $icons_directory ) { + return; + } + + $svg_files = glob( $icons_directory . '/*.svg' ); + + if ( false === $svg_files ) { + return; + } + + foreach ( $svg_files as $svg_file ) { + $this->register_icon_file( $collection_slug, $svg_file ); + } + } + + /** + * Resolves the directory containing standalone SVG files for a collection. + * + * @param string $source_subdir Subdirectory name (e.g. sprite, social). + * + * @return string|null Absolute path or null when not found. + */ + private function resolve_icons_directory( string $source_subdir ): ?string { + $source_subdir = preg_replace( '/[^a-z0-9-]/', '', strtolower( $source_subdir ) ); + + if ( '' === $source_subdir ) { + return null; + } + + $candidates = [ + get_theme_file_path( 'dist/images/' . $source_subdir ), + get_theme_file_path( 'src/img/icons/' . $source_subdir ), + ]; + + foreach ( $candidates as $candidate ) { + if ( ! is_dir( $candidate ) ) { + continue; + } + + $real_path = realpath( $candidate ); + + if ( false !== $real_path ) { + return $real_path; + } + } + + return null; + } + + /** + * @param string $collection_slug Collection slug. + * @param string $svg_file Absolute path to an SVG file. + * + * @return void + */ + private function register_icon_file( string $collection_slug, string $svg_file ): void { + if ( ! function_exists( 'wp_register_icon' ) ) { + return; + } + + $real_file = realpath( $svg_file ); + + if ( false === $real_file || ! is_readable( $real_file ) ) { + return; + } + + $icon_slug = pathinfo( $real_file, PATHINFO_FILENAME ); + $icon_slug = preg_replace( '/[^a-z0-9-]/', '', strtolower( (string) $icon_slug ) ); + + if ( '' === $icon_slug ) { + return; + } + + $icon_name = $collection_slug . '/' . $icon_slug; + $label = apply_filters( + 'bff_icon_label', + $this->format_icon_label( $icon_slug ), + $icon_slug, + $collection_slug + ); + + $properties = apply_filters( + 'bff_icon_properties', + [ + 'label' => $label, + 'file_path' => $real_file, + ], + $icon_name, + $collection_slug + ); + + if ( empty( $properties['file_path'] ) && empty( $properties['content'] ) ) { + return; + } + + wp_register_icon( $icon_name, $properties ); + } + + /** + * @param string $icon_slug Icon filename without extension. + * + * @return string + */ + private function format_icon_label( string $icon_slug ): string { + $label = str_replace( [ '-', '_' ], ' ', $icon_slug ); + + return ucwords( $label ); + } +}