UNPKG

@wordpress/block-library

Version:
939 lines (869 loc) 26.8 kB
/** * External dependencies */ import classnames from 'classnames'; /** * WordPress dependencies */ import { useState, useEffect, useRef, Platform } from '@wordpress/element'; import { addQueryArgs } from '@wordpress/url'; import { InspectorControls, useBlockProps, __experimentalRecursionProvider as RecursionProvider, __experimentalUseHasRecursion as useHasRecursion, store as blockEditorStore, withColors, PanelColorSettings, ContrastChecker, getColorClassName, Warning, __experimentalUseBlockOverlayActive as useBlockOverlayActive, } from '@wordpress/block-editor'; import { EntityProvider, store as coreStore } from '@wordpress/core-data'; import { useDispatch } from '@wordpress/data'; import { PanelBody, ToggleControl, __experimentalToggleGroupControl as ToggleGroupControl, __experimentalToggleGroupControlOption as ToggleGroupControlOption, Button, Spinner, } from '@wordpress/components'; import { __, sprintf } from '@wordpress/i18n'; import { speak } from '@wordpress/a11y'; import { createBlock } from '@wordpress/blocks'; import { close, Icon } from '@wordpress/icons'; /** * Internal dependencies */ import useNavigationMenu from '../use-navigation-menu'; import useNavigationEntities from '../use-navigation-entities'; import Placeholder from './placeholder'; import ResponsiveWrapper from './responsive-wrapper'; import NavigationInnerBlocks from './inner-blocks'; import NavigationMenuSelector from './navigation-menu-selector'; import NavigationMenuNameControl from './navigation-menu-name-control'; import UnsavedInnerBlocks from './unsaved-inner-blocks'; import NavigationMenuDeleteControl from './navigation-menu-delete-control'; import useNavigationNotice from './use-navigation-notice'; import OverlayMenuIcon from './overlay-menu-icon'; import OverlayMenuPreview from './overlay-menu-preview'; import useConvertClassicToBlockMenu, { CLASSIC_MENU_CONVERSION_ERROR, CLASSIC_MENU_CONVERSION_PENDING, CLASSIC_MENU_CONVERSION_SUCCESS, } from './use-convert-classic-menu-to-block-menu'; import useCreateNavigationMenu from './use-create-navigation-menu'; import { useInnerBlocks } from './use-inner-blocks'; import { detectColors } from './utils'; function Navigation( { attributes, setAttributes, clientId, isSelected, className, backgroundColor, setBackgroundColor, textColor, setTextColor, overlayBackgroundColor, setOverlayBackgroundColor, overlayTextColor, setOverlayTextColor, // These props are used by the navigation editor to override specific // navigation block settings. hasSubmenuIndicatorSetting = true, hasColorSettings = true, customPlaceholder: CustomPlaceholder = null, } ) { const { openSubmenusOnClick, overlayMenu, showSubmenuIcon, layout: { justifyContent, orientation = 'horizontal', flexWrap = 'wrap', } = {}, hasIcon, icon = 'handle', } = attributes; const ref = attributes.ref; const setRef = ( postId ) => { setAttributes( { ref: postId } ); }; const recursionId = `navigationMenu/${ ref }`; const hasAlreadyRendered = useHasRecursion( recursionId ); const { editEntityRecord } = useDispatch( coreStore ); // Preload classic menus, so that they don't suddenly pop-in when viewing // the Select Menu dropdown. useNavigationEntities(); const [ showNavigationMenuStatusNotice, hideNavigationMenuStatusNotice ] = useNavigationNotice( { name: 'block-library/core/navigation/status', } ); const [ showClassicMenuConversionNotice, hideClassicMenuConversionNotice ] = useNavigationNotice( { name: 'block-library/core/navigation/classic-menu-conversion', } ); const [ showMenuAutoPublishDraftNotice, hideMenuAutoPublishDraftNotice ] = useNavigationNotice( { name: 'block-library/core/navigation/auto-publish-draft', } ); const [ showNavigationMenuPermissionsNotice, hideNavigationMenuPermissionsNotice, ] = useNavigationNotice( { name: 'block-library/core/navigation/permissions/update', } ); const { create: createNavigationMenu, status: createNavigationMenuStatus, error: createNavigationMenuError, value: createNavigationMenuPost, isPending: isCreatingNavigationMenu, isSuccess: createNavigationMenuIsSuccess, isError: createNavigationMenuIsError, } = useCreateNavigationMenu( clientId ); const createUntitledEmptyNavigationMenu = () => { createNavigationMenu( '' ); }; useEffect( () => { hideNavigationMenuStatusNotice(); if ( isCreatingNavigationMenu ) { speak( __( `Creating Navigation Menu.` ) ); } if ( createNavigationMenuIsSuccess ) { handleUpdateMenu( createNavigationMenuPost.id, { focusNavigationBlock: true, } ); showNavigationMenuStatusNotice( __( `Navigation Menu successfully created.` ) ); } if ( createNavigationMenuIsError ) { showNavigationMenuStatusNotice( __( 'Failed to create Navigation Menu.' ) ); } }, [ createNavigationMenuStatus, createNavigationMenuError, createNavigationMenuPost, ] ); const { hasUncontrolledInnerBlocks, uncontrolledInnerBlocks, isInnerBlockSelected, innerBlocks, } = useInnerBlocks( clientId ); const hasSubmenus = !! innerBlocks.find( ( block ) => block.name === 'core/navigation-submenu' ); const { replaceInnerBlocks, selectBlock, __unstableMarkNextChangeAsNotPersistent, } = useDispatch( blockEditorStore ); const [ hasSavedUnsavedInnerBlocks, setHasSavedUnsavedInnerBlocks ] = useState( false ); const [ isResponsiveMenuOpen, setResponsiveMenuVisibility ] = useState( false ); const [ overlayMenuPreview, setOverlayMenuPreview ] = useState( false ); const { hasResolvedNavigationMenus, isNavigationMenuResolved, isNavigationMenuMissing, navigationMenus, navigationMenu, canUserUpdateNavigationMenu, hasResolvedCanUserUpdateNavigationMenu, canUserDeleteNavigationMenu, hasResolvedCanUserDeleteNavigationMenu, canUserCreateNavigationMenu, isResolvingCanUserCreateNavigationMenu, hasResolvedCanUserCreateNavigationMenu, } = useNavigationMenu( ref ); const navMenuResolvedButMissing = hasResolvedNavigationMenus && isNavigationMenuMissing; // Attempt to retrieve and prioritize any existing navigation menu unless: // - the are uncontrolled inner blocks already present in the block. // - the user is creating a new menu. // - there are no menus to choose from. // This attempts to pick the first menu if there is a single Navigation Post. If more // than 1 exists then use the most recent. // The aim is for the block to "just work" from a user perspective using existing data. useEffect( () => { if ( hasUncontrolledInnerBlocks || isCreatingNavigationMenu || ref || ! navigationMenus?.length ) { return; } navigationMenus.sort( ( menuA, menuB ) => { const menuADate = new Date( menuA.date ); const menuBDate = new Date( menuB.date ); return menuADate.getTime() < menuBDate.getTime(); } ); // Only autofallback to published menus. const fallbackNavigationMenus = navigationMenus.filter( ( menu ) => menu.status === 'publish' ); if ( fallbackNavigationMenus.length === 0 ) return; /** * This fallback displays (both in editor and on front) * a list of pages only if no menu (user assigned or * automatically picked) is available. * The fallback should not request a save (entity dirty state) * nor to be undoable, hence why it is marked as non persistent */ __unstableMarkNextChangeAsNotPersistent(); setRef( fallbackNavigationMenus[ 0 ].id ); }, [ navigationMenus ] ); const navRef = useRef(); const { convert: convertClassicMenu, status: classicMenuConversionStatus, error: classicMenuConversionError, } = useConvertClassicToBlockMenu( clientId ); const isConvertingClassicMenu = classicMenuConversionStatus === CLASSIC_MENU_CONVERSION_PENDING; // The standard HTML5 tag for the block wrapper. const TagName = 'nav'; // "placeholder" shown if: // - there is no ref attribute pointing to a Navigation Post. // - there is no classic menu conversion process in progress. // - there is no menu creation process in progress. // - there are no uncontrolled blocks. const isPlaceholder = ! ref && ! isCreatingNavigationMenu && ! isConvertingClassicMenu && hasResolvedNavigationMenus && ! hasUncontrolledInnerBlocks; if ( isPlaceholder && ! ref ) { /** * this fallback only displays (both in editor and on front) * the list of pages block if no menu is available as a fallback. * We don't want the fallback to request a save, * nor to be undoable, hence we mark it non persistent. */ __unstableMarkNextChangeAsNotPersistent(); replaceInnerBlocks( clientId, [ createBlock( 'core/page-list' ) ] ); } const isEntityAvailable = ! isNavigationMenuMissing && isNavigationMenuResolved; // "loading" state: // - there is a menu creation process in progress. // - there is a classic menu conversion process in progress. // OR: // - there is a ref attribute pointing to a Navigation Post // - the Navigation Post isn't available (hasn't resolved) yet. const isLoading = ! hasResolvedNavigationMenus || isCreatingNavigationMenu || isConvertingClassicMenu || !! ( ref && ! isEntityAvailable && ! isConvertingClassicMenu ); const textDecoration = attributes.style?.typography?.textDecoration; const hasBlockOverlay = useBlockOverlayActive( clientId ); const blockProps = useBlockProps( { ref: navRef, className: classnames( className, { 'items-justified-right': justifyContent === 'right', 'items-justified-space-between': justifyContent === 'space-between', 'items-justified-left': justifyContent === 'left', 'items-justified-center': justifyContent === 'center', 'is-vertical': orientation === 'vertical', 'no-wrap': flexWrap === 'nowrap', 'is-responsive': 'never' !== overlayMenu, '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, [ `has-text-decoration-${ textDecoration }` ]: textDecoration, 'block-editor-block-content-overlay': hasBlockOverlay, } ), style: { color: ! textColor?.slug && textColor?.color, backgroundColor: ! backgroundColor?.slug && backgroundColor?.color, }, } ); // 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(); const handleUpdateMenu = ( menuId, options = { focusNavigationBlock: false } ) => { const { focusNavigationBlock } = options; setRef( menuId ); if ( focusNavigationBlock ) { selectBlock( clientId ); } }; useEffect( () => { hideClassicMenuConversionNotice(); if ( classicMenuConversionStatus === CLASSIC_MENU_CONVERSION_PENDING ) { speak( __( 'Classic menu importing.' ) ); } if ( classicMenuConversionStatus === CLASSIC_MENU_CONVERSION_SUCCESS ) { showClassicMenuConversionNotice( __( 'Classic menu imported successfully.' ) ); } if ( classicMenuConversionStatus === CLASSIC_MENU_CONVERSION_ERROR ) { showClassicMenuConversionNotice( __( 'Classic menu import failed.' ) ); } }, [ classicMenuConversionStatus, classicMenuConversionError ] ); // Spacer block needs orientation from context. This is a patch until // https://github.com/WordPress/gutenberg/issues/36197 is addressed. useEffect( () => { if ( orientation ) { __unstableMarkNextChangeAsNotPersistent(); setAttributes( { orientation } ); } }, [ orientation ] ); 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 ); } } ); useEffect( () => { if ( ! isSelected && ! isInnerBlockSelected ) { hideNavigationMenuPermissionsNotice(); } if ( isSelected || isInnerBlockSelected ) { if ( ref && ! navMenuResolvedButMissing && hasResolvedCanUserUpdateNavigationMenu && ! canUserUpdateNavigationMenu ) { showNavigationMenuPermissionsNotice( __( 'You do not have permission to edit this Menu. Any changes made will not be saved.' ) ); } if ( ! ref && hasResolvedCanUserCreateNavigationMenu && ! canUserCreateNavigationMenu ) { showNavigationMenuPermissionsNotice( __( 'You do not have permission to create Navigation Menus.' ) ); } } }, [ isSelected, isInnerBlockSelected, canUserUpdateNavigationMenu, hasResolvedCanUserUpdateNavigationMenu, canUserCreateNavigationMenu, hasResolvedCanUserCreateNavigationMenu, ref, ] ); const hasManagePermissions = canUserCreateNavigationMenu || canUserUpdateNavigationMenu; const isResponsive = 'never' !== overlayMenu; const overlayMenuPreviewClasses = classnames( 'wp-block-navigation__overlay-menu-preview', { open: overlayMenuPreview } ); // Prompt the user to publish the menu they have set as a draft const isDraftNavigationMenu = navigationMenu?.status === 'draft'; useEffect( async () => { hideMenuAutoPublishDraftNotice(); if ( ! isDraftNavigationMenu ) return; try { await editEntityRecord( 'postType', 'wp_navigation', navigationMenu?.id, { status: 'publish', }, { throwOnError: true } ); } catch { showMenuAutoPublishDraftNotice( __( 'Error ocurred while publishing the navigation menu.' ) ); } }, [ isDraftNavigationMenu, navigationMenu ] ); const stylingInspectorControls = ( <InspectorControls> { hasSubmenuIndicatorSetting && ( <PanelBody title={ __( 'Display' ) }> { isResponsive && ( <> <Button className={ overlayMenuPreviewClasses } onClick={ () => { setOverlayMenuPreview( ! overlayMenuPreview ); } } > { hasIcon && ( <> <OverlayMenuIcon icon={ icon } /> <Icon icon={ close } /> </> ) } { ! hasIcon && ( <> <span>{ __( 'Menu' ) }</span> <span>{ __( 'Close' ) }</span> </> ) } </Button> { overlayMenuPreview && ( <OverlayMenuPreview setAttributes={ setAttributes } hasIcon={ hasIcon } icon={ icon } /> ) } </> ) } <h3>{ __( 'Overlay Menu' ) }</h3> <ToggleGroupControl label={ __( 'Configure overlay menu' ) } value={ overlayMenu } help={ __( 'Collapses the navigation options in a menu icon opening an overlay.' ) } onChange={ ( value ) => setAttributes( { overlayMenu: value } ) } isBlock hideLabelFromVision > <ToggleGroupControlOption value="never" label={ __( 'Off' ) } /> <ToggleGroupControlOption value="mobile" label={ __( 'Mobile' ) } /> <ToggleGroupControlOption value="always" label={ __( 'Always' ) } /> </ToggleGroupControl> { hasSubmenus && ( <> <h3>{ __( 'Submenus' ) }</h3> <ToggleControl checked={ openSubmenusOnClick } onChange={ ( value ) => { setAttributes( { openSubmenusOnClick: value, ...( value && { showSubmenuIcon: true, } ), // Make sure arrows are shown when we toggle this on. } ); } } label={ __( 'Open on click' ) } /> <ToggleControl checked={ showSubmenuIcon } onChange={ ( value ) => { setAttributes( { showSubmenuIcon: value, } ); } } disabled={ attributes.openSubmenusOnClick } label={ __( 'Show arrow' ) } /> </> ) } </PanelBody> ) } { hasColorSettings && ( <PanelColorSettings __experimentalHasMultipleOrigins __experimentalIsRenderedInSidebar title={ __( 'Color' ) } initialOpen={ false } colorSettings={ [ { value: textColor.color, onChange: setTextColor, label: __( 'Text' ), }, { value: backgroundColor.color, onChange: setBackgroundColor, label: __( 'Background' ), }, { value: overlayTextColor.color, onChange: setOverlayTextColor, label: __( 'Submenu & overlay text' ), }, { value: overlayBackgroundColor.color, onChange: setOverlayBackgroundColor, label: __( 'Submenu & overlay background' ), }, ] } > { enableContrastChecking && ( <> <ContrastChecker backgroundColor={ detectedBackgroundColor } textColor={ detectedColor } /> <ContrastChecker backgroundColor={ detectedOverlayBackgroundColor } textColor={ detectedOverlayColor } /> </> ) } </PanelColorSettings> ) } </InspectorControls> ); // If the block has inner blocks, but no menu id, then these blocks are either: // - inserted via a pattern. // - inserted directly via Code View (or otherwise). // - from an older version of navigation block added before the block used a wp_navigation entity. // Consider this state as 'unsaved' and offer an uncontrolled version of inner blocks, // that automatically saves the menu as an entity when changes are made to the inner blocks. const hasUnsavedBlocks = hasUncontrolledInnerBlocks && ! isEntityAvailable; if ( hasUnsavedBlocks ) { return ( <TagName { ...blockProps }> <InspectorControls> <PanelBody title={ __( 'Menu' ) }> <NavigationMenuSelector currentMenuId={ ref } clientId={ clientId } onSelectNavigationMenu={ ( menuId ) => { handleUpdateMenu( menuId ); } } onSelectClassicMenu={ async ( classicMenu ) => { const navMenu = await convertClassicMenu( classicMenu.id, classicMenu.name ); if ( navMenu ) { handleUpdateMenu( navMenu.id, { focusNavigationBlock: true, } ); } } } onCreateNew={ createUntitledEmptyNavigationMenu } createNavigationMenuIsSuccess={ createNavigationMenuIsSuccess } /* translators: %s: The name of a menu. */ actionLabel={ __( "Switch to '%s'" ) } /> <Button variant="link" disabled={ ! hasManagePermissions || ! hasResolvedNavigationMenus } href={ addQueryArgs( 'edit.php', { post_type: 'wp_navigation', } ) } > { __( 'Manage menus' ) } </Button> </PanelBody> </InspectorControls> { stylingInspectorControls } <ResponsiveWrapper id={ clientId } onToggle={ setResponsiveMenuVisibility } isOpen={ isResponsiveMenuOpen } hasIcon={ hasIcon } icon={ icon } isResponsive={ 'never' !== overlayMenu } isHiddenByDefault={ 'always' === overlayMenu } overlayBackgroundColor={ overlayBackgroundColor } overlayTextColor={ overlayTextColor } > <UnsavedInnerBlocks blocks={ uncontrolledInnerBlocks } clientId={ clientId } navigationMenus={ navigationMenus } hasSelection={ isSelected || isInnerBlockSelected } hasSavedUnsavedInnerBlocks={ hasSavedUnsavedInnerBlocks } onSave={ ( post ) => { // Set some state used as a guard to prevent the creation of multiple posts. setHasSavedUnsavedInnerBlocks( true ); // Switch to using the wp_navigation entity. setRef( post.id ); showNavigationMenuStatusNotice( __( `New Navigation Menu created.` ) ); } } /> </ResponsiveWrapper> </TagName> ); } // Show a warning if the selected menu is no longer available. // TODO - the user should be able to select a new one? if ( ref && isNavigationMenuMissing ) { return ( <TagName { ...blockProps }> <InspectorControls> <PanelBody title={ __( 'Menu' ) }> <NavigationMenuSelector currentMenuId={ null } clientId={ clientId } onSelectNavigationMenu={ ( menuId ) => { handleUpdateMenu( menuId ); } } onSelectClassicMenu={ async ( classicMenu ) => { const navMenu = await convertClassicMenu( classicMenu.id, classicMenu.name ); if ( navMenu ) { handleUpdateMenu( navMenu.id, { focusNavigationBlock: true, } ); } } } onCreateNew={ createUntitledEmptyNavigationMenu } createNavigationMenuIsSuccess={ createNavigationMenuIsSuccess } /* translators: %s: The name of a menu. */ actionLabel={ __( "Switch to '%s'" ) } /> <Button variant="link" disabled={ ! hasManagePermissions || ! hasResolvedNavigationMenus } href={ addQueryArgs( 'edit.php', { post_type: 'wp_navigation', } ) } > { __( 'Manage menus' ) } </Button> </PanelBody> </InspectorControls> <Warning> { __( 'Navigation menu has been deleted or is unavailable. ' ) } <Button onClick={ createUntitledEmptyNavigationMenu } variant="link" > { __( 'Create a new menu?' ) } </Button> </Warning> </TagName> ); } if ( isEntityAvailable && hasAlreadyRendered ) { return ( <div { ...blockProps }> <Warning> { __( 'Block cannot be rendered inside itself.' ) } </Warning> </div> ); } const PlaceholderComponent = CustomPlaceholder ? CustomPlaceholder : Placeholder; /** * Historically the navigation block has supported custom placeholders. * Even though the current UX tries as hard as possible not to * end up in a placeholder state, the block continues to support * this extensibility point, via a CustomPlaceholder. * When CustomPlaceholder is present it becomes the default fallback * for an empty navigation block, instead of the default fallbacks. * */ if ( isPlaceholder && CustomPlaceholder ) { return ( <TagName { ...blockProps }> <PlaceholderComponent isSelected={ isSelected } currentMenuId={ ref } clientId={ clientId } canUserCreateNavigationMenu={ canUserCreateNavigationMenu } isResolvingCanUserCreateNavigationMenu={ isResolvingCanUserCreateNavigationMenu } onSelectNavigationMenu={ ( menuId ) => { handleUpdateMenu( menuId ); } } onSelectClassicMenu={ async ( classicMenu ) => { const navMenu = await convertClassicMenu( classicMenu.id, classicMenu.name ); if ( navMenu ) { handleUpdateMenu( navMenu.id, { focusNavigationBlock: true, } ); } } } onCreateEmpty={ createUntitledEmptyNavigationMenu } /> </TagName> ); } return ( <EntityProvider kind="postType" type="wp_navigation" id={ ref }> <RecursionProvider uniqueId={ recursionId }> <InspectorControls> <PanelBody title={ __( 'Menu' ) }> <NavigationMenuSelector currentMenuId={ ref } clientId={ clientId } onSelectNavigationMenu={ ( menuId ) => { handleUpdateMenu( menuId ); } } onSelectClassicMenu={ async ( classicMenu ) => { const navMenu = await convertClassicMenu( classicMenu.id, classicMenu.name ); if ( navMenu ) { handleUpdateMenu( navMenu.id, { focusNavigationBlock: true, } ); } } } onCreateNew={ createUntitledEmptyNavigationMenu } createNavigationMenuIsSuccess={ createNavigationMenuIsSuccess } createNavigationMenuIsError={ createNavigationMenuIsError } /* translators: %s: The name of a menu. */ actionLabel={ __( "Switch to '%s'" ) } /> <Button variant="link" disabled={ ! hasManagePermissions || ! hasResolvedNavigationMenus } href={ addQueryArgs( 'edit.php', { post_type: 'wp_navigation', } ) } > { __( 'Manage menus' ) } </Button> </PanelBody> </InspectorControls> { stylingInspectorControls } { isEntityAvailable && ( <InspectorControls __experimentalGroup="advanced"> { hasResolvedCanUserUpdateNavigationMenu && canUserUpdateNavigationMenu && ( <NavigationMenuNameControl /> ) } { hasResolvedCanUserDeleteNavigationMenu && canUserDeleteNavigationMenu && ( <NavigationMenuDeleteControl onDelete={ ( deletedMenuTitle = '' ) => { replaceInnerBlocks( clientId, [] ); showNavigationMenuStatusNotice( sprintf( // translators: %s: the name of a menu (e.g. Header navigation). __( 'Navigation menu %s successfully deleted.' ), deletedMenuTitle ) ); } } /> ) } </InspectorControls> ) } { isLoading && ( <TagName { ...blockProps }> <Spinner className="wp-block-navigation__loading-indicator" /> </TagName> ) } { ! isLoading && ( <TagName { ...blockProps }> <ResponsiveWrapper id={ clientId } onToggle={ setResponsiveMenuVisibility } label={ __( 'Menu' ) } hasIcon={ hasIcon } icon={ icon } isOpen={ isResponsiveMenuOpen } isResponsive={ isResponsive } isHiddenByDefault={ 'always' === overlayMenu } overlayBackgroundColor={ overlayBackgroundColor } overlayTextColor={ overlayTextColor } > { isEntityAvailable && ( <NavigationInnerBlocks clientId={ clientId } hasCustomPlaceholder={ !! CustomPlaceholder } orientation={ orientation } /> ) } </ResponsiveWrapper> </TagName> ) } </RecursionProvider> </EntityProvider> ); } export default withColors( { textColor: 'color' }, { backgroundColor: 'color' }, { overlayBackgroundColor: 'color' }, { overlayTextColor: 'color' } )( Navigation );