Skip to content

Commit

Permalink
NavigationMenu: <LinkControl /> integration. (#18062)
Browse files Browse the repository at this point in the history
* link-control: doc

* navigation-menu-item: replace URLPopover by LinkControl

* navigation-menu: do not set link when onClose

* navigation-menu-item: ensuring hide popover once it closes

* navigation-menu-item: anchor LinkControl at TextControl

* navigation-menu-item: render external link

* navigation-menu-item: remove link object from attrs

* navigation-menu-item: handing close link popover

* navigation-menu: apply colors correctly in edition mode

* navigation-menu: set text color to external link

* navigation-menu-item: apply text color to external link

* fixing eslint errors/warnings

* fix camelcase warning flawlessly
props to @getdave

* navigation-menu-item: set current link as null when it changes

* link-control: doc fixes

* Updates setting change handler to test for specific setting and value

Addresses #18062 (comment)

* Remove hyperlinks from nav item labels

Addresses #18062 (comment)

* Update attribute name

Addresses #18062 (comment)

* Fix save output to handle new tab attribute

Addresses #18062 (comment)

* Update to simplify link update with default args

Addresses #18062 (comment)

* Remove content accidentally re-added in merge of rebase

* navigation-menu-item: set title as label when it's empty

* navigation-menu-item: open LinkControl when new item

* navigation-menu-item: new item workflow
open link control and show fake placeholder when it's a new iotem

* navigation-menu: rename state hook

* navigation-menu-item: use camelCase for link ID

* navigation-menu-item: set edit mode style

* navigation-menu-item: remove fake placeholder stuff

* navigation-menu-item: no use effect to Initialize is open state

* navigation-menu-item: remove unneeded setting state

* navigation-menu-item: move link control callback outside of the component rendering

* navigation-menu-item: simplify closing linkcontrol
It adds a hacky solution to deal with both events that happen at the
same time: LinkControl.onClose and ToolbatButton.onClick, removing the
useState ussage in favor adding a setTimeout call.

* navigation-menu: fixing eslint errors

* Update packages/block-library/src/navigation-menu-item/edit.js

Co-Authored-By: Daniel Richards <[email protected]>

* Update packages/block-library/src/navigation-menu-item/edit.js

Co-Authored-By: Daniel Richards <[email protected]>

* Fixes bug where Link UI was visually divorced from target Menu Item

Addresses #18062 (comment)

* Updates to remove unwanted prop being passed to LinkControl

`fetchSearchSuggestions` seems to have been accidentally re-added. It’s not required.

Addresses #18062 (comment)

* navigation-menu: remove `destination` attr

* navigation-menu-item: cleanup timer once unmount
  • Loading branch information
retrofox authored and talldan committed Nov 6, 2019
1 parent f3fb987 commit b138818
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 82 deletions.
13 changes: 13 additions & 0 deletions packages/block-editor/src/components/link-control/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,19 @@ through of its function parameter.
- Type: `Function`
- Required: No
Use this callback to take an action after a user set or updated a link.
The function callback will receive the selected item, or Null.
```es6
<LinkControl
onLinkChange={ ( item ) => {
item
? console.log( `The item selected has the ${ item.id } id.` )
: console.warn( 'No Item selected.' );
}
/>
```
### onSettingChange
- Type: `Function`
Expand Down
6 changes: 6 additions & 0 deletions packages/block-library/src/navigation-menu-item/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,15 @@
"title": {
"type": "string"
},
"type": {
"type": "string"
},
"description": {
"type": "string"
},
"linkId": {
"type": "number"
},
"opensInNewTab": {
"type": "boolean",
"default": false
Expand Down
173 changes: 101 additions & 72 deletions packages/block-library/src/navigation-menu-item/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,41 @@ import {
BlockControls,
InnerBlocks,
InspectorControls,
URLPopover,
RichText,
__experimentalLinkControl as LinkControl,
} from '@wordpress/block-editor';
import {
Fragment,
useRef,
useState,
} from '@wordpress/element';
import { Fragment, useState, useEffect } from '@wordpress/element';

/**
* It updates the link attribute when the
* link settings changes.
*
* @param {Function} setter Setter attribute function.
*/
const updateLinkSetting = ( setter ) => ( setting, value ) => {
if ( setting === 'new-tab' ) {
setter( { opensInNewTab: value } );
}
};

/**
* Updates the link attribute when it changes
* through of the `onLinkChange` LinkControl callback.
*
* @param {Function} setter Setter attribute function.
* @param {string} label ItemMenu link label.
*/
const updateLink = ( setter, label ) => ( { title: newTitle = '', url: newURL = '' } = {} ) => {
setter( {
title: newTitle,
url: newURL,
} );

// Set the item label as well if it isn't already defined.
if ( ! label ) {
setter( { label: newTitle } );
}
};

function NavigationMenuItemEdit( {
attributes,
Expand All @@ -50,40 +77,48 @@ function NavigationMenuItemEdit( {
setAttributes,
insertMenuItemBlock,
} ) {
const [ isLinkOpen, setIsLinkOpen ] = useState( false );
const [ isEditingLink, setIsEditingLink ] = useState( false );
const [ urlInput, setUrlInput ] = useState( null );
const { label, opensInNewTab, title, url } = attributes;
const link = title ? { title, url } : null;
const [ isLinkOpen, setIsLinkOpen ] = useState( ! label && isSelected );

const inputValue = urlInput !== null ? urlInput : url;
let onCloseTimerId = null;

const onKeyDown = ( event ) => {
if ( [ LEFT, DOWN, RIGHT, UP, BACKSPACE, ENTER ].indexOf( event.keyCode ) > -1 ) {
// Stop the key event from propagating up to ObserveTyping.startTypingInTextField.
event.stopPropagation();
/**
* It's a kind of hack to handle closing the LinkControl popover
* clicking on the ToolbarButton link.
*/
useEffect( () => {
if ( ! isSelected ) {
setIsLinkOpen( false );
}
};

const closeURLPopover = () => {
setIsEditingLink( false );
setUrlInput( null );
setIsLinkOpen( false );
};
return () => {
// Clear LinkControl.OnClose timeout.
if ( onCloseTimerId ) {
clearTimeout( onCloseTimerId );
}
};
}, [ isSelected ] );

const autocompleteRef = useRef( null );
/**
* `onKeyDown` LinkControl handler.
* It takes over to stop the event propagation to make the
* navigation work, avoiding undesired behaviors.
* For instance, it will block to move between menu items
* when the LinkControl is focused.
*
* @param {Event} event
*/
const handleLinkControlOnKeyDown = ( event ) => {
const { keyCode } = event;

const onFocusOutside = ( event ) => {
const autocompleteElement = autocompleteRef.current;
if ( autocompleteElement && autocompleteElement.contains( event.target ) ) {
return;
if ( [ LEFT, DOWN, RIGHT, UP, BACKSPACE, ENTER ].indexOf( keyCode ) > -1 ) {
// Stop the key event from propagating up to ObserveTyping.startTypingInTextField.
event.stopPropagation();
}
closeURLPopover();
};

const stopPropagation = ( event ) => {
event.stopPropagation();
};

const { label, url } = attributes;
const itemLabelPlaceholder = __( 'Add item…' );

return (
<Fragment>
Expand All @@ -93,51 +128,29 @@ function NavigationMenuItemEdit( {
name="link"
icon="admin-links"
title={ __( 'Link' ) }
onClick={ () => setIsLinkOpen( ! isLinkOpen ) }
onClick={ () => {
if ( isLinkOpen ) {
return;
}
setIsLinkOpen( ! isLinkOpen );
} }
/>
{ <ToolbarButton
<ToolbarButton
name="submenu"
icon={ <SVG xmlns="http://www.w3.org/2000/svg" width="24" height="24"><Path d="M14 5h8v2h-8zm0 5.5h8v2h-8zm0 5.5h8v2h-8zM2 11.5C2 15.08 4.92 18 8.5 18H9v2l3-3-3-3v2h-.5C6.02 16 4 13.98 4 11.5S6.02 7 8.5 7H12V5H8.5C4.92 5 2 7.92 2 11.5z" /><Path fill="none" d="M0 0h24v24H0z" /></SVG> }
title={ __( 'Add submenu item' ) }
onClick={ insertMenuItemBlock }
/> }
/>
</Toolbar>
{ isLinkOpen &&
<>
<URLPopover
className="wp-block-navigation-menu-item__inline-link-input"
onClose={ closeURLPopover }
onFocusOutside={ onFocusOutside }
>
{ ( ! url || isEditingLink ) &&
<URLPopover.LinkEditor
value={ inputValue }
onChangeInputValue={ setUrlInput }
onKeyPress={ stopPropagation }
onKeyDown={ onKeyDown }
onSubmit={ ( event ) => event.preventDefault() }
autocompleteRef={ autocompleteRef }
/>
}
{ ( url && ! isEditingLink ) &&
<URLPopover.LinkViewer
onKeyPress={ stopPropagation }
url={ url }
/>
}

</URLPopover>
</>
}
</BlockControls>
<InspectorControls>
<PanelBody
title={ __( 'Menu Settings' ) }
>
<ToggleControl
checked={ attributes.opensInNewTab }
onChange={ ( opensInNewTab ) => {
setAttributes( { opensInNewTab } );
onChange={ ( newTab ) => {
setAttributes( { opensInNewTab: newTab } );
} }
label={ __( 'Open in new tab' ) }
/>
Expand All @@ -154,8 +167,8 @@ function NavigationMenuItemEdit( {
>
<TextControl
value={ attributes.title || '' }
onChange={ ( title ) => {
setAttributes( { title } );
onChange={ ( itemTitle ) => {
setAttributes( { title: itemTitle } );
} }
label={ __( 'Title Attribute' ) }
help={ __( 'Provide more context about where the link goes.' ) }
Expand Down Expand Up @@ -186,13 +199,29 @@ function NavigationMenuItemEdit( {
'is-selected': isSelected,
} ) }
>
<RichText
className="wp-block-navigation-menu-item__content"
value={ label }
onChange={ ( labelValue ) => setAttributes( { label: labelValue } ) }
placeholder={ __( 'Add item…' ) }
withoutInteractiveFormatting
/>
<div className="wp-block-navigation-menu-item__inner">
<RichText
className="wp-block-navigation-menu-item__content"
value={ label }
onChange={ ( labelValue ) => setAttributes( { label: labelValue } ) }
placeholder={ itemLabelPlaceholder }
withoutInteractiveFormatting
/>
{ isLinkOpen && (
<LinkControl
className="wp-block-navigation-menu-item__inline-link-input"
onKeyDown={ handleLinkControlOnKeyDown }
onKeyPress={ ( event ) => event.stopPropagation() }
currentLink={ link }
onLinkChange={ updateLink( setAttributes, label ) }
onClose={ () => {
onCloseTimerId = setTimeout( () => setIsLinkOpen( false ), 100 );
} }
currentSettings={ { 'new-tab': opensInNewTab } }
onSettingsChange={ updateLinkSetting( setAttributes ) }
/>
) }
</div>
<InnerBlocks
allowedBlocks={ [ 'core/navigation-menu-item' ] }
renderAppender={ hasDescendants ? InnerBlocks.ButtonBlockAppender : false }
Expand Down
33 changes: 28 additions & 5 deletions packages/block-library/src/navigation-menu-item/editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,36 @@
}

.wp-block-navigation-menu-item {
font-family: inherit;
font-size: inherit;
background-color: var(--background-color-menu-link);
color: var(--color-menu-link);
&:not(.is-editing) {
font-family: inherit;
font-size: inherit;
background-color: var(--background-color-menu-link);
color: var(--color-menu-link);
width: inherit;

.components-external-link {
color: var(--color-menu-link);
}
}

&:focus {
&.is-editing .components-text-control__input {
width: inherit;
background-color: var(--background-color-menu-link);
color: var(--color-menu-link);

.components-external-link {
color: var(--color-menu-link);
&:focus {
color: var(--color-menu-link);
}
}
}

&.is-editing,
&.is-selected {
box-shadow: 0 0 1px $light-gray-900 inset;
border-radius: 4px;
min-width: 30px;
}
}

Expand Down
16 changes: 11 additions & 5 deletions packages/block-library/src/navigation-menu/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,17 @@ function NavigationMenu( {
if ( ! pages ) {
return null;
}
return pages.map( ( page ) => {
return [ 'core/navigation-menu-item',
{ label: page.title.rendered, url: page.permalink_template },
];
} );

return pages.map( ( { title, type, link: url, id: linkId } ) => (
[ 'core/navigation-menu-item', {
label: title.rendered,
title: title.raw,
type,
linkId,
url,
opensInNewTab: false,
} ]
) );
},
[ pages ]
);
Expand Down
9 changes: 9 additions & 0 deletions packages/block-library/src/navigation-menu/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,17 +102,26 @@ function build_navigation_menu_html( $block, $colors ) {
class="wp-block-navigation-menu-item__link ' . $colors['text_css_classes'] . '"
' . $colors['text_inline_styles'];

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

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

// Start anchor tag content.
$html .= '>';
if ( isset( $menu_item['attrs']['label'] ) ) {
$html .= $menu_item['attrs']['label'];
}
$html .= '</a>';
// End anchor tag content.

if ( count( (array) $menu_item['innerBlocks'] ) > 0 ) {
$html .= build_navigation_menu_html( $menu_item, $colors );
Expand Down

0 comments on commit b138818

Please sign in to comment.