@@ -8,6 +8,7 @@ import classnames from 'classnames';
88 */
99import { AsyncModeProvider , useSelect } from '@wordpress/data' ;
1010import { useViewportMatch , useMergeRefs } from '@wordpress/compose' ;
11+ import { createContext , useState , useMemo } from '@wordpress/element' ;
1112
1213/**
1314 * Internal dependencies
@@ -21,6 +22,8 @@ import { usePreParsePatterns } from '../../utils/pre-parse-patterns';
2122import { LayoutProvider , defaultLayout } from './layout' ;
2223import BlockToolsBackCompat from '../block-tools/back-compat' ;
2324
25+ export const IntersectionObserver = createContext ( ) ;
26+
2427function Root ( { className, children } ) {
2528 const isLargeViewport = useViewportMatch ( 'medium' ) ;
2629 const {
@@ -82,39 +85,51 @@ function Items( {
8285 __experimentalAppenderTagName,
8386 __experimentalLayout : layout = defaultLayout ,
8487} ) {
85- function selector ( select ) {
86- const {
87- getBlockOrder,
88- getSelectedBlockClientId,
89- getMultiSelectedBlockClientIds,
90- hasMultiSelection,
91- } = select ( blockEditorStore ) ;
92- return {
93- blockClientIds : getBlockOrder ( rootClientId ) ,
94- selectedBlockClientId : getSelectedBlockClientId ( ) ,
95- multiSelectedBlockClientIds : getMultiSelectedBlockClientIds ( ) ,
96- hasMultiSelection : hasMultiSelection ( ) ,
97- } ;
98- }
88+ const [ intersectingBlocks , setIntersectingBlocks ] = useState ( new Set ( ) ) ;
89+ const intersectionObserver = useMemo ( ( ) => {
90+ const { IntersectionObserver : Observer } = window ;
9991
100- const {
101- blockClientIds,
102- selectedBlockClientId,
103- multiSelectedBlockClientIds,
104- hasMultiSelection,
105- } = useSelect ( selector , [ rootClientId ] ) ;
92+ if ( ! Observer ) {
93+ return ;
94+ }
95+
96+ return new Observer ( ( entries ) => {
97+ setIntersectingBlocks ( ( oldIntersectingBlocks ) => {
98+ const newIntersectingBlocks = new Set ( oldIntersectingBlocks ) ;
99+ for ( const entry of entries ) {
100+ const clientId = entry . target . getAttribute ( 'data-block' ) ;
101+ const action = entry . isIntersecting ? 'add' : 'delete' ;
102+ newIntersectingBlocks [ action ] ( clientId ) ;
103+ }
104+ return newIntersectingBlocks ;
105+ } ) ;
106+ } ) ;
107+ } , [ setIntersectingBlocks ] ) ;
108+ const { order, selectedBlocks } = useSelect (
109+ ( select ) => {
110+ const { getBlockOrder, getSelectedBlockClientIds } = select (
111+ blockEditorStore
112+ ) ;
113+ return {
114+ order : getBlockOrder ( rootClientId ) ,
115+ selectedBlocks : getSelectedBlockClientIds ( ) ,
116+ } ;
117+ } ,
118+ [ rootClientId ]
119+ ) ;
106120
107121 return (
108122 < LayoutProvider value = { layout } >
109- { blockClientIds . map ( ( clientId , index ) => {
110- const isBlockInSelection = hasMultiSelection
111- ? multiSelectedBlockClientIds . includes ( clientId )
112- : selectedBlockClientId === clientId ;
113-
114- return (
123+ < IntersectionObserver . Provider value = { intersectionObserver } >
124+ { order . map ( ( clientId , index ) => (
115125 < AsyncModeProvider
116126 key = { clientId }
117- value = { ! isBlockInSelection }
127+ value = {
128+ // Only provide data asynchronously if the block is
129+ // not visible and not selected.
130+ ! intersectingBlocks . has ( clientId ) &&
131+ ! selectedBlocks . includes ( clientId )
132+ }
118133 >
119134 < BlockListBlock
120135 rootClientId = { rootClientId }
@@ -125,9 +140,9 @@ function Items( {
125140 index = { index }
126141 />
127142 </ AsyncModeProvider >
128- ) ;
129- } ) }
130- { blockClientIds . length < 1 && placeholder }
143+ ) ) }
144+ </ IntersectionObserver . Provider >
145+ { order . length < 1 && placeholder }
131146 < BlockListAppender
132147 tagName = { __experimentalAppenderTagName }
133148 rootClientId = { rootClientId }
0 commit comments