: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
$responsive_container_directives = '
data-wp-class--has-modal-open="state.isMenuOpen"
data-wp-class--is-menu-open="state.isMenuOpen"
data-wp-watch="callbacks.initMenu"
data-wp-on--keydown="actions.handleMenuKeydown"
data-wp-on-async--focusout="actions.handleMenuFocusout"
$responsive_dialog_directives = '
data-wp-bind--aria-modal="state.ariaModal"
data-wp-bind--aria-label="state.ariaLabel"
data-wp-bind--role="state.roleAttribute"
$close_button_directives = '
data-wp-on-async--click="actions.closeMenuOnClick"
$responsive_container_content_directives = '
data-wp-watch="callbacks.focusFirstElement"
$overlay_inline_styles = esc_attr( safecss_filter_attr( $colors['overlay_inline_styles'] ) );
'<button aria-haspopup="dialog" %3$s class="%6$s" %10$s>%8$s</button>
<div class="%5$s" %7$s id="%1$s" %11$s>
<div class="wp-block-navigation__responsive-close" tabindex="-1">
<div class="wp-block-navigation__responsive-dialog" %12$s>
<button %4$s class="wp-block-navigation__responsive-container-close" %13$s>%9$s</button>
<div class="wp-block-navigation__responsive-container-content" %14$s id="%1$s-content">
esc_attr( $modal_unique_id ),
$toggle_aria_label_close,
esc_attr( implode( ' ', $responsive_container_classes ) ),
esc_attr( implode( ' ', $open_button_classes ) ),
( ! empty( $overlay_inline_styles ) ) ? "style=\"$overlay_inline_styles\"" : '',
$toggle_close_button_content,
$responsive_container_directives,
$responsive_dialog_directives,
$close_button_directives,
$responsive_container_content_directives
* Get the wrapper attributes
* @param array $attributes The block attributes.
* @param WP_Block_List $inner_blocks A list of inner blocks.
* @return string Returns the navigation block markup.
private static function get_nav_wrapper_attributes( $attributes, $inner_blocks ) {
$nav_menu_name = static::get_unique_navigation_name( $attributes );
$is_interactive = static::is_interactive( $attributes, $inner_blocks );
$is_responsive_menu = static::is_responsive( $attributes );
$style = static::get_styles( $attributes );
$class = static::get_classes( $attributes );
$wrapper_attributes = get_block_wrapper_attributes(
'aria-label' => $nav_menu_name,
if ( $is_responsive_menu ) {
$nav_element_directives = static::get_nav_element_directives( $is_interactive );
$wrapper_attributes .= ' ' . $nav_element_directives;
return $wrapper_attributes;
* Gets the nav element directives.
* @param bool $is_interactive Whether the block is interactive.
* @return string the directives for the navigation element.
private static function get_nav_element_directives( $is_interactive ) {
if ( ! $is_interactive ) {
// When adding to this array be mindful of security concerns.
$nav_element_context = wp_interactivity_data_wp_context(
'overlayOpenedBy' => array(
'ariaLabel' => __( 'Menu' ),
$nav_element_directives = '
data-wp-interactive="core/navigation" '
return $nav_element_directives;
* Handle view script module loading.
* @param array $attributes The block attributes.
* @param WP_Block $block The parsed block.
* @param WP_Block_List $inner_blocks The list of inner blocks.
private static function handle_view_script_module_loading( $attributes, $block, $inner_blocks ) {
if ( static::is_interactive( $attributes, $inner_blocks ) ) {
$suffix = wp_scripts_get_suffix();
if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) {
$module_url = gutenberg_url( '/build/interactivity/navigation.min.js' );
wp_register_script_module(
'@wordpress/block-library/navigation',
isset( $module_url ) ? $module_url : includes_url( "blocks/navigation/view{$suffix}.js" ),
array( '@wordpress/interactivity' ),
defined( 'GUTENBERG_VERSION' ) ? GUTENBERG_VERSION : get_bloginfo( 'version' )
wp_enqueue_script_module( '@wordpress/block-library/navigation' );
* Returns the markup for the navigation block.
* @param array $attributes The block attributes.
* @param WP_Block_List $inner_blocks The list of inner blocks.
* @return string Returns the navigation wrapper markup.
private static function get_wrapper_markup( $attributes, $inner_blocks ) {
$inner_blocks_html = static::get_inner_blocks_html( $attributes, $inner_blocks );
if ( static::is_responsive( $attributes ) ) {
return static::get_responsive_container_markup( $attributes, $inner_blocks, $inner_blocks_html );
return $inner_blocks_html;
* Returns a unique name for the navigation.
* @param array $attributes The block attributes.
* @return string Returns a unique name for the navigation.
private static function get_unique_navigation_name( $attributes ) {
$nav_menu_name = static::get_navigation_name( $attributes );
// If the menu name has been used previously then append an ID
// to the name to ensure uniqueness across a given post.
if ( isset( static::$seen_menu_names[ $nav_menu_name ] ) && static::$seen_menu_names[ $nav_menu_name ] > 1 ) {
$count = static::$seen_menu_names[ $nav_menu_name ];
$nav_menu_name = $nav_menu_name . ' ' . ( $count );
* Renders the navigation block.
* @param array $attributes The block attributes.
* @param string $content The saved content.
* @param WP_Block $block The parsed block.
* @return string Returns the navigation block markup.
public static function render( $attributes, $content, $block ) {
* The rgbTextColor and rgbBackgroundColor attributes
* have been deprecated in favor of
* customTextColor and customBackgroundColor ones.
* Move the values from old attrs to the new ones.
if ( isset( $attributes['rgbTextColor'] ) && empty( $attributes['textColor'] ) ) {
$attributes['customTextColor'] = $attributes['rgbTextColor'];
if ( isset( $attributes['rgbBackgroundColor'] ) && empty( $attributes['backgroundColor'] ) ) {
$attributes['customBackgroundColor'] = $attributes['rgbBackgroundColor'];
unset( $attributes['rgbTextColor'], $attributes['rgbBackgroundColor'] );
$inner_blocks = static::get_inner_blocks( $attributes, $block );
// Prevent navigation blocks referencing themselves from rendering.
if ( block_core_navigation_block_contains_core_navigation( $inner_blocks ) ) {
static::handle_view_script_module_loading( $attributes, $block, $inner_blocks );
static::get_nav_wrapper_attributes( $attributes, $inner_blocks ),
static::get_wrapper_markup( $attributes, $inner_blocks )
// These functions are used for the __unstableLocation feature and only active
// when the gutenberg plugin is active.
if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) {
* Returns the menu items for a WordPress menu location.
* @param string $location The menu location.
* @return array Menu items for the location.
function block_core_navigation_get_menu_items_at_location( $location ) {
if ( empty( $location ) ) {
// Build menu data. The following approximates the code in
// `wp_nav_menu()` and `gutenberg_output_block_nav_menu`.
// Find the location in the list of locations, returning early if the
// location can't be found.
$locations = get_nav_menu_locations();
if ( ! isset( $locations[ $location ] ) ) {
// Get the menu from the location, returning early if there is no
// menu or there was an error.
$menu = wp_get_nav_menu_object( $locations[ $location ] );
if ( ! $menu || is_wp_error( $menu ) ) {
$menu_items = wp_get_nav_menu_items( $menu->term_id, array( 'update_post_term_cache' => false ) );
_wp_menu_item_classes_by_context( $menu_items );
* Sorts a standard array of menu items into a nested structure keyed by the
* @param array $menu_items Menu items to sort.
* @return array An array keyed by the id of the parent menu where each element
* is an array of menu items that belong to that parent.
function block_core_navigation_sort_menu_items_by_parent_id( $menu_items ) {
$sorted_menu_items = array();
foreach ( (array) $menu_items as $menu_item ) {
$sorted_menu_items[ $menu_item->menu_order ] = $menu_item;
unset( $menu_items, $menu_item );
$menu_items_by_parent_id = array();
foreach ( $sorted_menu_items as $menu_item ) {
$menu_items_by_parent_id[ $menu_item->menu_item_parent ][] = $menu_item;
return $menu_items_by_parent_id;
* Gets the inner blocks for the navigation block from the unstable location attribute.
* @param array $attributes The block attributes.
* @return WP_Block_List Returns the inner blocks for the navigation block.
function block_core_navigation_get_inner_blocks_from_unstable_location( $attributes ) {
$menu_items = block_core_navigation_get_menu_items_at_location( $attributes['__unstableLocation'] );
if ( empty( $menu_items ) ) {
return new WP_Block_List( array(), $attributes );
$menu_items_by_parent_id = block_core_navigation_sort_menu_items_by_parent_id( $menu_items );
$parsed_blocks = block_core_navigation_parse_blocks_from_menu_items( $menu_items_by_parent_id[0], $menu_items_by_parent_id );
return new WP_Block_List( $parsed_blocks, $attributes );
* Add Interactivity API directives to the navigation-submenu and page-list
* blocks markup using the Tag Processor.
* @param WP_HTML_Tag_Processor $tags Markup of the navigation block.
* @param array $block_attributes Block attributes.
* @return string Submenu markup with the directives injected.
function block_core_navigation_add_directives_to_submenu( $tags, $block_attributes ) {
'class_name' => 'has-child',
// Add directives to the parent `<li>`.
$tags->set_attribute( 'data-wp-interactive', 'core/navigation' );
$tags->set_attribute( 'data-wp-context', '{ "submenuOpenedBy": { "click": false, "hover": false, "focus": false }, "type": "submenu" }' );
$tags->set_attribute( 'data-wp-watch', 'callbacks.initMenu' );
$tags->set_attribute( 'data-wp-on--focusout', 'actions.handleMenuFocusout' );
$tags->set_attribute( 'data-wp-on--keydown', 'actions.handleMenuKeydown' );
// This is a fix for Safari. Without it, Safari doesn't change the active
// element when the user clicks on a button. It can be removed once we add
// an overlay to capture the clicks, instead of relying on the focusout
$tags->set_attribute( 'tabindex', '-1' );
if ( ! isset( $block_attributes['openSubmenusOnClick'] ) || false === $block_attributes['openSubmenusOnClick'] ) {
$tags->set_attribute( 'data-wp-on-async--mouseenter', 'actions.openMenuOnHover' );
$tags->set_attribute( 'data-wp-on-async--mouseleave', 'actions.closeMenuOnHover' );
// Add directives to the toggle submenu button.
'class_name' => 'wp-block-navigation-submenu__toggle',
$tags->set_attribute( 'data-wp-on-async--click', 'actions.toggleMenuOnClick' );
$tags->set_attribute( 'data-wp-bind--aria-expanded', 'state.isMenuOpen' );
// The `aria-expanded` attribute for SSR is already added in the submenu block.
// Add directives to the submenu.
'class_name' => 'wp-block-navigation__submenu-container',
$tags->set_attribute( 'data-wp-on-async--focus', 'actions.openMenuOnFocus' );
// Iterate through subitems if exist.
block_core_navigation_add_directives_to_submenu( $tags, $block_attributes );
return $tags->get_updated_html();
* Build an array with CSS classes and inline styles defining the colors
* which will be applied to the navigation markup in the front-end.
* @param array $attributes Navigation block attributes.
* @return array Colors CSS classes and inline styles.
function block_core_navigation_build_css_colors( $attributes ) {
'css_classes' => array(),
'overlay_css_classes' => array(),
'overlay_inline_styles' => '',
$has_named_text_color = array_key_exists( 'textColor', $attributes );
$has_custom_text_color = array_key_exists( 'customTextColor', $attributes );
if ( $has_custom_text_color || $has_named_text_color ) {
// Add has-text-color class.
$colors['css_classes'][] = 'has-text-color';
if ( $has_named_text_color ) {
$colors['css_classes'][] = sprintf( 'has-%s-color', $attributes['textColor'] );
} elseif ( $has_custom_text_color ) {
// Add the custom color inline style.
$colors['inline_styles'] .= sprintf( 'color: %s;', $attributes['customTextColor'] );
$has_named_background_color = array_key_exists( 'backgroundColor', $attributes );
$has_custom_background_color = array_key_exists( 'customBackgroundColor', $attributes );
// If has background color.
if ( $has_custom_background_color || $has_named_background_color ) {
// Add has-background class.
$colors['css_classes'][] = 'has-background';
if ( $has_named_background_color ) {
// Add the background-color class.
$colors['css_classes'][] = sprintf( 'has-%s-background-color', $attributes['backgroundColor'] );
} elseif ( $has_custom_background_color ) {
// Add the custom background-color inline style.
$colors['inline_styles'] .= sprintf( 'background-color: %s;', $attributes['customBackgroundColor'] );
$has_named_overlay_text_color = array_key_exists( 'overlayTextColor', $attributes );
$has_custom_overlay_text_color = array_key_exists( 'customOverlayTextColor', $attributes );
// If has overlay text color.
if ( $has_custom_overlay_text_color || $has_named_overlay_text_color ) {
// Add has-text-color class.
$colors['overlay_css_classes'][] = 'has-text-color';
if ( $has_named_overlay_text_color ) {
// Add the overlay color class.
$colors['overlay_css_classes'][] = sprintf( 'has-%s-color', $attributes['overlayTextColor'] );
} elseif ( $has_custom_overlay_text_color ) {
// Add the custom overlay color inline style.
$colors['overlay_inline_styles'] .= sprintf( 'color: %s;', $attributes['customOverlayTextColor'] );
// Overlay background color.
$has_named_overlay_background_color = array_key_exists( 'overlayBackgroundColor', $attributes );
$has_custom_overlay_background_color = array_key_exists( 'customOverlayBackgroundColor', $attributes );
// If has overlay background color.
if ( $has_custom_overlay_background_color || $has_named_overlay_background_color ) {
// Add has-background class.
$colors['overlay_css_classes'][] = 'has-background';
if ( $has_named_overlay_background_color ) {
// Add the overlay background-color class.
$colors['overlay_css_classes'][] = sprintf( 'has-%s-background-color', $attributes['overlayBackgroundColor'] );
} elseif ( $has_custom_overlay_background_color ) {
// Add the custom overlay background-color inline style.
$colors['overlay_inline_styles'] .= sprintf( 'background-color: %s;', $attributes['customOverlayBackgroundColor'] );
* Build an array with CSS classes and inline styles defining the font sizes
* which will be applied to the navigation markup in the front-end.
* @param array $attributes Navigation block attributes.
* @return array Font size CSS classes and inline styles.
function block_core_navigation_build_css_font_sizes( $attributes ) {
'css_classes' => array(),
$has_named_font_size = array_key_exists( 'fontSize', $attributes );
$has_custom_font_size = array_key_exists( 'customFontSize', $attributes );
if ( $has_named_font_size ) {
// Add the font size class.
$font_sizes['css_classes'][] = sprintf( 'has-%s-font-size', $attributes['fontSize'] );
} elseif ( $has_custom_font_size ) {
// Add the custom font size inline style.
$font_sizes['inline_styles'] = sprintf( 'font-size: %spx;', $attributes['customFontSize'] );
* Returns the top-level submenu SVG chevron icon.