diff --git a/lib/blocks.php b/lib/blocks.php
index 82339e155fd0f..5d8069e0f45a9 100644
--- a/lib/blocks.php
+++ b/lib/blocks.php
@@ -61,6 +61,7 @@ function gutenberg_reregister_core_block_types() {
'latest-posts.php' => 'core/latest-posts',
'legacy-widget.php' => 'core/legacy-widget',
'navigation.php' => 'core/navigation',
+ 'navigation-link.php' => 'core/navigation-link',
'rss.php' => 'core/rss',
'search.php' => 'core/search',
'shortcode.php' => 'core/shortcode',
diff --git a/packages/block-library/src/navigation-link/block.json b/packages/block-library/src/navigation-link/block.json
index b6be7a4206e4a..8bbf88c23a00f 100644
--- a/packages/block-library/src/navigation-link/block.json
+++ b/packages/block-library/src/navigation-link/block.json
@@ -29,6 +29,15 @@
"type": "string"
}
},
+ "usesContext": [
+ "textColor",
+ "customTextColor",
+ "backgroundColor",
+ "customBackgroundColor",
+ "fontSize",
+ "customFontSize",
+ "showSubmenuIcon"
+ ],
"supports": {
"reusable": false,
"html": false,
diff --git a/packages/block-library/src/navigation-link/index.php b/packages/block-library/src/navigation-link/index.php
new file mode 100644
index 0000000000000..be71511cf61ac
--- /dev/null
+++ b/packages/block-library/src/navigation-link/index.php
@@ -0,0 +1,214 @@
+ array(),
+ 'inline_styles' => '',
+ );
+
+ // Text color.
+ $has_named_text_color = array_key_exists( 'textColor', $context );
+ $has_custom_text_color = array_key_exists( 'customTextColor', $context );
+
+ // If has text color.
+ 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 ) {
+ // Add the color class.
+ $colors['css_classes'][] = sprintf( 'has-%s-color', $context['textColor'] );
+ } elseif ( $has_custom_text_color ) {
+ // Add the custom color inline style.
+ $colors['inline_styles'] .= sprintf( 'color: %s;', $context['customTextColor'] );
+ }
+
+ // Background color.
+ $has_named_background_color = array_key_exists( 'backgroundColor', $context );
+ $has_custom_background_color = array_key_exists( 'customBackgroundColor', $context );
+
+ // 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', $context['backgroundColor'] );
+ } elseif ( $has_custom_background_color ) {
+ // Add the custom background-color inline style.
+ $colors['inline_styles'] .= sprintf( 'background-color: %s;', $context['customBackgroundColor'] );
+ }
+
+ return $colors;
+}
+
+/**
+ * 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 $context Navigation block context.
+ * @return array Font size CSS classes and inline styles.
+ */
+function block_core_navigation_link_build_css_font_sizes( $context ) {
+ // CSS classes.
+ $font_sizes = array(
+ 'css_classes' => array(),
+ 'inline_styles' => '',
+ );
+
+ $has_named_font_size = array_key_exists( 'fontSize', $context );
+ $has_custom_font_size = array_key_exists( 'customFontSize', $context );
+
+ if ( $has_named_font_size ) {
+ // Add the font size class.
+ $font_sizes['css_classes'][] = sprintf( 'has-%s-font-size', $context['fontSize'] );
+ } elseif ( $has_custom_font_size ) {
+ // Add the custom font size inline style.
+ $font_sizes['inline_styles'] = sprintf( 'font-size: %spx;', $context['customFontSize'] );
+ }
+
+ return $font_sizes;
+}
+
+/**
+ * Returns the top-level submenu SVG chevron icon.
+ *
+ * @return string
+ */
+function block_core_navigation_link_render_submenu_icon() {
+ return '';
+}
+
+/**
+ * Renders the `core/navigation-link` block.
+ *
+ * @param array $attributes The block attributes.
+ * @param array $content The saved content.
+ * @param array $block The parsed block.
+ *
+ * @return string Returns the post content with the legacy widget added.
+ */
+function render_block_core_navigation_link( $attributes, $content, $block ) {
+ // Don't render the block's subtree if it has no label.
+ if ( empty( $attributes['label'] ) ) {
+ return '';
+ }
+
+ $colors = block_core_navigation_link_build_css_colors( $block->context );
+ $font_sizes = block_core_navigation_link_build_css_font_sizes( $block->context );
+ $classes = array_merge(
+ $colors['css_classes'],
+ $font_sizes['css_classes']
+ );
+ $classes[] = 'wp-block-navigation-link';
+ $style_attribute = ( $colors['inline_styles'] || $font_sizes['inline_styles'] );
+
+ $css_classes = trim( implode( ' ', $classes ) );
+ $has_submenu = count( $block->inner_blocks ) > 0;
+ $is_active = ! empty( $attributes['id'] ) && ( get_the_ID() === $attributes['id'] );
+
+ $class_name = ! empty( $attributes['className'] ) ? implode( ' ', (array) $attributes['className'] ) : false;
+
+ if ( false !== $class_name ) {
+ $css_classes .= ' ' . $class_name;
+ };
+
+ $html = '
';
+
+ return $html;
+}
+
+/**
+ * Register the navigation link block.
+ *
+ * @uses render_block_core_navigation()
+ * @throws WP_Error An WP_Error exception parsing the block definition.
+ */
+function register_block_core_navigation_link() {
+ register_block_type_from_metadata(
+ __DIR__ . '/navigation-link',
+ array(
+ 'render_callback' => 'render_block_core_navigation_link',
+ )
+ );
+}
+add_action( 'init', 'register_block_core_navigation_link' );
diff --git a/packages/block-library/src/navigation-link/style.scss b/packages/block-library/src/navigation-link/style.scss
new file mode 100644
index 0000000000000..83e5578d1916c
--- /dev/null
+++ b/packages/block-library/src/navigation-link/style.scss
@@ -0,0 +1,135 @@
+.wp-block-navigation-link {
+ display: flex;
+ align-items: center;
+ position: relative;
+ margin: 0;
+
+ .wp-block-navigation__container:empty {
+ display: none;
+ }
+}
+
+.wp-block-navigation__container {
+ // Reset the default list styles
+ list-style: none;
+ margin: 0;
+ padding-left: 0;
+
+ // Horizontal layout
+ display: flex;
+ flex-wrap: wrap;
+
+ // Vertical layout
+
+ .is-vertical & {
+ display: block;
+ }
+}
+
+// Styles for submenu flyout
+.has-child {
+ $navigation-vertical-padding: $grid-unit-10 * 0.75;
+ .wp-block-navigation__container {
+ border: $border-width solid rgba(0, 0, 0, 0.15);
+ background-color: inherit;
+ color: inherit;
+ position: absolute;
+ left: 0;
+ top: 100%;
+ width: fit-content;
+ z-index: 1;
+ opacity: 0;
+ transition: opacity 0.1s linear;
+ visibility: hidden;
+
+ > .wp-block-navigation-link {
+ > .wp-block-navigation-link__content {
+ flex-grow: 1;
+ }
+ > .wp-block-navigation-link__submenu-icon {
+ padding-right: $grid-unit-10;
+ }
+ }
+
+ @include break-medium {
+ left: $grid-unit-30;
+
+ // Nested submenus sit to the left on large breakpoints
+ .wp-block-navigation__container {
+ left: 100%;
+ top: -1px;
+
+ // Prevent the menu from disappearing when the mouse is over the gap
+ &::before {
+ content: "";
+ position: absolute;
+ right: 100%;
+ height: 100%;
+ display: block;
+ width: $grid-unit-10;
+ background: transparent;
+ }
+ }
+
+ .wp-block-navigation-link__submenu-icon svg {
+ transform: rotate(0);
+ }
+ }
+ }
+ // Separating out hover and focus-within so hover works again on IE: https://davidwalsh.name/css-focus-within#comment-513401
+ // We will need to replace focus-within with a JS solution for IE keyboard support.
+ &:hover {
+ cursor: pointer;
+
+ > .wp-block-navigation__container {
+ visibility: visible;
+ opacity: 1;
+ display: flex;
+ flex-direction: column;
+ }
+ }
+
+ &:focus-within {
+ cursor: pointer;
+
+ > .wp-block-navigation__container {
+ visibility: visible;
+ opacity: 1;
+ display: flex;
+ flex-direction: column;
+ }
+ }
+}
+
+// All links
+.wp-block-navigation-link__content {
+ text-decoration: none;
+ padding: $grid-unit-10 $grid-unit-10 * 2;
+
+ + .wp-block-navigation-link__content {
+ padding-top: 0;
+ }
+ .has-text-color & {
+ color: inherit;
+ }
+}
+
+.wp-block-navigation-link__label {
+ font-family: $default-font;
+
+ word-break: normal;
+ overflow-wrap: break-word;
+}
+
+.wp-block-navigation-link__submenu-icon {
+ height: inherit;
+ padding: $grid-unit-10 * 0.75 $grid-unit-10 * 2;
+
+ svg {
+ fill: currentColor;
+
+ @include break-medium {
+ transform: rotate(90deg);
+ }
+ }
+}
diff --git a/packages/block-library/src/navigation/block.json b/packages/block-library/src/navigation/block.json
index 2da6cd5b3fa20..050e31f3fffdc 100644
--- a/packages/block-library/src/navigation/block.json
+++ b/packages/block-library/src/navigation/block.json
@@ -40,6 +40,15 @@
"default": true
}
},
+ "providesContext": {
+ "textColor": "textColor",
+ "customTextColor": "customTextColor",
+ "backgroundColor": "backgroundColor",
+ "customBackgroundColor": "customBackgroundColor",
+ "fontSize": "fontSize",
+ "customFontSize": "customFontSize",
+ "showSubmenuIcon": "showSubmenuIcon"
+ },
"supports": {
"align": [
"wide",
diff --git a/packages/block-library/src/navigation/index.php b/packages/block-library/src/navigation/index.php
index 2462a921bf6c5..e28c521667101 100644
--- a/packages/block-library/src/navigation/index.php
+++ b/packages/block-library/src/navigation/index.php
@@ -85,31 +85,6 @@ function block_core_navigation_build_css_font_sizes( $attributes ) {
return $font_sizes;
}
-/**
- * Recursively filters out links with no labels to build a clean navigation block structure.
- *
- * @param array $blocks Link inner blocks from the Navigation block.
- * @return array Blocks that had valid labels
- */
-function block_core_navigation_empty_navigation_links_recursive( $blocks ) {
- $blocks = array_filter(
- $blocks,
- function( $block ) {
- return ! empty( $block['attrs']['label'] );
- }
- );
-
- if ( ! empty( $blocks ) ) {
- foreach ( $blocks as $key => $block ) {
- if ( ! empty( $block['innerBlocks'] ) ) {
- $blocks[ $key ]['innerBlocks'] = block_core_navigation_empty_navigation_links_recursive( $block['innerBlocks'] );
- }
- }
- }
-
- return $blocks;
-}
-
/**
* Returns the top-level submenu SVG chevron icon.
*
@@ -122,20 +97,13 @@ function block_core_navigation_render_submenu_icon() {
/**
* Renders the `core/navigation` block on server.
*
+ * @param array $attributes The block attributes.
* @param array $content The saved content.
* @param array $block The parsed block.
*
* @return string Returns the post content with the legacy widget added.
*/
-function render_block_core_navigation( $content, $block ) {
-
- if ( 'core/navigation' !== $block['blockName'] ) {
- return $content;
- }
-
- $block['innerBlocks'] = block_core_navigation_empty_navigation_links_recursive( $block['innerBlocks'] );
- $attributes = $block['attrs'];
-
+function render_block_core_navigation( $attributes, $content, $block ) {
/**
* Deprecated:
* The rgbTextColor and rgbBackgroundColor attributes
@@ -153,7 +121,7 @@ function render_block_core_navigation( $content, $block ) {
unset( $attributes['rgbTextColor'], $attributes['rgbBackgroundColor'] );
- if ( empty( $block['innerBlocks'] ) ) {
+ if ( empty( $block->inner_blocks ) ) {
return '';
}
@@ -173,112 +141,17 @@ function render_block_core_navigation( $content, $block ) {
? sprintf( ' style="%s"', esc_attr( $colors['inline_styles'] ) . esc_attr( $font_sizes['inline_styles'] ) )
: '';
+ $inner_blocks_html = '';
+ foreach ( $block->inner_blocks as $inner_block ) {
+ $inner_blocks_html .= $inner_block->render();
+ }
+
return sprintf(
- '',
+ '',
$class_attribute,
$style_attribute,
- block_core_navigation_build_html( $attributes, $block, $colors, $font_sizes, true )
- );
-}
-
-/**
- * Walks the inner block structure and returns an HTML list for it.
- *
- * @param array $attributes The Navigation block attributes.
- * @param array $block The NavigationItem block.
- * @param array $colors Contains inline styles and CSS classes to apply to navigation item.
- * @param array $font_sizes Contains inline styles and CSS classes to apply to navigation item.
- *
- * @return string Returns an HTML list from innerBlocks.
- */
-function block_core_navigation_build_html( $attributes, $block, $colors, $font_sizes ) {
- $html = '';
- $classes = array_merge(
- $colors['css_classes'],
- $font_sizes['css_classes']
+ $inner_blocks_html
);
- $classes[] = 'wp-block-navigation-link';
- $style_attribute = ( $colors['inline_styles'] || $font_sizes['inline_styles'] )
- ? sprintf( ' style="%s"', esc_attr( $colors['inline_styles'] ) . esc_attr( $font_sizes['inline_styles'] ) )
- : '';
-
- foreach ( (array) $block['innerBlocks'] as $key => $block ) {
- $css_classes = trim( implode( ' ', $classes ) );
- $has_submenu = count( (array) $block['innerBlocks'] ) > 0;
- $is_active = ! empty( $block['attrs']['id'] ) && ( get_the_ID() === $block['attrs']['id'] );
-
- $class_name = ! empty( $block['attrs']['className'] ) ? implode( ' ', (array) $block['attrs']['className'] ) : false;
-
- if ( false !== $class_name ) {
- $css_classes .= ' ' . $class_name;
- };
-
- $html .= '';
- }
- return '';
}
/**
@@ -289,8 +162,11 @@ function block_core_navigation_build_html( $attributes, $block, $colors, $font_s
*/
function register_block_core_navigation() {
register_block_type_from_metadata(
- __DIR__ . '/navigation'
+ __DIR__ . '/navigation',
+ array(
+ 'render_callback' => 'render_block_core_navigation',
+ )
);
}
+
add_action( 'init', 'register_block_core_navigation' );
-add_filter( 'render_block', 'render_block_core_navigation', 10, 2 );
diff --git a/packages/block-library/src/navigation/style.scss b/packages/block-library/src/navigation/style.scss
index 6284d3f8b0d04..99366a5501c21 100644
--- a/packages/block-library/src/navigation/style.scss
+++ b/packages/block-library/src/navigation/style.scss
@@ -1,139 +1,3 @@
-.wp-block-navigation__container {
- // Reset the default list styles
- list-style: none;
- margin: 0;
- padding-left: 0;
-
- // Horizontal layout
- display: flex;
- flex-wrap: wrap;
-
- // Vertical layout
-
- .is-vertical & {
- display: block;
- }
-}
-
-.wp-block-navigation-link {
- display: flex;
- align-items: center;
- position: relative;
- margin: 0;
-
- .wp-block-navigation__container:empty {
- display: none;
- }
-}
-
-// Styles for submenu flyout
-.has-child {
- $navigation-vertical-padding: $grid-unit-10 * 0.75;
- .wp-block-navigation__container {
- border: $border-width solid rgba(0, 0, 0, 0.15);
- background-color: inherit;
- color: inherit;
- position: absolute;
- left: 0;
- top: 100%;
- width: fit-content;
- z-index: 1;
- opacity: 0;
- transition: opacity 0.1s linear;
- visibility: hidden;
-
- > .wp-block-navigation-link {
- > .wp-block-navigation-link__content {
- flex-grow: 1;
- }
- > .wp-block-navigation-link__submenu-icon {
- padding-right: $grid-unit-10;
- }
- }
-
- @include break-medium {
- left: $grid-unit-30;
-
- // Nested submenus sit to the left on large breakpoints
- .wp-block-navigation__container {
- left: 100%;
- top: -1px;
-
- // Prevent the menu from disappearing when the mouse is over the gap
- &::before {
- content: "";
- position: absolute;
- right: 100%;
- height: 100%;
- display: block;
- width: $grid-unit-10;
- background: transparent;
- }
- }
-
- .wp-block-navigation-link__submenu-icon svg {
- transform: rotate(0);
- }
- }
- }
- // Separating out hover and focus-within so hover works again on IE: https://davidwalsh.name/css-focus-within#comment-513401
- // We will need to replace focus-within with a JS solution for IE keyboard support.
- &:hover {
- cursor: pointer;
-
- > .wp-block-navigation__container {
- visibility: visible;
- opacity: 1;
- display: flex;
- flex-direction: column;
- }
- }
-
- &:focus-within {
- cursor: pointer;
-
- > .wp-block-navigation__container {
- visibility: visible;
- opacity: 1;
- display: flex;
- flex-direction: column;
- }
- }
-}
-
-// All links
-.wp-block-navigation-link__content {
- text-decoration: none;
- padding: $grid-unit-10 $grid-unit-10 * 2;
-
- + .wp-block-navigation-link__content {
- padding-top: 0;
- }
- .has-text-color & {
- color: inherit;
- }
-}
-
-.wp-block-navigation-link__label {
- font-family: $default-font;
-
- word-break: normal;
- overflow-wrap: break-word;
-}
-
-.wp-block-navigation-link__submenu-icon {
- height: inherit;
- padding: $grid-unit-10 * 0.75 $grid-unit-10 * 2;
-
- svg {
- fill: currentColor;
-
- @include break-medium {
- transform: rotate(90deg);
- }
- }
-}
-
// Default / Light styles
.wp-block-navigation,
.wp-block-navigation.is-style-light {
diff --git a/packages/block-library/src/style.scss b/packages/block-library/src/style.scss
index 2ef1a87bba417..47f8daf198b77 100644
--- a/packages/block-library/src/style.scss
+++ b/packages/block-library/src/style.scss
@@ -19,6 +19,7 @@
@import "./latest-posts/style.scss";
@import "./media-text/style.scss";
@import "./navigation/style.scss";
+@import "./navigation-link/style.scss";
@import "./paragraph/style.scss";
@import "./post-author/style.scss";
@import "./pullquote/style.scss";