-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Fix allow mouse users to edit link text when Link UI is active #59635
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
65b37bc
f084f50
509a8ea
d4e6861
f1b33ee
fa591fb
d6eee85
d0c5015
3f63012
ceadb8f
e6c1f84
60bb62c
7dfee4f
2ea36cf
c190469
1242c56
31e309f
f923286
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,7 +2,7 @@ | |
| * WordPress dependencies | ||
| */ | ||
| import { __ } from '@wordpress/i18n'; | ||
| import { useState, useLayoutEffect } from '@wordpress/element'; | ||
| import { useState, useLayoutEffect, useEffect } from '@wordpress/element'; | ||
| import { | ||
| getTextContent, | ||
| applyFormat, | ||
|
|
@@ -38,10 +38,39 @@ function Edit( { | |
| onFocus, | ||
| contentRef, | ||
| } ) { | ||
| const [ addingLink, setAddingLink ] = useState( false ); | ||
| const [ editingLink, setEditingLink ] = useState( false ); | ||
| const [ creatingLink, setCreatingLink ] = useState( false ); | ||
|
|
||
| // We only need to store the button element that opened the popover. We can ignore the other states, as they will be handled by the onFocus prop to return to the rich text field. | ||
| const [ openedBy, setOpenedBy ] = useState( null ); | ||
|
|
||
| // Manages whether the Link UI popover should autofocus when shown. | ||
| const [ shouldAutoFocus, setShouldAutoFocus ] = useState( true ); | ||
|
|
||
| function setIsEditingLink( isEditing, { autoFocus = true } = {} ) { | ||
| setEditingLink( isEditing ); | ||
| setShouldAutoFocus( autoFocus ); | ||
| } | ||
|
|
||
| function setIsCreatingLink( isCreating ) { | ||
| // Don't add a new link if there is already an active link. | ||
|
getdave marked this conversation as resolved.
|
||
| // The two states are mutually exclusive. | ||
| if ( isCreating === true && isActive ) { | ||
| return; | ||
| } | ||
| setCreatingLink( isCreating ); | ||
| } | ||
|
|
||
| useEffect( () => { | ||
| // When the link becomes inactive (i.e. isActive is false), reset the editingLink state | ||
| // and the creatingLink state. This means that if the Link UI is displayed and the link | ||
| // becomes inactive (e.g. used arrow keys to move cursor outside of link bounds), the UI will close. | ||
| if ( ! isActive ) { | ||
| setEditingLink( false ); | ||
| setCreatingLink( false ); | ||
| } | ||
| }, [ isActive ] ); | ||
|
|
||
| useLayoutEffect( () => { | ||
| const editableContentElement = contentRef.current; | ||
| if ( ! editableContentElement ) { | ||
|
|
@@ -52,14 +81,18 @@ function Edit( { | |
| // There is a situation whereby there is an existing link in the rich text | ||
| // and the user clicks on the leftmost edge of that link and fails to activate | ||
| // the link format, but the click event still fires on the `<a>` element. | ||
| // This causes the `addingLink` state to be set to `true` and the link UI | ||
| // This causes the `editingLink` state to be set to `true` and the link UI | ||
| // to be rendered in "creating" mode. We need to check isActive to see if | ||
| // we have an active link format. | ||
| if ( event.target.tagName !== 'A' || ! isActive ) { | ||
| if ( | ||
| ! event.target.closest( '[contenteditable] a' ) || // other formats (e.g. bold) may be nested within the link. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is an enhancement compared to
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ...yes in terms of it fixes a regression introduced during the 6.5 cycle whereby links that had other formats inside them would not activate on click 👍 |
||
| ! isActive | ||
| ) { | ||
| setIsEditingLink( false ); | ||
| return; | ||
| } | ||
|
|
||
| setAddingLink( true ); | ||
| setIsEditingLink( true, { autoFocus: false } ); | ||
| } | ||
|
|
||
| editableContentElement.addEventListener( 'click', handleClick ); | ||
|
|
@@ -70,6 +103,7 @@ function Edit( { | |
| }, [ contentRef, isActive ] ); | ||
|
|
||
| function addLink( target ) { | ||
| setShouldAutoFocus( true ); | ||
| const text = getTextContent( slice( value ) ); | ||
|
|
||
| if ( ! isActive && text && isURL( text ) && isValidHref( text ) ) { | ||
|
|
@@ -90,7 +124,11 @@ function Edit( { | |
| if ( target ) { | ||
| setOpenedBy( target ); | ||
| } | ||
| setAddingLink( true ); | ||
| if ( ! isActive ) { | ||
| setIsCreatingLink( true ); | ||
| } else { | ||
| setIsEditingLink( true ); | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -109,7 +147,9 @@ function Edit( { | |
| // Otherwise, we rely on the passed in onFocus to return focus to the rich text field. | ||
|
|
||
| // Close the popover | ||
| setAddingLink( false ); | ||
| setIsEditingLink( false ); | ||
| setIsCreatingLink( false ); | ||
|
|
||
| // Return focus to the toolbar button or the rich text field | ||
| if ( openedBy?.tagName === 'BUTTON' ) { | ||
| openedBy.focus(); | ||
|
|
@@ -127,7 +167,8 @@ function Edit( { | |
| // 4. Press Escape | ||
| // 5. Focus should be on the Options button | ||
| function onFocusOutside() { | ||
| setAddingLink( false ); | ||
| setIsEditingLink( false ); | ||
| setIsCreatingLink( false ); | ||
| setOpenedBy( null ); | ||
| } | ||
|
|
||
|
|
@@ -136,6 +177,8 @@ function Edit( { | |
| speak( __( 'Link removed.' ), 'assertive' ); | ||
| } | ||
|
|
||
| const isEditingActiveLink = editingLink && isActive; | ||
|
|
||
| return ( | ||
| <> | ||
| <RichTextShortcut type="primary" character="k" onUse={ addLink } /> | ||
|
|
@@ -151,13 +194,13 @@ function Edit( { | |
| onClick={ ( event ) => { | ||
| addLink( event.currentTarget ); | ||
| } } | ||
| isActive={ isActive || addingLink } | ||
| isActive={ isActive || editingLink } | ||
| shortcutType="primary" | ||
| shortcutCharacter="k" | ||
| aria-haspopup="true" | ||
| aria-expanded={ addingLink } | ||
| aria-expanded={ editingLink } | ||
| /> | ||
| { addingLink && ( | ||
| { ( isEditingActiveLink || creatingLink ) && ( | ||
| <InlineLinkUI | ||
| stopAddingLink={ stopAddingLink } | ||
| onFocusOutside={ onFocusOutside } | ||
|
|
@@ -166,6 +209,7 @@ function Edit( { | |
| value={ value } | ||
| onChange={ onChange } | ||
| contentRef={ contentRef } | ||
| focusOnMount={ shouldAutoFocus ? 'firstElement' : false } | ||
| /> | ||
| ) } | ||
| </> | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the concept we've been missing.