class-activation.php
2 weeks ago
class-admin-menu-organizer.php
2 weeks ago
class-admin-menu-svg-icon-mask.php
2 weeks ago
class-auto-publish-posts-with-missed-schedule.php
2 weeks ago
class-avif-upload.php
2 weeks ago
class-captcha-protection.php
2 weeks ago
class-change-login-url.php
2 weeks ago
class-cleanup-admin-bar.php
2 weeks ago
class-common-methods.php
2 weeks ago
class-content-duplication.php
2 weeks ago
class-content-order.php
2 weeks ago
class-custom-admin-footer-text.php
2 weeks ago
class-custom-body-class.php
2 weeks ago
class-custom-css.php
2 weeks ago
class-custom-nav-menu-items-in-new-tab.php
2 weeks ago
class-deactivation.php
2 weeks ago
class-disable-author-archives.php
2 weeks ago
class-disable-comments.php
2 weeks ago
class-disable-dashboard-widgets.php
2 weeks ago
class-disable-embeds.php
2 weeks ago
class-disable-feeds.php
2 weeks ago
class-disable-gutenberg.php
2 weeks ago
class-disable-rest-api.php
2 weeks ago
class-disable-smaller-components.php
2 weeks ago
class-disable-updates.php
2 weeks ago
class-disable-user-account.php
2 weeks ago
class-disable-xml-rpc.php
2 weeks ago
class-display-system-summary.php
2 weeks ago
class-email-address-obfuscator.php
2 weeks ago
class-email-delivery.php
2 weeks ago
class-enhance-list-tables.php
2 weeks ago
class-external-permalinks.php
2 weeks ago
class-heartbeat-control.php
2 weeks ago
class-hide-admin-bar.php
2 weeks ago
class-hide-admin-notices.php
2 weeks ago
class-image-sizes-panel.php
2 weeks ago
class-image-upload-control.php
2 weeks ago
class-insert-head-body-footer-code.php
2 weeks ago
class-last-login-column.php
2 weeks ago
class-limit-login-attempts.php
2 weeks ago
class-login-id-type.php
2 weeks ago
class-login-logout-menu.php
2 weeks ago
class-maintenance-mode.php
2 weeks ago
class-manage-ads-appads-txt.php
2 weeks ago
class-manage-robots-txt.php
2 weeks ago
class-media-files-visibility-control.php
2 weeks ago
class-media-replacement.php
2 weeks ago
class-multiple-user-roles.php
2 weeks ago
class-navigation-menu-duplicator.php
2 weeks ago
class-obfuscate-author-slugs.php
2 weeks ago
class-open-external-links-in-new-tab.php
2 weeks ago
class-password-protection.php
2 weeks ago
class-redirect-after-login.php
2 weeks ago
class-redirect-after-logout.php
2 weeks ago
class-redirect-fourofour.php
2 weeks ago
class-registration-date-column.php
2 weeks ago
class-revisions-control.php
2 weeks ago
class-search-engines-visibility.php
2 weeks ago
class-settings-fields-render.php
2 weeks ago
class-settings-sanitization.php
2 weeks ago
class-settings-sections-fields.php
2 weeks ago
class-show-custom-taxonomy-filters.php
2 weeks ago
class-site-identity-on-login-page.php
2 weeks ago
class-svg-upload.php
2 weeks ago
class-third-party-compat.php
2 weeks ago
class-various-admin-ui-enhancements.php
2 weeks ago
class-view-admin-as-role.php
2 weeks ago
class-wider-admin-menu.php
2 weeks ago
class-wp-config-transformer.php
2 weeks ago
class-admin-menu-svg-icon-mask.php
236 lines
| 1 | <?php |
| 2 | /** |
| 3 | * Shared helper for rendering SVG admin menu icons with correct colors on first paint. |
| 4 | * |
| 5 | * WordPress renders `menu_icon` SVG data URIs as <img> initially, and then core's |
| 6 | * `svg-painter` inlines/recolors them, causing a visible "black → scheme color" flash. |
| 7 | * This helper pre-empts that by forcing WordPress to use the `.wp-menu-image:before` |
| 8 | * pseudo-element (via a Dashicon placeholder) and then applying the SVG as a CSS mask. |
| 9 | * |
| 10 | * @since 7.6.0 |
| 11 | */ |
| 12 | |
| 13 | namespace ASENHA\Classes; |
| 14 | |
| 15 | class Admin_Menu_Svg_Icon_Mask { |
| 16 | |
| 17 | /** |
| 18 | * Registered SVG data URIs keyed by post type key. |
| 19 | * |
| 20 | * @since 7.6.0 |
| 21 | * @var array<string,string> |
| 22 | */ |
| 23 | private static $post_type_svgs = array(); |
| 24 | |
| 25 | /** |
| 26 | * Registered SVG data URIs keyed by top-level menu slug (from add_menu_page()). |
| 27 | * |
| 28 | * @since 7.6.0 |
| 29 | * @var array<string,string> |
| 30 | */ |
| 31 | private static $toplevel_menu_slug_svgs = array(); |
| 32 | |
| 33 | /** |
| 34 | * Registered SVG data URIs keyed by a substring to match against `href`. |
| 35 | * |
| 36 | * This is useful for menu slugs that are full URLs/queries (e.g. `post.php?post=123&action=edit`). |
| 37 | * |
| 38 | * @since 7.6.0 |
| 39 | * @var array<string,string> |
| 40 | */ |
| 41 | private static $href_contains_svgs = array(); |
| 42 | |
| 43 | /** |
| 44 | * Check whether a string is an SVG data URI that WordPress would render as an <img>. |
| 45 | * |
| 46 | * @since 7.6.0 |
| 47 | * |
| 48 | * @param mixed $value Potential menu icon. |
| 49 | * @return bool |
| 50 | */ |
| 51 | public static function is_svg_data_uri( $value ) { |
| 52 | return is_string( $value ) && ( 0 === strpos( $value, 'data:image/svg+xml' ) ); |
| 53 | } |
| 54 | |
| 55 | /** |
| 56 | * Register an SVG icon for a post type's admin menu entry. |
| 57 | * |
| 58 | * @since 7.6.0 |
| 59 | * |
| 60 | * @param string $post_type Post type key. |
| 61 | * @param string $svg_data_uri SVG data URI. |
| 62 | * @return void |
| 63 | */ |
| 64 | public static function register_post_type_svg_icon( $post_type, $svg_data_uri ) { |
| 65 | $post_type = sanitize_key( (string) $post_type ); |
| 66 | $svg_data_uri = trim( (string) $svg_data_uri ); |
| 67 | |
| 68 | if ( '' === $post_type || '' === $svg_data_uri ) { |
| 69 | return; |
| 70 | } |
| 71 | |
| 72 | if ( ! self::is_svg_data_uri( $svg_data_uri ) ) { |
| 73 | return; |
| 74 | } |
| 75 | |
| 76 | self::$post_type_svgs[ $post_type ] = $svg_data_uri; |
| 77 | } |
| 78 | |
| 79 | /** |
| 80 | * Register an SVG icon for a top-level menu entry created by add_menu_page(). |
| 81 | * |
| 82 | * @since 7.6.0 |
| 83 | * |
| 84 | * @param string $menu_slug Menu slug passed to add_menu_page(). |
| 85 | * @param string $svg_data_uri SVG data URI. |
| 86 | * @return void |
| 87 | */ |
| 88 | public static function register_toplevel_menu_slug_svg_icon( $menu_slug, $svg_data_uri ) { |
| 89 | $menu_slug = (string) $menu_slug; |
| 90 | $svg_data_uri = trim( (string) $svg_data_uri ); |
| 91 | |
| 92 | if ( '' === $menu_slug || '' === $svg_data_uri ) { |
| 93 | return; |
| 94 | } |
| 95 | |
| 96 | if ( ! self::is_svg_data_uri( $svg_data_uri ) ) { |
| 97 | return; |
| 98 | } |
| 99 | |
| 100 | self::$toplevel_menu_slug_svgs[ $menu_slug ] = $svg_data_uri; |
| 101 | } |
| 102 | |
| 103 | /** |
| 104 | * Register an SVG icon for a menu entry by matching a substring within the anchor href. |
| 105 | * |
| 106 | * @since 7.6.0 |
| 107 | * |
| 108 | * @param string $needle Substring expected in href. |
| 109 | * @param string $svg_data_uri SVG data URI. |
| 110 | * @return void |
| 111 | */ |
| 112 | public static function register_menu_href_contains_svg_icon( $needle, $svg_data_uri ) { |
| 113 | $needle = trim( (string) $needle ); |
| 114 | $svg_data_uri = trim( (string) $svg_data_uri ); |
| 115 | |
| 116 | if ( '' === $needle || '' === $svg_data_uri ) { |
| 117 | return; |
| 118 | } |
| 119 | |
| 120 | if ( ! self::is_svg_data_uri( $svg_data_uri ) ) { |
| 121 | return; |
| 122 | } |
| 123 | |
| 124 | self::$href_contains_svgs[ $needle ] = $svg_data_uri; |
| 125 | } |
| 126 | |
| 127 | /** |
| 128 | * Enqueue inline CSS masks for all registered menu SVG icons. |
| 129 | * |
| 130 | * @since 7.6.0 |
| 131 | * |
| 132 | * @return void |
| 133 | */ |
| 134 | public static function enqueue_mask_css() { |
| 135 | if ( empty( self::$post_type_svgs ) && empty( self::$toplevel_menu_slug_svgs ) && empty( self::$href_contains_svgs ) ) { |
| 136 | return; |
| 137 | } |
| 138 | |
| 139 | $css = ''; |
| 140 | |
| 141 | foreach ( self::$post_type_svgs as $post_type => $svg_data_uri ) { |
| 142 | $post_type = sanitize_key( (string) $post_type ); |
| 143 | $svg_data_uri = trim( (string) $svg_data_uri ); |
| 144 | |
| 145 | if ( '' === $post_type || '' === $svg_data_uri || ! self::is_svg_data_uri( $svg_data_uri ) ) { |
| 146 | continue; |
| 147 | } |
| 148 | |
| 149 | $selector = '#adminmenu .menu-icon-' . $post_type . ' .wp-menu-image:before'; |
| 150 | $css .= self::build_mask_rule( $selector, $svg_data_uri ); |
| 151 | } |
| 152 | |
| 153 | foreach ( self::$toplevel_menu_slug_svgs as $menu_slug => $svg_data_uri ) { |
| 154 | $menu_slug = (string) $menu_slug; |
| 155 | $svg_data_uri = trim( (string) $svg_data_uri ); |
| 156 | |
| 157 | if ( '' === $menu_slug || '' === $svg_data_uri || ! self::is_svg_data_uri( $svg_data_uri ) ) { |
| 158 | continue; |
| 159 | } |
| 160 | |
| 161 | $selectors = array( |
| 162 | '#toplevel_page_' . sanitize_title( $menu_slug ) . ' .wp-menu-image:before', |
| 163 | '#adminmenu a[href*="' . self::escape_css_attr_value( $menu_slug ) . '"] .wp-menu-image:before', |
| 164 | ); |
| 165 | $css .= self::build_mask_rule( implode( ',' . PHP_EOL, $selectors ), $svg_data_uri ); |
| 166 | } |
| 167 | |
| 168 | foreach ( self::$href_contains_svgs as $needle => $svg_data_uri ) { |
| 169 | $needle = trim( (string) $needle ); |
| 170 | $svg_data_uri = trim( (string) $svg_data_uri ); |
| 171 | |
| 172 | if ( '' === $needle || '' === $svg_data_uri || ! self::is_svg_data_uri( $svg_data_uri ) ) { |
| 173 | continue; |
| 174 | } |
| 175 | |
| 176 | $selector = '#adminmenu a[href*="' . self::escape_css_attr_value( $needle ) . '"] .wp-menu-image:before'; |
| 177 | $css .= self::build_mask_rule( $selector, $svg_data_uri ); |
| 178 | } |
| 179 | |
| 180 | if ( '' === $css ) { |
| 181 | return; |
| 182 | } |
| 183 | |
| 184 | wp_add_inline_style( 'asenha-wp-admin', $css ); |
| 185 | } |
| 186 | |
| 187 | /** |
| 188 | * Build a CSS rule that masks a WP admin menu icon pseudo-element. |
| 189 | * |
| 190 | * @since 7.6.0 |
| 191 | * |
| 192 | * @param string $selector CSS selector(s). |
| 193 | * @param string $svg_data_uri SVG data URI. |
| 194 | * @return string |
| 195 | */ |
| 196 | private static function build_mask_rule( $selector, $svg_data_uri ) { |
| 197 | $selector = trim( (string) $selector ); |
| 198 | $svg_data_uri = trim( (string) $svg_data_uri ); |
| 199 | |
| 200 | if ( '' === $selector || '' === $svg_data_uri ) { |
| 201 | return ''; |
| 202 | } |
| 203 | |
| 204 | return $selector . '{' . PHP_EOL . |
| 205 | "\tcontent:\"\";" . PHP_EOL . |
| 206 | "\tbackground-color:currentColor;" . PHP_EOL . |
| 207 | "\t-webkit-mask-image:url(\"" . $svg_data_uri . "\");" . PHP_EOL . |
| 208 | "\tmask-image:url(\"" . $svg_data_uri . "\");" . PHP_EOL . |
| 209 | "\t-webkit-mask-repeat:no-repeat;" . PHP_EOL . |
| 210 | "\tmask-repeat:no-repeat;" . PHP_EOL . |
| 211 | "\t-webkit-mask-position:center;" . PHP_EOL . |
| 212 | "\tmask-position:center;" . PHP_EOL . |
| 213 | "\t-webkit-mask-size:20px 20px;" . PHP_EOL . |
| 214 | "\tmask-size:20px 20px;" . PHP_EOL . |
| 215 | '}' . PHP_EOL; |
| 216 | } |
| 217 | |
| 218 | /** |
| 219 | * Escape a value used inside a double-quoted CSS attribute selector. |
| 220 | * |
| 221 | * @since 7.6.0 |
| 222 | * |
| 223 | * @param string $value Raw value. |
| 224 | * @return string |
| 225 | */ |
| 226 | private static function escape_css_attr_value( $value ) { |
| 227 | return str_replace( |
| 228 | array( '\\', '"' ), |
| 229 | array( '\\\\', '\"' ), |
| 230 | (string) $value |
| 231 | ); |
| 232 | } |
| 233 | } |
| 234 | |
| 235 | |
| 236 |