| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Server-side rendering of the `core/navigation-submenu` block. 4 * 5 * @package WordPress 6 */ 7 8 require_once __DIR__ . '/navigation-link/shared/item-should-render.php'; 9 require_once __DIR__ . '/navigation-link/shared/render-submenu-icon.php'; 10 11 /** 12 * Renders the submenu icon SVG for the Navigation Submenu block. 13 * 14 * @since 5.9.0 15 * @deprecated 7.0.0 Use block_core_shared_navigation_render_submenu_icon() instead. 16 * 17 * @return string SVG markup for the submenu icon. 18 */ 19 function block_core_navigation_submenu_render_submenu_icon() { 20 _deprecated_function( __FUNCTION__, '7.0.0', 'block_core_shared_navigation_render_submenu_icon()' ); 21 return block_core_shared_navigation_render_submenu_icon(); 22 } 23 24 /** 25 * Returns the submenu visibility value with backward compatibility 26 * for the deprecated openSubmenusOnClick attribute. 27 * 28 * This function centralizes the migration logic from the boolean 29 * openSubmenusOnClick to the new submenuVisibility enum. 30 * 31 * Backward compatibility handling: 32 * - Legacy blocks (saved before migration, never opened in editor): 33 * Have openSubmenusOnClick in database. Parent Navigation block passes it via context. 34 * We prioritize openSubmenusOnClick to preserve the original behavior. 35 * 36 * - Migrated blocks (opened in editor after migration): 37 * JavaScript deprecation removes openSubmenusOnClick and sets submenuVisibility. 38 * We use submenuVisibility since openSubmenusOnClick is null. 39 * 40 * - New blocks (created after migration): 41 * Only have submenuVisibility, openSubmenusOnClick is null. 42 * We use submenuVisibility. 43 * 44 * @since 6.9.0 45 * 46 * @param array $context Block context from parent Navigation block. 47 * @return string The visibility mode: 'hover', 'click', or 'always'. 48 */ 49 function block_core_navigation_submenu_get_submenu_visibility( $context ) { 50 $deprecated_open_submenus_on_click = $context['openSubmenusOnClick'] ?? null; 51 52 // For backward compatibility, prioritize the legacy attribute if present. If it has been loaded and saved in the editor, then 53 // the deprecated attribute will be replaced by submenuVisibility. 54 if ( null !== $deprecated_open_submenus_on_click ) { 55 // Convert boolean to string: true -> 'click', false -> 'hover'. 56 return ! empty( $deprecated_open_submenus_on_click ) ? 'click' : 'hover'; 57 } 58 59 $submenu_visibility = $context['submenuVisibility'] ?? null; 60 61 // Use submenuVisibility for migrated/new blocks. 62 return $submenu_visibility ?? 'hover'; 63 } 64 65 /** 66 * Renders the `core/navigation-submenu` block. 67 * 68 * @since 5.9.0 69 * 70 * @param array $attributes The block attributes. 71 * @param string $content The saved content. 72 * @param WP_Block $block The parsed block. 73 * 74 * @return string Returns the post content with the legacy widget added. 75 */ 76 function render_block_core_navigation_submenu( $attributes, $content, $block ) { 77 // Check if this navigation item should render based on post status. 78 if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) { 79 if ( ! gutenberg_block_core_shared_navigation_item_should_render( $attributes, $block ) ) { 80 return ''; 81 } 82 } 83 84 // Don't render the block's subtree if it has no label. 85 if ( empty( $attributes['label'] ) ) { 86 return ''; 87 } 88 89 // Render inner blocks first to check if any menu items will actually display. 90 $inner_blocks_html = ''; 91 foreach ( $block->inner_blocks as $inner_block ) { 92 $inner_blocks_html .= $inner_block->render(); 93 } 94 $has_submenu = ! empty( trim( $inner_blocks_html ) ); 95 96 $kind = empty( $attributes['kind'] ) ? 'post_type' : str_replace( '-', '_', $attributes['kind'] ); 97 $is_active = ! empty( $attributes['id'] ) && get_queried_object_id() === (int) $attributes['id'] && ! empty( get_queried_object()->$kind ); 98 99 if ( is_post_type_archive() && ! empty( $attributes['url'] ) ) { 100 $queried_archive_link = get_post_type_archive_link( get_queried_object()->name ); 101 if ( $attributes['url'] === $queried_archive_link ) { 102 $is_active = true; 103 } 104 } 105 106 $show_submenu_indicators = isset( $block->context['showSubmenuIcon'] ) && $block->context['showSubmenuIcon']; 107 $computed_visibility = block_core_navigation_submenu_get_submenu_visibility( $block->context ); 108 $open_on_click = 'click' === $computed_visibility; 109 $open_on_hover = 'hover' === $computed_visibility; 110 $open_on_hover_and_click = $open_on_hover && $show_submenu_indicators; 111 112 $classes = array( 113 'wp-block-navigation-item', 114 ); 115 116 if ( $has_submenu ) { 117 $classes[] = 'has-child'; 118 } 119 if ( $open_on_click ) { 120 $classes[] = 'open-on-click'; 121 } 122 if ( $open_on_hover_and_click ) { 123 $classes[] = 'open-on-hover-click'; 124 } 125 if ( 'always' === $computed_visibility ) { 126 $classes[] = 'open-always'; 127 } 128 if ( $is_active ) { 129 $classes[] = 'current-menu-item'; 130 } 131 132 $wrapper_attributes = get_block_wrapper_attributes( 133 array( 134 'class' => implode( ' ', $classes ), 135 ) 136 ); 137 138 $label = ''; 139 140 if ( isset( $attributes['label'] ) ) { 141 $label .= wp_kses_post( $attributes['label'] ); 142 } 143 144 $aria_label = sprintf( 145 /* translators: Accessibility text. %s: Parent page title. */ 146 __( '%s submenu' ), 147 wp_strip_all_tags( $label ) 148 ); 149 150 $html = '<li ' . $wrapper_attributes . '>'; 151 152 // If Submenus open on hover or are always open, we render an anchor tag with attributes. 153 // If submenu icons are set to show, we also render a submenu button, so the submenu can be opened on click. 154 if ( ! $open_on_click ) { 155 $item_url = $attributes['url'] ?? ''; 156 // Start appending HTML attributes to anchor tag. 157 $html .= '<a class="wp-block-navigation-item__content"'; 158 159 // The href attribute on a and area elements is not required; 160 // when those elements do not have href attributes they do not create hyperlinks. 161 // But also The href attribute must have a value that is a valid URL potentially 162 // surrounded by spaces. 163 // see: https://html.spec.whatwg.org/multipage/links.html#links-created-by-a-and-area-elements. 164 if ( ! empty( $item_url ) ) { 165 $html .= ' href="' . esc_url( $item_url ) . '"'; 166 } 167 168 if ( $is_active ) { 169 $html .= ' aria-current="page"'; 170 } 171 172 if ( isset( $attributes['opensInNewTab'] ) && true === $attributes['opensInNewTab'] ) { 173 $html .= ' target="_blank" '; 174 } 175 176 if ( isset( $attributes['rel'] ) ) { 177 $html .= ' rel="' . esc_attr( $attributes['rel'] ) . '"'; 178 } elseif ( isset( $attributes['nofollow'] ) && $attributes['nofollow'] ) { 179 $html .= ' rel="nofollow"'; 180 } 181 182 if ( isset( $attributes['title'] ) ) { 183 $html .= ' title="' . esc_attr( $attributes['title'] ) . '"'; 184 } 185 186 $html .= '>'; 187 // End appending HTML attributes to anchor tag. 188 189 $html .= '<span class="wp-block-navigation-item__label">'; 190 $html .= $label; 191 $html .= '</span>'; 192 193 // Add description if available. 194 if ( ! empty( $attributes['description'] ) ) { 195 $html .= '<span class="wp-block-navigation-item__description">'; 196 $html .= wp_kses_post( $attributes['description'] ); 197 $html .= '</span>'; 198 } 199 200 $html .= '</a>'; 201 // End anchor tag content. 202 203 if ( $show_submenu_indicators && $has_submenu ) { 204 // The submenu icon is rendered in a button here 205 // so that there's a clickable element to open the submenu. 206 $html .= '<button aria-label="' . esc_attr( $aria_label ) . '" class="wp-block-navigation__submenu-icon wp-block-navigation-submenu__toggle" aria-expanded="false">'; 207 if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) { 208 $html .= gutenberg_block_core_shared_navigation_render_submenu_icon(); 209 } else { 210 $html .= block_core_shared_navigation_render_submenu_icon(); 211 } 212 $html .= '</button>'; 213 } 214 } else { 215 $html .= '<button aria-label="' . esc_attr( $aria_label ) . '" class="wp-block-navigation-item__content wp-block-navigation-submenu__toggle" aria-expanded="false">'; 216 217 // Wrap title with span to isolate it from submenu icon. 218 $html .= '<span class="wp-block-navigation-item__label">'; 219 220 $html .= $label; 221 222 $html .= '</span>'; 223 224 // Add description if available. 225 if ( ! empty( $attributes['description'] ) ) { 226 $html .= '<span class="wp-block-navigation-item__description">'; 227 $html .= wp_kses_post( $attributes['description'] ); 228 $html .= '</span>'; 229 } 230 231 $html .= '</button>'; 232 233 if ( $has_submenu ) { 234 $html .= '<span class="wp-block-navigation__submenu-icon">'; 235 if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) { 236 $html .= gutenberg_block_core_shared_navigation_render_submenu_icon(); 237 } else { 238 $html .= block_core_shared_navigation_render_submenu_icon(); 239 } 240 $html .= '</span>'; 241 } 242 } 243 244 if ( $has_submenu ) { 245 // Copy some attributes from the parent block to this one. 246 // Ideally this would happen in the client when the block is created. 247 if ( array_key_exists( 'overlayTextColor', $block->context ) ) { 248 $attributes['textColor'] = $block->context['overlayTextColor']; 249 } 250 if ( array_key_exists( 'overlayBackgroundColor', $block->context ) ) { 251 $attributes['backgroundColor'] = $block->context['overlayBackgroundColor']; 252 } 253 if ( array_key_exists( 'customOverlayTextColor', $block->context ) ) { 254 $attributes['style']['color']['text'] = $block->context['customOverlayTextColor']; 255 } 256 if ( array_key_exists( 'customOverlayBackgroundColor', $block->context ) ) { 257 $attributes['style']['color']['background'] = $block->context['customOverlayBackgroundColor']; 258 } 259 260 // This allows us to be able to get a response from wp_apply_colors_support. 261 $block->block_type->supports['color'] = true; 262 $colors_supports = wp_apply_colors_support( $block->block_type, $attributes ); 263 $css_classes = 'wp-block-navigation__submenu-container'; 264 if ( array_key_exists( 'class', $colors_supports ) ) { 265 $css_classes .= ' ' . $colors_supports['class']; 266 } 267 268 $style_attribute = ''; 269 if ( array_key_exists( 'style', $colors_supports ) ) { 270 $style_attribute = $colors_supports['style']; 271 } 272 273 if ( strpos( $inner_blocks_html, 'current-menu-item' ) ) { 274 $tag_processor = new WP_HTML_Tag_Processor( $html ); 275 while ( $tag_processor->next_tag( array( 'class_name' => 'wp-block-navigation-item' ) ) ) { 276 $tag_processor->add_class( 'current-menu-ancestor' ); 277 } 278 $html = $tag_processor->get_updated_html(); 279 } 280 281 $wrapper_attributes = get_block_wrapper_attributes( 282 array( 283 'class' => $css_classes, 284 'style' => $style_attribute, 285 ) 286 ); 287 288 $html .= sprintf( 289 '<ul %s>%s</ul>', 290 $wrapper_attributes, 291 $inner_blocks_html 292 ); 293 294 } 295 296 $html .= '</li>'; 297 298 return $html; 299 } 300 301 /** 302 * Register the navigation submenu block. 303 * 304 * @since 5.9.0 305 * 306 * @uses render_block_core_navigation_submenu() 307 * @throws WP_Error An WP_Error exception parsing the block definition. 308 */ 309 function register_block_core_navigation_submenu() { 310 register_block_type_from_metadata( 311 __DIR__ . '/navigation-submenu', 312 array( 313 'render_callback' => 'render_block_core_navigation_submenu', 314 ) 315 ); 316 } 317 add_action( 'init', 'register_block_core_navigation_submenu' );
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Tue Jul 14 08:20:16 2026 | Cross-referenced by PHPXref |