Skip to content

Commit

Permalink
Move the visible blocks state to the block editor store (#41104)
Browse files Browse the repository at this point in the history
  • Loading branch information
youknowriad authored May 19, 2022
1 parent aa1f770 commit a1e1fdc
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 52 deletions.
21 changes: 21 additions & 0 deletions docs/reference-guides/data/data-core-block-editor.md
Original file line number Diff line number Diff line change
Expand Up @@ -976,6 +976,19 @@ _Returns_

- `boolean`: Is Valid.

### isBlockVisible

Tells if the block is visible on the canvas or not.

_Parameters_

- _state_ `Object`: Global application state.
- _clientId_ `Object`: Client Id of the block.

_Returns_

- `boolean`: True if the block is visible.

### isBlockWithinSelection

Returns true if the block corresponding to the specified client ID is
Expand Down Expand Up @@ -1456,6 +1469,14 @@ _Parameters_

- _hasBlockMovingClientId_ `string|null`: Enable/Disable block moving mode.

### setBlockVisibility

Action that sets whether a block has controlled inner blocks.

_Parameters_

- _updates_ `Record<string,boolean>`: The block's clientId.

### setHasControlledInnerBlocks

Action that sets whether a block has controlled inner blocks.
Expand Down
88 changes: 44 additions & 44 deletions packages/block-editor/src/components/block-list/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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( [
Expand All @@ -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>
);
}
Expand All @@ -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(),
};
},
[ 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 }
Expand Down
19 changes: 12 additions & 7 deletions packages/block-editor/src/components/block-popover/inbetween.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,23 @@ function BlockPopoverInbetween( {
__unstableContentRef,
...props
} ) {
const { orientation, rootClientId } = useSelect(
const { orientation, rootClientId, isVisible } = useSelect(
( select ) => {
const { getBlockListSettings, getBlockRootClientId } = select(
blockEditorStore
);
const {
getBlockListSettings,
getBlockRootClientId,
isBlockVisible,
} = select( blockEditorStore );

const _rootClientId = getBlockRootClientId( previousClientId );
return {
orientation:
getBlockListSettings( _rootClientId )?.orientation ||
'vertical',
rootClientId: _rootClientId,
isVisible:
isBlockVisible( previousClientId ) &&
isBlockVisible( nextClientId ),
};
},
[ previousClientId ]
Expand All @@ -48,7 +53,7 @@ function BlockPopoverInbetween( {
const nextElement = useBlockElement( nextClientId );
const isVertical = orientation === 'vertical';
const style = useMemo( () => {
if ( ! previousElement && ! nextElement ) {
if ( ( ! previousElement && ! nextElement ) || ! isVisible ) {
return {};
}

Expand Down Expand Up @@ -87,7 +92,7 @@ function BlockPopoverInbetween( {
}, [ previousElement, nextElement, isVertical ] );

const getAnchorRect = useCallback( () => {
if ( ! previousElement && ! nextElement ) {
if ( ( ! previousElement && ! nextElement ) || ! isVisible ) {
return {};
}

Expand Down Expand Up @@ -149,7 +154,7 @@ function BlockPopoverInbetween( {

const popoverScrollRef = usePopoverScroll( __unstableContentRef );

if ( ! previousElement || ! nextElement ) {
if ( ! previousElement || ! nextElement || ! isVisible ) {
return null;
}

Expand Down
12 changes: 12 additions & 0 deletions packages/block-editor/src/store/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -1603,3 +1603,15 @@ export function setHasControlledInnerBlocks(
clientId,
};
}

/**
* Action that sets whether a block has controlled inner blocks.
*
* @param {Record<string,boolean>} updates The block's clientId.
*/
export function setBlockVisibility( updates ) {
return {
type: 'SET_BLOCK_VISIBILITY',
updates,
};
}
15 changes: 14 additions & 1 deletion packages/block-editor/src/store/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,7 @@ const withBlockReset = ( reducer ) => ( state, action ) => {
order: mapBlockOrder( action.blocks ),
parents: mapBlockParents( action.blocks ),
controlledInnerBlocks: {},
visibility: {},
};

const subTree = buildBlockTree( newState, action.blocks );
Expand Down Expand Up @@ -1139,6 +1140,17 @@ export const blocks = flow(
}
return state;
},

visibility( state = {}, action ) {
if ( action.type === 'SET_BLOCK_VISIBILITY' ) {
return {
...state,
...action.updates,
};
}

return state;
},
} );

/**
Expand Down Expand Up @@ -1678,7 +1690,8 @@ export function automaticChangeStatus( state, action ) {

return;
// Undoing an automatic change should still be possible after mouse
// move.
// move or after visibility change.
case 'SET_BLOCK_VISIBILITY':
case 'START_TYPING':
case 'STOP_TYPING':
return state;
Expand Down
28 changes: 28 additions & 0 deletions packages/block-editor/src/store/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -2651,3 +2651,31 @@ export function wasBlockJustInserted( state, clientId, source ) {
lastBlockInserted.source === source
);
}

/**
* Tells if the block is visible on the canvas or not.
*
* @param {Object} state Global application state.
* @param {Object} clientId Client Id of the block.
* @return {boolean} True if the block is visible.
*/
export function isBlockVisible( state, clientId ) {
return state.blocks.visibility?.[ clientId ] ?? true;
}

/**
* Returns the list of all hidden blocks.
*
* @param {Object} state Global application state.
* @return {[string]} List of hidden blocks.
*/
export const __unstableGetVisibleBlocks = createSelector(
( state ) => {
return new Set(
Object.keys( state.blocks.visibility ).filter(
( key ) => state.blocks.visibility[ key ]
)
);
},
( state ) => [ state.blocks.visibility ]
);
5 changes: 5 additions & 0 deletions packages/block-editor/src/store/test/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ describe( 'state', () => {
chicken: '',
},
controlledInnerBlocks: {},
visibility: {},
} );
expect( state.tree.chicken ).not.toBe(
existingState.tree.chicken
Expand Down Expand Up @@ -371,6 +372,7 @@ describe( 'state', () => {
chicken: '',
},
controlledInnerBlocks: {},
visibility: {},
} );
expect( state.tree.chicken ).not.toBe(
existingState.tree.chicken
Expand Down Expand Up @@ -519,6 +521,7 @@ describe( 'state', () => {
[ newChildBlockId3 ]: 'chicken',
},
controlledInnerBlocks: {},
visibility: {},
} );

expect( state.tree[ '' ].innerBlocks[ 0 ] ).toBe(
Expand Down Expand Up @@ -627,6 +630,7 @@ describe( 'state', () => {
[ newChildBlockId ]: 'chicken',
},
controlledInnerBlocks: {},
visibility: {},
} );

// The block object of the parent should be updated.
Expand All @@ -648,6 +652,7 @@ describe( 'state', () => {
isIgnoredChange: false,
tree: {},
controlledInnerBlocks: {},
visibility: {},
} );
} );

Expand Down

0 comments on commit a1e1fdc

Please sign in to comment.