diff --git a/lib/blocks.php b/lib/blocks.php index 82339e155fd0f9..5d8069e0f45a9f 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 b6be7a4206e4a8..8bbf88c23a00f6 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 00000000000000..be71511cf61acc --- /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 = '
  • ' . + ''; + + if ( isset( $attributes['label'] ) ) { + $html .= wp_kses( + $attributes['label'], + array( + 'code' => array(), + 'em' => array(), + 'img' => array( + 'scale' => array(), + 'class' => array(), + 'style' => array(), + 'src' => array(), + 'alt' => array(), + ), + 's' => array(), + 'span' => array( + 'style' => array(), + ), + 'strong' => array(), + ) + ); + } + + $html .= ''; + + $html .= ''; + // End anchor tag content. + + if ( $block->context['showSubmenuIcon'] && $has_submenu ) { + // The submenu icon can be hidden by a CSS rule on the Navigation Block. + $html .= '' . block_core_navigation_link_render_submenu_icon() . ''; + } + + if ( $has_submenu ) { + $inner_blocks_html = ''; + foreach ( $block->inner_blocks as $inner_block ) { + $inner_blocks_html .= $inner_block->render(); + } + + // TODO - classname is wrong! + $html .= sprintf( + '', + $inner_blocks_html + ); + } + + $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 00000000000000..83e5578d1916c6 --- /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 2da6cd5b3fa207..050e31f3fffdc1 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 2462a921bf6c56..e28c5216671013 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 .= '
  • ' . - ''; - - if ( isset( $block['attrs']['label'] ) ) { - $html .= wp_kses( - $block['attrs']['label'], - array( - 'code' => array(), - 'em' => array(), - 'img' => array( - 'scale' => array(), - 'class' => array(), - 'style' => array(), - 'src' => array(), - 'alt' => array(), - ), - 's' => array(), - 'span' => array( - 'style' => array(), - ), - 'strong' => array(), - ) - ); - } - - $html .= ''; - - $html .= ''; - // End anchor tag content. - - // Append submenu icon to top-level item. - // it shows the icon as default, when 'showSubmenuIcon' is not set, - // or when it's set and also not False. - if ( - ( - isset( $attributes['showSubmenuIcon'] ) && false !== $attributes['showSubmenuIcon'] || - ! isset( $attributes['showSubmenuIcon'] ) - ) && - $has_submenu - ) { - $html .= '' . block_core_navigation_render_submenu_icon() . ''; - } - - if ( $has_submenu ) { - $html .= block_core_navigation_build_html( $attributes, $block, $colors, $font_sizes ); - } - - $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 6284d3f8b0d04b..99366a5501c21e 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 2ef1a87bba4179..47f8daf198b779 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";