Skip to content
Merged
1 change: 1 addition & 0 deletions lib/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
9 changes: 9 additions & 0 deletions packages/block-library/src/navigation-link/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@
"type": "string"
}
},
"usesContext": [
"textColor",
"customTextColor",
"backgroundColor",
"customBackgroundColor",
"fontSize",
"customFontSize",
"showSubmenuIcon"
],
"supports": {
"reusable": false,
"html": false,
Expand Down
214 changes: 214 additions & 0 deletions packages/block-library/src/navigation-link/index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
<?php
/**
* Server-side rendering of the `core/navigation-link` block.
*
* @package gutenberg
*/

/**
* 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 $context Navigation block context.
* @return array Colors CSS classes and inline styles.
*/
function block_core_navigation_link_build_css_colors( $context ) {
$colors = array(
'css_classes' => 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 '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" transform="rotate(90)"><path d="M8 5v14l11-7z"/><path d="M0 0h24v24H0z" fill="none"/></svg>';
}

/**
* 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 = '<li class="' . esc_attr( $css_classes . ( $has_submenu ? ' has-child' : '' ) ) .
( $is_active ? ' current-menu-item' : '' ) . '"' . $style_attribute . '>' .
'<a class="wp-block-navigation-link__content"';

// Start appending HTML attributes to anchor tag.
if ( isset( $attributes['url'] ) ) {
$html .= ' href="' . esc_url( $attributes['url'] ) . '"';
}

if ( isset( $attributes['opensInNewTab'] ) && true === $attributes['opensInNewTab'] ) {
$html .= ' target="_blank" ';
}
// End appending HTML attributes to anchor tag.

// Start anchor tag content.
$html .= '>' .
// Wrap title with span to isolate it from submenu icon.
'<span class="wp-block-navigation-link__label">';

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 .= '</span>';

$html .= '</a>';
// 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 .= '<span class="wp-block-navigation-link__submenu-icon">' . block_core_navigation_link_render_submenu_icon() . '</span>';
}

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(
'<ul class="wp-block-navigation__container">%s</ul>',
$inner_blocks_html
);
}

$html .= '</li>';

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' );
135 changes: 135 additions & 0 deletions packages/block-library/src/navigation-link/style.scss
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
9 changes: 9 additions & 0 deletions packages/block-library/src/navigation/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@
"default": true
}
},
"providesContext": {
"textColor": "textColor",
"customTextColor": "customTextColor",
"backgroundColor": "backgroundColor",
"customBackgroundColor": "customBackgroundColor",
"fontSize": "fontSize",
"customFontSize": "customFontSize",
"showSubmenuIcon": "showSubmenuIcon"
},
"supports": {
"align": [
"wide",
Expand Down
Loading