diff --git a/packages/block-editor/src/components/block-list/index.js b/packages/block-editor/src/components/block-list/index.js index 8157709b40b765..2193cedcf1c108 100644 --- a/packages/block-editor/src/components/block-list/index.js +++ b/packages/block-editor/src/components/block-list/index.js @@ -6,9 +6,23 @@ import classnames from 'classnames'; /** * WordPress dependencies */ -import { AsyncModeProvider, useSelect, useDispatch } from '@wordpress/data'; -import { useViewportMatch, useMergeRefs } from '@wordpress/compose'; -import { createContext, useState, useMemo } from '@wordpress/element'; +import { + AsyncModeProvider, + useSelect, + useDispatch, + useRegistry, +} from '@wordpress/data'; +import { + useViewportMatch, + useMergeRefs, + useDebounce, +} from '@wordpress/compose'; +import { + createContext, + useState, + useMemo, + useCallback, +} from '@wordpress/element'; /** * Internal dependencies @@ -30,6 +44,7 @@ import { const elementContext = createContext(); export const IntersectionObserver = createContext(); +const pendingBlockVisibilityUpdatesPerRegistry = new WeakMap(); function Root( { className, ...settings } ) { const [ element, setElement ] = useState(); @@ -47,7 +62,24 @@ function Root( { className, ...settings } ) { }, [] ); + const registry = useRegistry(); const { setBlockVisibility } = useDispatch( blockEditorStore ); + + const delayedBlockVisibilityUpdates = useDebounce( + useCallback( () => { + const updates = {}; + pendingBlockVisibilityUpdatesPerRegistry + .get( registry ) + .forEach( ( [ id, isIntersecting ] ) => { + updates[ id ] = isIntersecting; + } ); + setBlockVisibility( updates ); + }, [ registry ] ), + 300, + { + trailing: true, + } + ); const intersectionObserver = useMemo( () => { const { IntersectionObserver: Observer } = window; @@ -56,12 +88,16 @@ function Root( { className, ...settings } ) { } return new Observer( ( entries ) => { - const updates = {}; + if ( ! pendingBlockVisibilityUpdatesPerRegistry.get( registry ) ) { + pendingBlockVisibilityUpdatesPerRegistry.set( registry, [] ); + } for ( const entry of entries ) { const clientId = entry.target.getAttribute( 'data-block' ); - updates[ clientId ] = entry.isIntersecting; + pendingBlockVisibilityUpdatesPerRegistry + .get( registry ) + .push( [ clientId, entry.isIntersecting ] ); } - setBlockVisibility( updates ); + delayedBlockVisibilityUpdates(); } ); }, [] ); const innerBlocksProps = useInnerBlocksProps( diff --git a/packages/block-editor/src/components/block-list/use-in-between-inserter.js b/packages/block-editor/src/components/block-list/use-in-between-inserter.js index dac535e74c37f4..82dcffeaa4dde1 100644 --- a/packages/block-editor/src/components/block-list/use-in-between-inserter.js +++ b/packages/block-editor/src/components/block-list/use-in-between-inserter.js @@ -1,8 +1,7 @@ /** * WordPress dependencies */ -import { useRefEffect } from '@wordpress/compose'; - +import { useRefEffect, useDebounce } from '@wordpress/compose'; import { useSelect, useDispatch } from '@wordpress/data'; import { useContext } from '@wordpress/element'; @@ -33,6 +32,10 @@ export function useInBetweenInserter() { const { showInsertionPoint, hideInsertionPoint } = useDispatch( blockEditorStore ); + const delayedShowInsertionPoint = useDebounce( showInsertionPoint, 500, { + trailing: true, + } ); + return useRefEffect( ( node ) => { if ( isInBetweenInserterDisabled ) { @@ -53,6 +56,7 @@ export function useInBetweenInserter() { 'block-editor-block-list__layout' ) ) { + delayedShowInsertionPoint.cancel(); if ( isBlockInsertionPointVisible() ) { hideInsertionPoint(); } @@ -134,6 +138,7 @@ export function useInBetweenInserter() { ( event.clientX > elementRect.right || event.clientX < elementRect.left ) ) ) { + delayedShowInsertionPoint.cancel(); if ( isBlockInsertionPointVisible() ) { hideInsertionPoint(); } @@ -145,13 +150,14 @@ export function useInBetweenInserter() { // Don't show the in-between inserter before the first block in // the list (preserves the original behaviour). if ( index === 0 ) { + delayedShowInsertionPoint.cancel(); if ( isBlockInsertionPointVisible() ) { hideInsertionPoint(); } return; } - showInsertionPoint( rootClientId, index, { + delayedShowInsertionPoint( rootClientId, index, { __unstableWithInserter: true, } ); } diff --git a/packages/block-editor/src/components/block-tools/insertion-point.js b/packages/block-editor/src/components/block-tools/insertion-point.js index 5bd69fcd2e5329..004dbfe85222f1 100644 --- a/packages/block-editor/src/components/block-tools/insertion-point.js +++ b/packages/block-editor/src/components/block-tools/insertion-point.js @@ -149,13 +149,13 @@ function InsertionPointPopover( { ...( ! isVertical ? horizontalLine.rest : verticalLine.rest ), opacity: 1, borderRadius: '2px', - transition: { delay: isInserterShown ? 0.5 : 0, type: 'tween' }, + transition: { delay: isInserterShown ? 0.1 : 0, type: 'tween' }, }, hover: { ...( ! isVertical ? horizontalLine.hover : verticalLine.hover ), opacity: 1, borderRadius: '2px', - transition: { delay: 0.5, type: 'tween' }, + transition: { delay: 0.1, type: 'tween' }, }, }; @@ -165,7 +165,7 @@ function InsertionPointPopover( { }, rest: { scale: 1, - transition: { delay: 0.4, type: 'tween' }, + transition: { type: 'tween' }, }, }; diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index 72075c025b4b08..a27a799a203347 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -614,7 +614,6 @@ const withBlockReset = ( reducer ) => ( state, action ) => { order: mapBlockOrder( action.blocks ), parents: mapBlockParents( action.blocks ), controlledInnerBlocks: {}, - visibility: {}, }; const subTree = buildBlockTree( newState, action.blocks ); @@ -1162,17 +1161,6 @@ export const blocks = flow( } return state; }, - - visibility( state = {}, action ) { - if ( action.type === 'SET_BLOCK_VISIBILITY' ) { - return { - ...state, - ...action.updates, - }; - } - - return state; - }, } ); /** @@ -1215,6 +1203,25 @@ export function draggedBlocks( state = [], action ) { return state; } +/** + * Reducer tracking the visible blocks. + * + * @param {Record} state Current state. + * @param {Object} action Dispatched action. + * + * @return {Record} Block visibility. + */ +export function blockVisibility( state = {}, action ) { + if ( action.type === 'SET_BLOCK_VISIBILITY' ) { + return { + ...state, + ...action.updates, + }; + } + + return state; +} + /** * Internal helper reducer for selectionStart and selectionEnd. Can hold a block * selection, represented by an object with property clientId. @@ -1660,7 +1667,7 @@ export function hasBlockMovingClientId( state = null, action ) { * * @return {[string,Object]} Updated state. */ -export function lastBlockAttributesChange( state, action ) { +export function lastBlockAttributesChange( state = null, action ) { switch ( action.type ) { case 'UPDATE_BLOCK': if ( ! action.updates.attributes ) { @@ -1681,7 +1688,7 @@ export function lastBlockAttributesChange( state, action ) { ); } - return null; + return state; } /** @@ -1813,4 +1820,5 @@ export default combineReducers( { highlightedBlock, lastBlockInserted, temporarilyEditingAsBlocks, + blockVisibility, } ); diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 401bf5b517d269..498f3f0c4e72b4 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -2670,7 +2670,7 @@ export function wasBlockJustInserted( state, clientId, source ) { * @return {boolean} True if the block is visible. */ export function isBlockVisible( state, clientId ) { - return state.blocks.visibility?.[ clientId ] ?? true; + return state.blockVisibility?.[ clientId ] ?? true; } /** @@ -2682,12 +2682,12 @@ export function isBlockVisible( state, clientId ) { export const __unstableGetVisibleBlocks = createSelector( ( state ) => { return new Set( - Object.keys( state.blocks.visibility ).filter( - ( key ) => state.blocks.visibility[ key ] + Object.keys( state.blockVisibility ).filter( + ( key ) => state.blockVisibility[ key ] ) ); }, - ( state ) => [ state.blocks.visibility ] + ( state ) => [ state.blockVisibility ] ); export const __unstableGetContentLockingParent = createSelector( diff --git a/packages/block-editor/src/store/test/reducer.js b/packages/block-editor/src/store/test/reducer.js index 8100048b3dc09a..0ae5d70504a4aa 100644 --- a/packages/block-editor/src/store/test/reducer.js +++ b/packages/block-editor/src/store/test/reducer.js @@ -292,7 +292,6 @@ describe( 'state', () => { chicken: '', }, controlledInnerBlocks: {}, - visibility: {}, } ); expect( state.tree.chicken ).not.toBe( existingState.tree.chicken @@ -375,7 +374,6 @@ describe( 'state', () => { chicken: '', }, controlledInnerBlocks: {}, - visibility: {}, } ); expect( state.tree.chicken ).not.toBe( existingState.tree.chicken @@ -525,7 +523,6 @@ describe( 'state', () => { [ newChildBlockId3 ]: 'chicken', }, controlledInnerBlocks: {}, - visibility: {}, } ); expect( state.tree[ '' ].innerBlocks[ 0 ] ).toBe( @@ -635,7 +632,6 @@ describe( 'state', () => { [ newChildBlockId ]: 'chicken', }, controlledInnerBlocks: {}, - visibility: {}, } ); // The block object of the parent should be updated. @@ -657,7 +653,6 @@ describe( 'state', () => { isIgnoredChange: false, tree: {}, controlledInnerBlocks: {}, - visibility: {}, } ); } ); @@ -3085,18 +3080,6 @@ describe( 'state', () => { 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': { food: 'banana' }, } ); } ); - - it( 'returns null on anything other than block attributes update', () => { - const original = deepFreeze( { - 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1': { food: 'banana' }, - } ); - - const state = lastBlockAttributesChange( original, { - type: '__INERT__', - } ); - - expect( state ).toBe( null ); - } ); } ); describe( 'lastBlockInserted', () => { diff --git a/packages/e2e-tests/specs/editor/plugins/cpt-locking.test.js b/packages/e2e-tests/specs/editor/plugins/cpt-locking.test.js index b64368aab8328e..33532f7c43f8bb 100644 --- a/packages/e2e-tests/specs/editor/plugins/cpt-locking.test.js +++ b/packages/e2e-tests/specs/editor/plugins/cpt-locking.test.js @@ -121,6 +121,9 @@ describe( 'cpt locking', () => { it( 'should not allow blocks to be inserted in inner blocks', async () => { await page.click( 'button[aria-label="Two columns; equal split"]' ); + await page.evaluate( + () => new Promise( window.requestIdleCallback ) + ); expect( await page.$( '.wp-block-column .block-editor-button-block-appender'