| [ 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 $should_render = gutenberg_block_core_shared_navigation_item_should_render( $attributes, $block ); 80 } else { 81 $should_render = block_core_shared_navigation_item_should_render( $attributes, $block ); 82 } 83 if ( ! $should_render ) { 84 return ''; 85 } 86 87 // Don't render the block's subtree if it has no label. 88 if ( empty( $attributes['label'] ) ) { 89 return ''; 90 } 91 92 // Render inner blocks first to check if any menu items will actually display. 93 $inner_blocks_html = ''; 94 foreach ( $block->inner_blocks as $inner_block ) { 95 $inner_blocks_html .= $inner_block->render(); 96 } 97 $has_submenu = ! empty( trim( $inner_blocks_html ) ); 98 99 $kind = empty( $attributes['kind'] ) ? 'post_type' : str_replace( '-', '_', $attributes['kind'] ); 100 $is_active = ! empty( $attributes['id'] ) && get_queried_object_id() === (int) $attributes['id'] && ! empty( get_queried_object()->$kind ); 101 102 if ( is_post_type_archive() && ! empty( $attributes['url'] ) ) { 103 $queried_archive_link = get_post_type_archive_link( get_queried_object()->name ); 104 if ( $attributes['url'] === $queried_archive_link ) { 105 $is_active = true; 106 } 107 } 108 109 $show_submenu_indicators = isset( $block->context['showSubmenuIcon'] ) && $block->context['showSubmenuIcon']; 110 $computed_visibility = block_core_navigation_submenu_get_submenu_visibility( $block->context ); 111 $open_on_click = 'click' === $computed_visibility; 112 $open_on_hover = 'hover' === $computed_visibility; 113 $open_on_hover_and_click = $open_on_hover && $show_submenu_indicators; 114 115 $classes = array( 116 'wp-block-navigation-item', 117 ); 118 119 if ( $has_submenu ) { 120 $classes[] = 'has-child'; 121 } 122 if ( $open_on_click ) { 123 $classes[] = 'open-on-click'; 124 } 125 if ( $open_on_hover_and_click ) { 126 $classes[] = 'open-on-hover-click'; 127 } 128 if ( 'always' === $computed_visibility ) { 129 $classes[] = 'open-always'; 130 } 131 if ( $is_active ) { 132 $classes[] = 'current-menu-item'; 133 } 134 135 $wrapper_attributes = get_block_wrapper_attributes( 136 array( 137 'class' => implode( ' ', $classes ), 138 ) 139 ); 140 141 $label = ''; 142 143 if ( isset( $attributes['label'] ) ) { 144 $label .= wp_kses_post( $attributes['label'] ); 145 } 146 147 $aria_label = sprintf( 148 /* translators: Accessibility text. %s: Parent page title. */ 149 __( '%s submenu' ), 150 wp_strip_all_tags( $label ) 151 ); 152 153 $html = '<li ' . $wrapper_attributes . '>'; 154 155 // If Submenus open on hover or are always open, we render an anchor tag with attributes. 156 // If submenu icons are set to show, we also render a submenu button, so the submenu can be opened on click. 157 if ( ! $open_on_click ) { 158 $item_url = $attributes['url'] ?? ''; 159 // Start appending HTML attributes to anchor tag. 160 $html .= '<a class="wp-block-navigation-item__content"'; 161 162 // The href attribute on a and area elements is not required; 163 // when those elements do not have href attributes they do not create hyperlinks. 164 // But also The href attribute must have a value that is a valid URL potentially 165 // surrounded by spaces. 166 // see: https://html.spec.whatwg.org/multipage/links.html#links-created-by-a-and-area-elements. 167 if ( ! empty( $item_url ) ) { 168 $html .= ' href="' . esc_url( $item_url ) . '"'; 169 } 170 171 if ( $is_active ) { 172 $html .= ' aria-current="page"'; 173 } 174 175 if ( isset( $attributes['opensInNewTab'] ) && true === $attributes['opensInNewTab'] ) { 176 $html .= ' target="_blank" '; 177 } 178 179 if ( isset( $attributes['rel'] ) ) { 180 $html .= ' rel="' . esc_attr( $attributes['rel'] ) . '"'; 181 } elseif ( isset( $attributes['nofollow'] ) && $attributes['nofollow'] ) { 182 $html .= ' rel="nofollow"'; 183 } 184 185 if ( isset( $attributes['title'] ) ) { 186 $html .= ' title="' . esc_attr( $attributes['title'] ) . '"'; 187 } 188 189 $html .= '>'; 190 // End appending HTML attributes to anchor tag. 191 192 $html .= '<span class="wp-block-navigation-item__label">'; 193 $html .= $label; 194 $html .= '</span>'; 195 196 // Add description if available. 197 if ( ! empty( $attributes['description'] ) ) { 198 $html .= '<span class="wp-block-navigation-item__description">'; 199 $html .= wp_kses_post( $attributes['description'] ); 200 $html .= '</span>'; 201 } 202 203 $html .= '</a>'; 204 // End anchor tag content. 205 206 if ( $show_submenu_indicators && $has_submenu ) { 207 // The submenu icon is rendered in a button here 208 // so that there's a clickable element to open the submenu. 209 $html .= '<button aria-label="' . esc_attr( $aria_label ) . '" class="wp-block-navigation__submenu-icon wp-block-navigation-submenu__toggle" aria-expanded="false">'; 210 if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) { 211 $html .= gutenberg_block_core_shared_navigation_render_submenu_icon(); 212 } else { 213 $html .= block_core_shared_navigation_render_submenu_icon(); 214 } 215 $html .= '</button>'; 216 } 217 } else { 218 $html .= '<button aria-label="' . esc_attr( $aria_label ) . '" class="wp-block-navigation-item__content wp-block-navigation-submenu__toggle" aria-expanded="false">'; 219 220 // Wrap title with span to isolate it from submenu icon. 221 $html .= '<span class="wp-block-navigation-item__label">'; 222 223 $html .= $label; 224 225 $html .= '</span>'; 226 227 // Add description if available. 228 if ( ! empty( $attributes['description'] ) ) { 229 $html .= '<span class="wp-block-navigation-item__description">'; 230 $html .= wp_kses_post( $attributes['description'] ); 231 $html .= '</span>'; 232 } 233 234 $html .= '</button>'; 235 236 if ( $has_submenu ) { 237 $html .= '<span class="wp-block-navigation__submenu-icon">'; 238 if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) { 239 $html .= gutenberg_block_core_shared_navigation_render_submenu_icon(); 240 } else { 241 $html .= block_core_shared_navigation_render_submenu_icon(); 242 } 243 $html .= '</span>'; 244 } 245 } 246 247 if ( $has_submenu ) { 248 // Copy some attributes from the parent block to this one. 249 // Ideally this would happen in the client when the block is created. 250 if ( array_key_exists( 'overlayTextColor', $block->context ) ) { 251 $attributes['textColor'] = $block->context['overlayTextColor']; 252 } 253 if ( array_key_exists( 'overlayBackgroundColor', $block->context ) ) { 254 $attributes['backgroundColor'] = $block->context['overlayBackgroundColor']; 255 } 256 if ( array_key_exists( 'customOverlayTextColor', $block->context ) ) { 257 $attributes['style']['color']['text'] = $block->context['customOverlayTextColor']; 258 } 259 if ( array_key_exists( 'customOverlayBackgroundColor', $block->context ) ) { 260 $attributes['style']['color']['background'] = $block->context['customOverlayBackgroundColor']; 261 } 262 263 // This allows us to be able to get a response from wp_apply_colors_support. 264 $block->block_type->supports['color'] = true; 265 $colors_supports = wp_apply_colors_support( $block->block_type, $attributes ); 266 $css_classes = 'wp-block-navigation__submenu-container'; 267 if ( array_key_exists( 'class', $colors_supports ) ) { 268 $css_classes .= ' ' . $colors_supports['class']; 269 } 270 271 $style_attribute = ''; 272 if ( array_key_exists( 'style', $colors_supports ) ) { 273 $style_attribute = $colors_supports['style']; 274 } 275 276 if ( strpos( $inner_blocks_html, 'current-menu-item' ) ) { 277 $tag_processor = new WP_HTML_Tag_Processor( $html ); 278 while ( $tag_processor->next_tag( array( 'class_name' => 'wp-block-navigation-item' ) ) ) { 279 $tag_processor->add_class( 'current-menu-ancestor' ); 280 } 281 $html = $tag_processor->get_updated_html(); 282 } 283 284 $wrapper_attributes = get_block_wrapper_attributes( 285 array( 286 'class' => $css_classes, 287 'style' => $style_attribute, 288 ) 289 ); 290 291 $html .= sprintf( 292 '<ul %s>%s</ul>', 293 $wrapper_attributes, 294 $inner_blocks_html 295 ); 296 297 } 298 299 $html .= '</li>'; 300 301 return $html; 302 } 303 304 /** 305 * Register the navigation submenu block. 306 * 307 * @since 5.9.0 308 * 309 * @uses render_block_core_navigation_submenu() 310 * @throws WP_Error An WP_Error exception parsing the block definition. 311 */ 312 function register_block_core_navigation_submenu() { 313 register_block_type_from_metadata( 314 __DIR__ . '/navigation-submenu', 315 array( 316 'render_callback' => 'render_block_core_navigation_submenu', 317 ) 318 ); 319 } 320 add_action( 'init', 'register_block_core_navigation_submenu' );
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Mon Jul 20 08:20:16 2026 | Cross-referenced by PHPXref |