-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Format Library: Assign Popover anchorRect to update selection position #14938
Changes from all commits
a309269
ad35be2
425900e
e3f0e9d
3b85cc9
04b8b1d
c42067e
28c44d2
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 |
---|---|---|
|
@@ -69,7 +69,7 @@ class Popover extends Component { | |
} | ||
|
||
componentDidMount() { | ||
this.toggleAutoRefresh( true ); | ||
this.toggleAutoRefresh( ! this.props.hasOwnProperty( 'anchorRect' ) ); | ||
this.refresh(); | ||
|
||
/* | ||
|
@@ -87,6 +87,15 @@ class Popover extends Component { | |
if ( prevProps.position !== this.props.position ) { | ||
this.computePopoverPosition( this.state.popoverSize, this.anchorRect ); | ||
} | ||
|
||
if ( prevProps.anchorRect !== this.props.anchorRect ) { | ||
this.refreshOnAnchorMove(); | ||
} | ||
|
||
const hasAnchorRect = this.props.hasOwnProperty( 'anchorRect' ); | ||
if ( hasAnchorRect !== prevProps.hasOwnProperty( 'anchorRect' ) ) { | ||
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. Why use 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.
Because we expect the value could change over time, but scheduling the interval doesn't depend on the value, it just depends on whether a value exists. i.e. if the alternative is |
||
this.toggleAutoRefresh( ! hasAnchorRect ); | ||
} | ||
} | ||
|
||
componentWillUnmount() { | ||
|
@@ -129,8 +138,7 @@ class Popover extends Component { | |
* will only refresh the popover position if the anchor moves. | ||
*/ | ||
refreshOnAnchorMove() { | ||
const { getAnchorRect = this.getAnchorRect } = this.props; | ||
const anchorRect = getAnchorRect( this.anchorNode.current ); | ||
const anchorRect = this.getAnchorRect( this.anchorNode.current ); | ||
const didAnchorRectChange = ! isShallowEqual( anchorRect, this.anchorRect ); | ||
if ( didAnchorRectChange ) { | ||
this.anchorRect = anchorRect; | ||
|
@@ -144,8 +152,7 @@ class Popover extends Component { | |
* position. | ||
*/ | ||
refresh() { | ||
const { getAnchorRect = this.getAnchorRect } = this.props; | ||
const anchorRect = getAnchorRect( this.anchorNode.current ); | ||
const anchorRect = this.getAnchorRect( this.anchorNode.current ); | ||
const contentRect = this.contentNode.current.getBoundingClientRect(); | ||
const popoverSize = { | ||
width: contentRect.width, | ||
|
@@ -191,6 +198,16 @@ class Popover extends Component { | |
} | ||
|
||
getAnchorRect( anchor ) { | ||
const { getAnchorRect, anchorRect } = this.props; | ||
|
||
if ( anchorRect ) { | ||
return anchorRect; | ||
} | ||
|
||
if ( getAnchorRect ) { | ||
return getAnchorRect( anchor ); | ||
} | ||
|
||
if ( ! anchor || ! anchor.parentNode ) { | ||
return; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,15 +7,15 @@ import classnames from 'classnames'; | |
* WordPress dependencies | ||
*/ | ||
import { __ } from '@wordpress/i18n'; | ||
import { Component, createRef } from '@wordpress/element'; | ||
import { Component, createRef, useMemo } from '@wordpress/element'; | ||
import { | ||
ExternalLink, | ||
IconButton, | ||
ToggleControl, | ||
withSpokenMessages, | ||
PositionedAtSelection, | ||
} from '@wordpress/components'; | ||
import { LEFT, RIGHT, UP, DOWN, BACKSPACE, ENTER } from '@wordpress/keycodes'; | ||
import { getRectangleFromRange } from '@wordpress/dom'; | ||
import { prependHTTP, safeDecodeURI, filterURLForDisplay } from '@wordpress/url'; | ||
import { | ||
create, | ||
|
@@ -77,6 +77,39 @@ const LinkViewerUrl = ( { url } ) => { | |
); | ||
}; | ||
|
||
const URLPopoverAtLink = ( { isActive, addingLink, value, ...props } ) => { | ||
const anchorRect = useMemo( () => { | ||
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. Just wondering... Does this have a limit on cache size? Sounds like it should have a limit of exactly one. 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.
It's not entirely clear, but it seems like something they would have considered as being the default behavior, but I also recall some similar discussion with @gziolo recently where he was showing a separate project which, by its name, might imply a different behavior 🤷♂️ https://reactjs.org/docs/hooks-reference.html#usememo
https://github.com/alexreardon/use-memo-one#readme
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. Yeah, to me it sounded like it was not the intention of 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.
Oh, I was thinking the opposite by my interpretation; that yes, it would only store the one latest result in memory. I'll plan to double-check, though. 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. From what I can discern, it only maintains a single value. |
||
const range = window.getSelection().getRangeAt( 0 ); | ||
if ( ! range ) { | ||
return; | ||
} | ||
|
||
if ( addingLink ) { | ||
return getRectangleFromRange( range ); | ||
} | ||
|
||
let element = range.startContainer; | ||
|
||
// If the caret is right before the element, select the next element. | ||
element = element.nextElementSibling || element; | ||
|
||
while ( element.nodeType !== window.Node.ELEMENT_NODE ) { | ||
element = element.parentNode; | ||
} | ||
|
||
const closest = element.closest( 'a' ); | ||
if ( closest ) { | ||
return closest.getBoundingClientRect(); | ||
} | ||
}, [ isActive, addingLink, value.start, value.end ] ); | ||
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. I don't know that |
||
|
||
if ( ! anchorRect ) { | ||
return null; | ||
} | ||
|
||
return <URLPopover anchorRect={ anchorRect } { ...props } />; | ||
}; | ||
|
||
const LinkViewer = ( { url, editLink } ) => { | ||
return ( | ||
// Disable reason: KeyPress must be suppressed so the block doesn't hide the toolbar | ||
|
@@ -221,37 +254,36 @@ class InlineLinkUI extends Component { | |
const showInput = isShowingInput( this.props, this.state ); | ||
|
||
return ( | ||
<PositionedAtSelection | ||
ellatrix marked this conversation as resolved.
Show resolved
Hide resolved
|
||
key={ `${ value.start }${ value.end }` /* Used to force rerender on selection change */ } | ||
<URLPopoverAtLink | ||
value={ value } | ||
isActive={ isActive } | ||
addingLink={ addingLink } | ||
onClickOutside={ this.onClickOutside } | ||
onClose={ this.resetState } | ||
focusOnMount={ showInput ? 'firstElement' : false } | ||
renderSettings={ () => ( | ||
<ToggleControl | ||
label={ __( 'Open in New Tab' ) } | ||
checked={ opensInNewWindow } | ||
onChange={ this.setLinkTarget } | ||
/> | ||
) } | ||
> | ||
<URLPopover | ||
onClickOutside={ this.onClickOutside } | ||
onClose={ this.resetState } | ||
focusOnMount={ showInput ? 'firstElement' : false } | ||
renderSettings={ () => ( | ||
<ToggleControl | ||
label={ __( 'Open in New Tab' ) } | ||
checked={ opensInNewWindow } | ||
onChange={ this.setLinkTarget } | ||
/> | ||
) } | ||
> | ||
{ showInput ? ( | ||
<LinkEditor | ||
value={ inputValue } | ||
onChangeInputValue={ this.onChangeInputValue } | ||
onKeyDown={ this.onKeyDown } | ||
submitLink={ this.submitLink } | ||
autocompleteRef={ this.autocompleteRef } | ||
/> | ||
) : ( | ||
<LinkViewer | ||
url={ url } | ||
editLink={ this.editLink } | ||
/> | ||
) } | ||
</URLPopover> | ||
</PositionedAtSelection> | ||
{ showInput ? ( | ||
<LinkEditor | ||
value={ inputValue } | ||
onChangeInputValue={ this.onChangeInputValue } | ||
onKeyDown={ this.onKeyDown } | ||
submitLink={ this.submitLink } | ||
autocompleteRef={ this.autocompleteRef } | ||
/> | ||
) : ( | ||
<LinkViewer | ||
url={ url } | ||
editLink={ this.editLink } | ||
/> | ||
) } | ||
</URLPopoverAtLink> | ||
); | ||
} | ||
} | ||
|
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.
I think I reviewed it already earlier this week 😃