-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Lighter block DOM: put sibling inserter in popover (#19456)
* Lighter block DOM: put sibling inserter in popover * Optimise * Fix multi select e2e test * Prevent lingering inserter * Don't show inserter above selected block * Remove some dead CSS * Add tabbable inserter for multi selected blocks * Polish * Simplify * Clean up * Simplify * Fix nested block inserter
- Loading branch information
Showing
11 changed files
with
174 additions
and
102 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
131 changes: 88 additions & 43 deletions
131
packages/block-editor/src/components/block-list/insertion-point.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,74 +1,119 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import classnames from 'classnames'; | ||
|
||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { useState } from '@wordpress/element'; | ||
import { useSelect } from '@wordpress/data'; | ||
import { useState } from '@wordpress/element'; | ||
import { Popover } from '@wordpress/components'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import Inserter from '../inserter'; | ||
|
||
export default function BlockInsertionPoint( { rootClientId, clientId } ) { | ||
const [ isInserterFocused, setInserterFocused ] = useState( false ); | ||
function Indicator( { clientId } ) { | ||
const showInsertionPoint = useSelect( ( select ) => { | ||
const { | ||
getBlockIndex, | ||
getBlockInsertionPoint, | ||
isBlockInsertionPointVisible, | ||
getBlockRootClientId, | ||
} = select( 'core/block-editor' ); | ||
const rootClientId = getBlockRootClientId( clientId ); | ||
const blockIndex = getBlockIndex( clientId, rootClientId ); | ||
const insertionPoint = getBlockInsertionPoint(); | ||
return ( | ||
isBlockInsertionPointVisible() && | ||
insertionPoint.index === blockIndex && | ||
insertionPoint.rootClientId === rootClientId | ||
); | ||
}, [ clientId, rootClientId ] ); | ||
}, [ clientId ] ); | ||
|
||
function onFocus( event ) { | ||
// Stop propagation of the focus event to avoid selecting the current | ||
// block while inserting a new block, as it is not relevant to sibling | ||
// insertion and conflicts with contextual toolbar placement. | ||
event.stopPropagation(); | ||
setInserterFocused( true ); | ||
if ( ! showInsertionPoint ) { | ||
return null; | ||
} | ||
|
||
function onBlur() { | ||
setInserterFocused( false ); | ||
return <div className="block-editor-block-list__insertion-point-indicator" />; | ||
} | ||
|
||
export default function InsertionPoint( { | ||
className, | ||
isMultiSelecting, | ||
selectedBlockClientId, | ||
children, | ||
} ) { | ||
const [ isInserterShown, setIsInserterShown ] = useState( false ); | ||
const [ isInserterForced, setIsInserterForced ] = useState( false ); | ||
const [ inserterElement, setInserterElement ] = useState( null ); | ||
const [ inserterClientId, setInserterClientId ] = useState( null ); | ||
|
||
function onMouseMove( event ) { | ||
if ( event.target.className !== className ) { | ||
if ( isInserterShown ) { | ||
setIsInserterShown( false ); | ||
} | ||
return; | ||
} | ||
|
||
const rect = event.target.getBoundingClientRect(); | ||
const offset = event.clientY - rect.top; | ||
const element = Array.from( event.target.children ).find( ( blockEl ) => { | ||
return blockEl.offsetTop > offset; | ||
} ); | ||
|
||
if ( ! element ) { | ||
return; | ||
} | ||
|
||
const clientId = element.id.slice( 'block-'.length ); | ||
|
||
if ( ! clientId || clientId === selectedBlockClientId ) { | ||
return; | ||
} | ||
|
||
const elementRect = element.getBoundingClientRect(); | ||
|
||
if ( event.clientX > elementRect.right || event.clientX < elementRect.left ) { | ||
if ( isInserterShown ) { | ||
setIsInserterShown( false ); | ||
} | ||
return; | ||
} | ||
|
||
setIsInserterShown( true ); | ||
setInserterElement( element ); | ||
setInserterClientId( clientId ); | ||
} | ||
|
||
return ( | ||
<div className="block-editor-block-list__insertion-point"> | ||
{ showInsertionPoint && ( | ||
<div className="block-editor-block-list__insertion-point-indicator" /> | ||
) } | ||
<div | ||
onFocus={ onFocus } | ||
onBlur={ onBlur } | ||
// While ideally it would be enough to capture the | ||
// bubbling focus event from the Inserter, due to the | ||
// characteristics of click focusing of `button`s in | ||
// Firefox and Safari, it is not reliable. | ||
// | ||
// See: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Clicking_and_focus | ||
tabIndex={ -1 } | ||
className={ | ||
classnames( 'block-editor-block-list__insertion-point-inserter', { | ||
'is-visible': isInserterFocused, | ||
} ) | ||
} | ||
> | ||
<Inserter | ||
rootClientId={ rootClientId } | ||
clientId={ clientId } | ||
/> | ||
return <> | ||
{ ! isMultiSelecting && ( isInserterShown || isInserterForced ) && <Popover | ||
noArrow | ||
animate={ false } | ||
anchorRef={ inserterElement } | ||
position="top right left" | ||
focusOnMount={ false } | ||
className="block-editor-block-list__block-popover" | ||
__unstableSlotName="block-toolbar" | ||
> | ||
<div className="block-editor-block-list__insertion-point" style={ { width: inserterElement.offsetWidth } }> | ||
<Indicator clientId={ inserterClientId } /> | ||
<div | ||
onFocus={ () => setIsInserterForced( true ) } | ||
onBlur={ () => setIsInserterForced( false ) } | ||
// While ideally it would be enough to capture the | ||
// bubbling focus event from the Inserter, due to the | ||
// characteristics of click focusing of `button`s in | ||
// Firefox and Safari, it is not reliable. | ||
// | ||
// See: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Clicking_and_focus | ||
tabIndex={ -1 } | ||
className="block-editor-block-list__insertion-point-inserter" | ||
> | ||
<Inserter clientId={ inserterClientId } /> | ||
</div> | ||
</div> | ||
</Popover> } | ||
<div onMouseMove={ ! isInserterForced && ! isMultiSelecting ? onMouseMove : undefined }> | ||
{ children } | ||
</div> | ||
); | ||
</>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.