-
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
Move the visible blocks state to the block editor store #41104
Changes from all commits
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 | ||
---|---|---|---|---|
|
@@ -6,7 +6,7 @@ import classnames from 'classnames'; | |||
/** | ||||
* WordPress dependencies | ||||
*/ | ||||
import { AsyncModeProvider, useSelect } from '@wordpress/data'; | ||||
import { AsyncModeProvider, useSelect, useDispatch } from '@wordpress/data'; | ||||
import { useViewportMatch, useMergeRefs } from '@wordpress/compose'; | ||||
import { createContext, useState, useMemo } from '@wordpress/element'; | ||||
|
||||
|
@@ -48,6 +48,23 @@ function Root( { className, ...settings } ) { | |||
}, | ||||
[] | ||||
); | ||||
const { setBlockVisibility } = useDispatch( blockEditorStore ); | ||||
const intersectionObserver = useMemo( () => { | ||||
const { IntersectionObserver: Observer } = window; | ||||
|
||||
if ( ! Observer ) { | ||||
return; | ||||
} | ||||
|
||||
return new Observer( ( entries ) => { | ||||
const updates = {}; | ||||
for ( const entry of entries ) { | ||||
const clientId = entry.target.getAttribute( 'data-block' ); | ||||
updates[ clientId ] = entry.isIntersecting; | ||||
} | ||||
setBlockVisibility( updates ); | ||||
} ); | ||||
}, [] ); | ||||
const innerBlocksProps = useInnerBlocksProps( | ||||
{ | ||||
ref: useMergeRefs( [ | ||||
|
@@ -65,7 +82,9 @@ function Root( { className, ...settings } ) { | |||
); | ||||
return ( | ||||
<elementContext.Provider value={ element }> | ||||
<div { ...innerBlocksProps } /> | ||||
<IntersectionObserver.Provider value={ intersectionObserver }> | ||||
<div { ...innerBlocksProps } /> | ||||
</IntersectionObserver.Provider> | ||||
</elementContext.Provider> | ||||
); | ||||
} | ||||
|
@@ -90,59 +109,40 @@ function Items( { | |||
__experimentalAppenderTagName, | ||||
__experimentalLayout: layout = defaultLayout, | ||||
} ) { | ||||
const [ intersectingBlocks, setIntersectingBlocks ] = useState( new Set() ); | ||||
const intersectionObserver = useMemo( () => { | ||||
const { IntersectionObserver: Observer } = window; | ||||
|
||||
if ( ! Observer ) { | ||||
return; | ||||
} | ||||
|
||||
return new Observer( ( entries ) => { | ||||
setIntersectingBlocks( ( oldIntersectingBlocks ) => { | ||||
const newIntersectingBlocks = new Set( oldIntersectingBlocks ); | ||||
for ( const entry of entries ) { | ||||
const clientId = entry.target.getAttribute( 'data-block' ); | ||||
const action = entry.isIntersecting ? 'add' : 'delete'; | ||||
newIntersectingBlocks[ action ]( clientId ); | ||||
} | ||||
return newIntersectingBlocks; | ||||
} ); | ||||
} ); | ||||
}, [ setIntersectingBlocks ] ); | ||||
const { order, selectedBlocks } = useSelect( | ||||
const { order, selectedBlocks, visibleBlocks } = useSelect( | ||||
( select ) => { | ||||
const { getBlockOrder, getSelectedBlockClientIds } = select( | ||||
blockEditorStore | ||||
); | ||||
const { | ||||
getBlockOrder, | ||||
getSelectedBlockClientIds, | ||||
__unstableGetVisibleBlocks, | ||||
} = select( blockEditorStore ); | ||||
return { | ||||
order: getBlockOrder( rootClientId ), | ||||
selectedBlocks: getSelectedBlockClientIds(), | ||||
visibleBlocks: __unstableGetVisibleBlocks(), | ||||
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. There could be a possible optimization opportunity here: Not sure how impactful this really is -- just noting down something I observed when working on #42525 (comment). 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. Unless I'm misunderstanding, I think we do care about the nested blocks here imagine a big group block containing a high number of paragraphs... The paragraphs need to be considered as "hidden" even if they're nested and part of the group is visible because otherwise, it will freeze the editor. 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. In that case I think it doesn't work. Would need to verify carefully to be 100% sure, but only the top-level If there is a group block with many paragraphs, then these paragraph blocks have correct 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. AsyncModeProvider is rendered around each block
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. Turns out I misunderstood how nested blocks are rendered! I thought that the Still, 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, that sounds like something we could try. That said, It's not always obvious what's more performant: a more complex selector or a loop that does not much (BlockListBlock components are memoized). |
||||
}; | ||||
}, | ||||
[ rootClientId ] | ||||
); | ||||
|
||||
return ( | ||||
<LayoutProvider value={ layout }> | ||||
<IntersectionObserver.Provider value={ intersectionObserver }> | ||||
{ order.map( ( clientId ) => ( | ||||
<AsyncModeProvider | ||||
key={ clientId } | ||||
value={ | ||||
// Only provide data asynchronously if the block is | ||||
// not visible and not selected. | ||||
! intersectingBlocks.has( clientId ) && | ||||
! selectedBlocks.includes( clientId ) | ||||
} | ||||
> | ||||
<BlockListBlock | ||||
rootClientId={ rootClientId } | ||||
clientId={ clientId } | ||||
/> | ||||
</AsyncModeProvider> | ||||
) ) } | ||||
</IntersectionObserver.Provider> | ||||
{ order.map( ( clientId ) => ( | ||||
<AsyncModeProvider | ||||
key={ clientId } | ||||
value={ | ||||
// Only provide data asynchronously if the block is | ||||
// not visible and not selected. | ||||
! visibleBlocks.has( clientId ) && | ||||
! selectedBlocks.includes( clientId ) | ||||
} | ||||
> | ||||
<BlockListBlock | ||||
rootClientId={ rootClientId } | ||||
clientId={ clientId } | ||||
/> | ||||
</AsyncModeProvider> | ||||
) ) } | ||||
{ order.length < 1 && placeholder } | ||||
<BlockListAppender | ||||
tagName={ __experimentalAppenderTagName } | ||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1603,3 +1603,15 @@ export function setHasControlledInnerBlocks( | |
clientId, | ||
}; | ||
} | ||
|
||
/** | ||
* Action that sets whether a block has controlled inner blocks. | ||
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. -Action that sets whether a block has controlled inner blocks.
+ Action that sets whether a block is visible on the canvas. |
||
* | ||
* @param {Record<string,boolean>} updates The block's clientId. | ||
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. There's a mismatch between the English copy, which assumes a single block — thus suggesting an argument list like From the rest of the PR, I would guess that the batching is preferred, and so the fix would be to update the docstring: Action that sets whether given blocks are visible on the canvas. and the param description: For each block's clientId, its new visibility setting. |
||
*/ | ||
export function setBlockVisibility( updates ) { | ||
return { | ||
type: 'SET_BLOCK_VISIBILITY', | ||
updates, | ||
}; | ||
} |
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've moved the "provider" to "root" because there's no need to have a different intersection observer per "inner blocks" container. One for the whole editor is sufficient.