diff --git a/packages/block-library/src/navigation-link/block.json b/packages/block-library/src/navigation-link/block.json index efcc6810705506..309073155c1009 100644 --- a/packages/block-library/src/navigation-link/block.json +++ b/packages/block-library/src/navigation-link/block.json @@ -36,6 +36,9 @@ }, "kind": { "type": "string" + }, + "isTopLevelLink": { + "type": "boolean" } }, "usesContext": [ @@ -43,6 +46,10 @@ "customTextColor", "backgroundColor", "customBackgroundColor", + "overlayTextColor", + "customOverlayTextColor", + "overlayBackgroundColor", + "customOverlayBackgroundColor", "fontSize", "customFontSize", "showSubmenuIcon", diff --git a/packages/block-library/src/navigation-link/edit.js b/packages/block-library/src/navigation-link/edit.js index ab8b44624fe2c6..f390210bf19330 100644 --- a/packages/block-library/src/navigation-link/edit.js +++ b/packages/block-library/src/navigation-link/edit.js @@ -29,6 +29,7 @@ import { __experimentalLinkControl as LinkControl, useBlockProps, store as blockEditorStore, + getColorClassName, } from '@wordpress/block-editor'; import { isURL, prependHTTP, safeDecodeURI } from '@wordpress/url'; import { @@ -133,6 +134,61 @@ function getSuggestionsQuery( type, kind ) { } } +/** + * Determine the colors for a menu. + * + * Order of priority is: + * 1: Overlay custom colors (if submenu) + * 2: Overlay theme colors (if submenu) + * 3: Custom colors + * 4: Theme colors + * 5: Global styles + * + * @param {Object} context + * @param {boolean} isSubMenu + */ +function getColors( context, isSubMenu ) { + const { + textColor, + customTextColor, + backgroundColor, + customBackgroundColor, + overlayTextColor, + customOverlayTextColor, + overlayBackgroundColor, + customOverlayBackgroundColor, + style, + } = context; + + const colors = {}; + + if ( isSubMenu && !! customOverlayTextColor ) { + colors.customTextColor = customOverlayTextColor; + } else if ( isSubMenu && !! overlayTextColor ) { + colors.textColor = overlayTextColor; + } else if ( !! customTextColor ) { + colors.customTextColor = customTextColor; + } else if ( !! textColor ) { + colors.textColor = textColor; + } else if ( !! style?.color?.text ) { + colors.customTextColor = style.color.text; + } + + if ( isSubMenu && !! customOverlayBackgroundColor ) { + colors.customBackgroundColor = customOverlayBackgroundColor; + } else if ( isSubMenu && !! overlayBackgroundColor ) { + colors.backgroundColor = overlayBackgroundColor; + } else if ( !! customBackgroundColor ) { + colors.customBackgroundColor = customBackgroundColor; + } else if ( !! backgroundColor ) { + colors.backgroundColor = backgroundColor; + } else if ( !! style?.color?.background ) { + colors.customTextColor = style.color.background; + } + + return colors; +} + /** * @typedef {'post-type'|'custom'|'taxonomy'|'post-type-archive'} WPNavigationLinkKind */ @@ -234,7 +290,7 @@ export default function NavigationLinkEdit( { url, opensInNewTab, }; - const { textColor, backgroundColor, style, showSubmenuIcon } = context; + const { showSubmenuIcon } = context; const { saveEntityRecord } = useDispatch( coreStore ); const { insertBlock } = useDispatch( blockEditorStore ); const [ isLinkOpen, setIsLinkOpen ] = useState( false ); @@ -245,6 +301,7 @@ export default function NavigationLinkEdit( { const { isAtMaxNesting, + isTopLevelLink, isParentOfSelectedBlock, isImmediateParentOfSelectedBlock, hasDescendants, @@ -270,6 +327,8 @@ export default function NavigationLinkEdit( { isAtMaxNesting: getBlockParentsByBlockName( clientId, name ).length >= MAX_NESTING, + isTopLevelLink: + getBlockParentsByBlockName( clientId, name ).length === 0, isParentOfSelectedBlock: hasSelectedInnerBlock( clientId, true @@ -296,6 +355,9 @@ export default function NavigationLinkEdit( { [ clientId ] ); + // Store the colors from context as attributes for rendering + useEffect( () => setAttributes( { isTopLevelLink } ), [ isTopLevelLink ] ); + /** * Insert a link block when submenu is added. */ @@ -381,6 +443,13 @@ export default function NavigationLinkEdit( { }; } + const { + textColor, + customTextColor, + backgroundColor, + customBackgroundColor, + } = getColors( context, ! isTopLevelLink ); + const blockProps = useBlockProps( { ref: listItemRef, className: classnames( { @@ -388,14 +457,17 @@ export default function NavigationLinkEdit( { 'is-dragging-within': isDraggingWithin, 'has-link': !! url, 'has-child': hasDescendants, - 'has-text-color': !! textColor || !! style?.color?.text, - [ `has-${ textColor }-color` ]: !! textColor, - 'has-background': !! backgroundColor || !! style?.color?.background, - [ `has-${ backgroundColor }-background-color` ]: !! backgroundColor, + 'has-text-color': !! textColor || !! customTextColor, + [ getColorClassName( 'color', textColor ) ]: !! textColor, + 'has-background': !! backgroundColor || customBackgroundColor, + [ getColorClassName( + 'background-color', + backgroundColor + ) ]: !! backgroundColor, } ), style: { - color: style?.color?.text, - backgroundColor: style?.color?.background, + color: ! textColor && customTextColor, + backgroundColor: ! backgroundColor && customBackgroundColor, }, } ); @@ -403,11 +475,27 @@ export default function NavigationLinkEdit( { blockProps.onClick = () => setIsLinkOpen( true ); } + // Always use overlay colors for submenus + const innerBlocksColors = getColors( context, true ); const innerBlocksProps = useInnerBlocksProps( { className: classnames( 'wp-block-navigation-link__container', { 'is-parent-of-selected-block': isParentOfSelectedBlock, + 'has-text-color': !! ( + innerBlocksColors.textColor || + innerBlocksColors.customTextColor + ), + [ `has-${ innerBlocksColors.textColor }-color` ]: !! innerBlocksColors.textColor, + 'has-background': !! ( + innerBlocksColors.backgroundColor || + innerBlocksColors.customBackgroundColor + ), + [ `has-${ innerBlocksColors.backgroundColor }-background-color` ]: !! innerBlocksColors.backgroundColor, } ), + style: { + color: innerBlocksColors.customTextColor, + backgroundColor: innerBlocksColors.customBackgroundColor, + }, }, { allowedBlocks: ALLOWED_BLOCKS, diff --git a/packages/block-library/src/navigation-link/index.php b/packages/block-library/src/navigation-link/index.php index a7e7c239b5eddc..3969318e5cac7c 100644 --- a/packages/block-library/src/navigation-link/index.php +++ b/packages/block-library/src/navigation-link/index.php @@ -9,49 +9,68 @@ * 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. + * @param array $context Navigation block context. + * @param array $attributes Block attributes. * @return array Colors CSS classes and inline styles. */ -function block_core_navigation_link_build_css_colors( $context ) { +function block_core_navigation_link_build_css_colors( $context, $attributes ) { $colors = array( 'css_classes' => array(), 'inline_styles' => '', ); - // Text color. - $has_named_text_color = array_key_exists( 'textColor', $context ); - $has_custom_text_color = isset( $context['style']['color']['text'] ); + $is_sub_menu = isset( $attributes['isTopLevelLink'] ) ? ( ! $attributes['isTopLevelLink'] ) : false; - // If has text color. - if ( $has_custom_text_color || $has_named_text_color ) { - // Add has-text-color class. - $colors['css_classes'][] = 'has-text-color'; + // Text color. + $named_text_color = null; + $custom_text_color = null; + + if ( $is_sub_menu && array_key_exists( 'customOverlayTextColor', $context ) ) { + $custom_text_color = $context['customOverlayTextColor']; + } elseif ( $is_sub_menu && array_key_exists( 'overlayTextColor', $context ) ) { + $named_text_color = $context['overlayTextColor']; + } elseif ( array_key_exists( 'customTextColor', $context ) ) { + $custom_text_color = $context['customTextColor']; + } elseif ( array_key_exists( 'textColor', $context ) ) { + $named_text_color = $context['textColor']; + } elseif ( isset( $context['style']['color']['text'] ) ) { + $custom_text_color = $context['style']['color']['text']; } - if ( $has_named_text_color ) { + // If has text color. + if ( ! is_null( $named_text_color ) ) { // Add the color class. - $colors['css_classes'][] = sprintf( 'has-%s-color', $context['textColor'] ); - } elseif ( $has_custom_text_color ) { + array_push( $colors['css_classes'], 'has-text-color', sprintf( 'has-%s-color', $named_text_color ) ); + } elseif ( ! is_null( $custom_text_color ) ) { // Add the custom color inline style. - $colors['inline_styles'] .= sprintf( 'color: %s;', $context['style']['color']['text'] ); + $colors['css_classes'][] = 'has-text-color'; + $colors['inline_styles'] .= sprintf( 'color: %s;', $custom_text_color ); } // Background color. - $has_named_background_color = array_key_exists( 'backgroundColor', $context ); - $has_custom_background_color = isset( $context['style']['color']['background'] ); - - // If has background color. - if ( $has_custom_background_color || $has_named_background_color ) { - // Add has-background class. - $colors['css_classes'][] = 'has-background'; + $named_background_color = null; + $custom_background_color = null; + + if ( $is_sub_menu && array_key_exists( 'customOverlayBackgroundColor', $context ) ) { + $custom_background_color = $context['customOverlayBackgroundColor']; + } elseif ( $is_sub_menu && array_key_exists( 'overlayBackgroundColor', $context ) ) { + $named_background_color = $context['overlayBackgroundColor']; + } elseif ( array_key_exists( 'customBackgroundColor', $context ) ) { + $custom_background_color = $context['customBackgroundColor']; + } elseif ( array_key_exists( 'backgroundColor', $context ) ) { + $named_background_color = $context['backgroundColor']; + } elseif ( isset( $context['style']['color']['background'] ) ) { + $custom_background_color = $context['style']['color']['background']; } - if ( $has_named_background_color ) { + // If has background color. + if ( ! is_null( $named_background_color ) ) { // Add the background-color class. - $colors['css_classes'][] = sprintf( 'has-%s-background-color', $context['backgroundColor'] ); - } elseif ( $has_custom_background_color ) { + array_push( $colors['css_classes'], 'has-background', sprintf( 'has-%s-background-color', $named_background_color ) ); + } elseif ( ! is_null( $custom_background_color ) ) { // Add the custom background-color inline style. - $colors['inline_styles'] .= sprintf( 'background-color: %s;', $context['style']['color']['background'] ); + $colors['css_classes'][] = 'has-background'; + $colors['inline_styles'] .= sprintf( 'background-color: %s;', $custom_background_color ); } return $colors; @@ -121,7 +140,7 @@ function render_block_core_navigation_link( $attributes, $content, $block ) { return ''; } - $colors = block_core_navigation_link_build_css_colors( $block->context ); + $colors = block_core_navigation_link_build_css_colors( $block->context, $attributes ); $font_sizes = block_core_navigation_link_build_css_font_sizes( $block->context ); $classes = array_merge( $colors['css_classes'], diff --git a/packages/block-library/src/navigation/block.json b/packages/block-library/src/navigation/block.json index 0293ce53386e80..79da72eb86a2fc 100644 --- a/packages/block-library/src/navigation/block.json +++ b/packages/block-library/src/navigation/block.json @@ -41,6 +41,18 @@ }, "__unstableLocation": { "type": "string" + }, + "overlayBackgroundColor": { + "type": "string" + }, + "customOverlayBackgroundColor": { + "type": "string" + }, + "overlayTextColor": { + "type": "string" + }, + "customOverlayTextColor": { + "type": "string" } }, "providesContext": { @@ -48,6 +60,10 @@ "customTextColor": "customTextColor", "backgroundColor": "backgroundColor", "customBackgroundColor": "customBackgroundColor", + "overlayTextColor": "overlayTextColor", + "customOverlayTextColor": "customOverlayTextColor", + "overlayBackgroundColor": "overlayBackgroundColor", + "customOverlayBackgroundColor": "customOverlayBackgroundColor", "fontSize": "fontSize", "customFontSize": "customFontSize", "showSubmenuIcon": "showSubmenuIcon", @@ -67,8 +83,7 @@ "__experimentalTextTransform": true, "__experimentalFontFamily": true, "__experimentalTextDecoration": true - }, - "color": true + } }, "viewScript": "file:./view.min.js", "editorStyle": "wp-block-navigation-editor", diff --git a/packages/block-library/src/navigation/edit.js b/packages/block-library/src/navigation/edit.js index 0e0e0ab021ce5b..ff1b4ed1f9d914 100644 --- a/packages/block-library/src/navigation/edit.js +++ b/packages/block-library/src/navigation/edit.js @@ -6,7 +6,13 @@ import classnames from 'classnames'; /** * WordPress dependencies */ -import { useState, useMemo } from '@wordpress/element'; +import { + useState, + useEffect, + useMemo, + useRef, + Platform, +} from '@wordpress/element'; import { InnerBlocks, __experimentalUseInnerBlocksProps as useInnerBlocksProps, @@ -15,6 +21,10 @@ import { BlockControls, useBlockProps, store as blockEditorStore, + withColors, + PanelColorSettings, + ContrastChecker, + getColorClassName, } from '@wordpress/block-editor'; import { useDispatch, withSelect, withDispatch } from '@wordpress/data'; import { PanelBody, ToggleControl, ToolbarGroup } from '@wordpress/components'; @@ -43,6 +53,33 @@ const LAYOUT = { alignments: [], }; +function getComputedStyle( node ) { + return node.ownerDocument.defaultView.getComputedStyle( node ); +} + +function detectColors( colorsDetectionElement, setColor, setBackground ) { + if ( ! colorsDetectionElement ) { + return; + } + setColor( getComputedStyle( colorsDetectionElement ).color ); + + let backgroundColorNode = colorsDetectionElement; + let backgroundColor = getComputedStyle( backgroundColorNode ) + .backgroundColor; + while ( + backgroundColor === 'rgba(0, 0, 0, 0)' && + backgroundColorNode.parentNode && + backgroundColorNode.parentNode.nodeType === + backgroundColorNode.parentNode.ELEMENT_NODE + ) { + backgroundColorNode = backgroundColorNode.parentNode; + backgroundColor = getComputedStyle( backgroundColorNode ) + .backgroundColor; + } + + setBackground( backgroundColor ); +} + function Navigation( { selectedBlockHasDescendants, attributes, @@ -53,8 +90,17 @@ function Navigation( { isSelected, updateInnerBlocks, className, + backgroundColor, + setBackgroundColor, + textColor, + setTextColor, + overlayBackgroundColor, + setOverlayBackgroundColor, + overlayTextColor, + setOverlayTextColor, hasSubmenuIndicatorSetting = true, hasItemJustificationControls = true, + hasColorSettings = true, } ) { const [ isPlaceholderShown, setIsPlaceholderShown ] = useState( ! hasExistingNavItems @@ -65,12 +111,29 @@ function Navigation( { const { selectBlock } = useDispatch( blockEditorStore ); + const navRef = useRef(); + const blockProps = useBlockProps( { + ref: navRef, className: classnames( className, { [ `items-justified-${ attributes.itemsJustification }` ]: attributes.itemsJustification, 'is-vertical': attributes.orientation === 'vertical', 'is-responsive': attributes.isResponsive, + 'has-text-color': !! textColor.color || !! textColor?.class, + [ getColorClassName( + 'color', + textColor?.slug + ) ]: !! textColor?.slug, + 'has-background': !! backgroundColor.color || backgroundColor.class, + [ getColorClassName( + 'background-color', + backgroundColor?.slug + ) ]: !! backgroundColor?.slug, } ), + style: { + color: ! textColor?.slug && textColor?.color, + backgroundColor: ! backgroundColor?.slug && backgroundColor?.color, + }, } ); const { navigatorToolbarButton, navigatorModal } = useBlockNavigator( @@ -103,6 +166,38 @@ function Navigation( { } ); + // Turn on contrast checker for web only since it's not supported on mobile yet. + const enableContrastChecking = Platform.OS === 'web'; + + const [ detectedBackgroundColor, setDetectedBackgroundColor ] = useState(); + const [ detectedColor, setDetectedColor ] = useState(); + const [ + detectedOverlayBackgroundColor, + setDetectedOverlayBackgroundColor, + ] = useState(); + const [ detectedOverlayColor, setDetectedOverlayColor ] = useState(); + + useEffect( () => { + if ( ! enableContrastChecking ) { + return; + } + detectColors( + navRef.current, + setDetectedColor, + setDetectedBackgroundColor + ); + const subMenuElement = navRef.current.querySelector( + '[data-type="core/navigation-link"] [data-type="core/navigation-link"]' + ); + if ( subMenuElement ) { + detectColors( + subMenuElement, + setDetectedOverlayColor, + setDetectedOverlayBackgroundColor + ); + } + } ); + if ( isPlaceholderShown ) { return (
@@ -166,6 +261,49 @@ function Navigation( { /> ) } + { hasColorSettings && ( + + { enableContrastChecking && ( + <> + + + + ) } + + ) }