Skip to content

Commit

Permalink
Block editor: async mode: enable only for blocks out of view
Browse files Browse the repository at this point in the history
  • Loading branch information
ellatrix committed May 14, 2021
1 parent f21f09d commit c3947bd
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 36 deletions.
75 changes: 45 additions & 30 deletions packages/block-editor/src/components/block-list/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import classnames from 'classnames';
*/
import { AsyncModeProvider, useSelect } from '@wordpress/data';
import { useViewportMatch, useMergeRefs } from '@wordpress/compose';
import { createContext, useState, useMemo } from '@wordpress/element';

/**
* Internal dependencies
Expand All @@ -21,6 +22,8 @@ import { usePreParsePatterns } from '../../utils/pre-parse-patterns';
import { LayoutProvider, defaultLayout } from './layout';
import BlockToolsBackCompat from '../block-tools/back-compat';

export const IntersectionObserver = createContext();

function Root( { className, children } ) {
const isLargeViewport = useViewportMatch( 'medium' );
const {
Expand Down Expand Up @@ -82,39 +85,51 @@ function Items( {
__experimentalAppenderTagName,
__experimentalLayout: layout = defaultLayout,
} ) {
function selector( select ) {
const {
getBlockOrder,
getSelectedBlockClientId,
getMultiSelectedBlockClientIds,
hasMultiSelection,
} = select( blockEditorStore );
return {
blockClientIds: getBlockOrder( rootClientId ),
selectedBlockClientId: getSelectedBlockClientId(),
multiSelectedBlockClientIds: getMultiSelectedBlockClientIds(),
hasMultiSelection: hasMultiSelection(),
};
}
const [ intersectingBlocks, setIntersectingBlocks ] = useState( new Set() );
const intersectionObserver = useMemo( () => {
const { IntersectionObserver: Observer } = window;

const {
blockClientIds,
selectedBlockClientId,
multiSelectedBlockClientIds,
hasMultiSelection,
} = useSelect( selector, [ rootClientId ] );
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(
( select ) => {
const { getBlockOrder, getSelectedBlockClientIds } = select(
blockEditorStore
);
return {
order: getBlockOrder( rootClientId ),
selectedBlocks: getSelectedBlockClientIds(),
};
},
[ rootClientId ]
);

return (
<LayoutProvider value={ layout }>
{ blockClientIds.map( ( clientId, index ) => {
const isBlockInSelection = hasMultiSelection
? multiSelectedBlockClientIds.includes( clientId )
: selectedBlockClientId === clientId;

return (
<IntersectionObserver.Provider value={ intersectionObserver }>
{ order.map( ( clientId, index ) => (
<AsyncModeProvider
key={ clientId }
value={ ! isBlockInSelection }
value={
// Only provide data asynchronously if the block is
// not visible and not selected.
! intersectingBlocks.has( clientId ) &&
! selectedBlocks.includes( clientId )
}
>
<BlockListBlock
rootClientId={ rootClientId }
Expand All @@ -125,9 +140,9 @@ function Items( {
index={ index }
/>
</AsyncModeProvider>
);
} ) }
{ blockClientIds.length < 1 && placeholder }
) ) }
</IntersectionObserver.Provider>
{ order.length < 1 && placeholder }
<BlockListAppender
tagName={ __experimentalAppenderTagName }
rootClientId={ rootClientId }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { useNavModeExit } from './use-nav-mode-exit';
import { useScrollIntoView } from './use-scroll-into-view';
import { useBlockRefProvider } from './use-block-refs';
import { useMultiSelection } from './use-multi-selection';
import { useIntersectionObserver } from './use-intersection-observer';
import { store as blockEditorStore } from '../../../store';

/**
Expand Down Expand Up @@ -113,6 +114,7 @@ export function useBlockProps( props = {}, { __unstableIsHtml } = {} ) {
useEventHandlers( clientId ),
useNavModeExit( clientId ),
useIsHovered(),
useIntersectionObserver(),
useMovingAnimation( {
isSelected: isPartOfSelection,
adjustScrolling,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* WordPress dependencies
*/
import { useRefEffect } from '@wordpress/compose';
import { useContext } from '@wordpress/element';

/**
* Internal dependencies
*/
import { IntersectionObserver } from '../';

export function useIntersectionObserver() {
const observer = useContext( IntersectionObserver );
return useRefEffect(
( node ) => {
if ( observer ) {
observer.observe( node );
return () => {
observer.unobserve( node );
};
}
},
[ observer ]
);
}
13 changes: 7 additions & 6 deletions packages/block-editor/src/utils/dom.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// Consider the block appender to be a child block of its own, which also has
// this class.
const BLOCK_SELECTOR = '.wp-block';

/**
* Returns true if two elements are contained within the same block.
*
Expand All @@ -7,10 +11,7 @@
* @return {boolean} Whether elements are in the same block.
*/
export function isInSameBlock( a, b ) {
return (
a.closest( '.block-editor-block-list__block' ) ===
b.closest( '.block-editor-block-list__block' )
);
return a.closest( BLOCK_SELECTOR ) === b.closest( BLOCK_SELECTOR );
}

/**
Expand All @@ -24,7 +25,7 @@ export function isInSameBlock( a, b ) {
* children.
*/
export function isInsideRootBlock( blockElement, element ) {
const parentBlock = element.closest( '.block-editor-block-list__block' );
const parentBlock = element.closest( BLOCK_SELECTOR );
return parentBlock === blockElement;
}

Expand All @@ -46,7 +47,7 @@ export function getBlockClientId( node ) {
}

const elementNode = /** @type {Element} */ ( node );
const blockNode = elementNode.closest( '.block-editor-block-list__block' );
const blockNode = elementNode.closest( BLOCK_SELECTOR );

if ( ! blockNode ) {
return;
Expand Down

0 comments on commit c3947bd

Please sign in to comment.