Skip to content

Commit

Permalink
Auto-inserting blocks: Remove toggle if block is present elsewhere (#…
Browse files Browse the repository at this point in the history
…54024)

If a block that would normally be auto-inserted is not found in its designated position but elsewhere, we can assume somewhat safely that it was inserted manually, and that the user is aware of the block and won't need any UI elements to make it more visible.

Additionally, this PR fixes most of the following warnings (raised [here](#52969 (comment))):

``` 
The 'useSelect' hook returns different values when called with the same state and parameters. This can lead to unnecessary rerenders. 
```

---------

Co-authored-by: George Mamadashvili <[email protected]>
  • Loading branch information
ockham and Mamaduka committed Sep 4, 2023
1 parent fb0b84b commit 4e40bc6
Showing 1 changed file with 84 additions and 57 deletions.
141 changes: 84 additions & 57 deletions packages/block-editor/src/hooks/auto-inserting-blocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/
import { __ } from '@wordpress/i18n';
import { addFilter } from '@wordpress/hooks';
import { Fragment } from '@wordpress/element';
import { Fragment, useMemo } from '@wordpress/element';
import {
__experimentalHStack as HStack,
PanelBody,
Expand All @@ -19,54 +19,53 @@ import { useDispatch, useSelect } from '@wordpress/data';
import { BlockIcon, InspectorControls } from '../components';
import { store as blockEditorStore } from '../store';

function AutoInsertingBlocksControl( props ) {
const { autoInsertedBlocksForCurrentBlock, groupedAutoInsertedBlocks } =
useSelect(
( select ) => {
const { getBlockTypes } = select( blocksStore );
const _autoInsertedBlocksForCurrentBlock =
getBlockTypes()?.filter(
( { autoInsert } ) =>
autoInsert && props.blockName in autoInsert
);
const EMPTY_OBJECT = {};

// Group by block namespace (i.e. prefix before the slash).
const _groupedAutoInsertedBlocks =
_autoInsertedBlocksForCurrentBlock?.reduce(
( groups, block ) => {
const [ namespace ] = block.name.split( '/' );
if ( ! groups[ namespace ] ) {
groups[ namespace ] = [];
}
groups[ namespace ].push( block );
return groups;
},
{}
);
function AutoInsertingBlocksControl( props ) {
const blockTypes = useSelect(
( select ) => select( blocksStore ).getBlockTypes(),
[]
);

return {
autoInsertedBlocksForCurrentBlock:
_autoInsertedBlocksForCurrentBlock,
groupedAutoInsertedBlocks: _groupedAutoInsertedBlocks,
};
},
[ props.blockName ]
);
const autoInsertedBlocksForCurrentBlock = useMemo(
() =>
blockTypes?.filter(
( { autoInsert } ) =>
autoInsert && props.blockName in autoInsert
),
[ blockTypes, props.blockName ]
);

const {
autoInsertedBlockClientIds,
blockIndex,
rootClientId,
innerBlocksLength,
} = useSelect(
const { blockIndex, rootClientId, innerBlocksLength } = useSelect(
( select ) => {
const { getBlock, getBlockIndex, getBlockRootClientId } =
select( blockEditorStore );
const _rootClientId = getBlockRootClientId( props.clientId );

return {
blockIndex: getBlockIndex( props.clientId ),
innerBlocksLength: getBlock( props.clientId )?.innerBlocks
?.length,
rootClientId: getBlockRootClientId( props.clientId ),
};
},
[ props.clientId ]
);

const autoInsertedBlockClientIds = useSelect(
( select ) => {
const { getBlock, getGlobalBlockCount } =
select( blockEditorStore );

const _autoInsertedBlockClientIds =
autoInsertedBlocksForCurrentBlock.reduce(
( clientIds, block ) => {
// If the block doesn't exist anywhere in the block tree,
// we know that we have to display the toggle for it, and set
// it to disabled.
if ( getGlobalBlockCount( block.name ) === 0 ) {
return clientIds;
}

const relativePosition =
block?.autoInsert?.[ props.blockName ];
let candidates;
Expand All @@ -78,7 +77,7 @@ function AutoInsertingBlocksControl( props ) {
// as an auto-inserted block (inserted `before` or `after` the current one),
// as the block might've been auto-inserted and then moved around a bit by the user.
candidates =
getBlock( _rootClientId )?.innerBlocks;
getBlock( rootClientId )?.innerBlocks;
break;

case 'first_child':
Expand All @@ -96,38 +95,66 @@ function AutoInsertingBlocksControl( props ) {
( { name } ) => name === block.name
);

// If the block exists in the designated location, we consider it auto-inserted
// and show the toggle as enabled.
if ( autoInsertedBlock ) {
clientIds[ block.name ] =
autoInsertedBlock.clientId;
return {
...clientIds,
[ block.name ]: autoInsertedBlock.clientId,
};
}

// TOOD: If no auto-inserted block was found in any of its designated locations,
// we want to check if it's present elsewhere in the block tree.
// If it is, we'd consider it manually inserted and would want to remove the
// corresponding toggle from the block inspector panel.

return clientIds;
// If no auto-inserted block was found in any of its designated locations,
// but it exists elsewhere in the block tree, we consider it manually inserted.
// In this case, we take note and will remove the corresponding toggle from the
// block inspector panel.
return {
...clientIds,
[ block.name ]: false,
};
},
{}
);

return {
blockIndex: getBlockIndex( props.clientId ),
innerBlocksLength: getBlock( props.clientId )?.innerBlocks
?.length,
rootClientId: _rootClientId,
autoInsertedBlockClientIds: _autoInsertedBlockClientIds,
};
if ( Object.values( _autoInsertedBlockClientIds ).length > 0 ) {
return _autoInsertedBlockClientIds;
}

return EMPTY_OBJECT;
},
[ autoInsertedBlocksForCurrentBlock, props.blockName, props.clientId ]
[
autoInsertedBlocksForCurrentBlock,
props.blockName,
props.clientId,
rootClientId,
]
);

const { insertBlock, removeBlock } = useDispatch( blockEditorStore );

if ( ! autoInsertedBlocksForCurrentBlock.length ) {
// Remove toggle if block isn't present in the designated location but elsewhere in the block tree.
const autoInsertedBlocksForCurrentBlockIfNotPresentElsewhere =
autoInsertedBlocksForCurrentBlock?.filter(
( block ) => autoInsertedBlockClientIds?.[ block.name ] !== false
);

if ( ! autoInsertedBlocksForCurrentBlockIfNotPresentElsewhere.length ) {
return null;
}

// Group by block namespace (i.e. prefix before the slash).
const groupedAutoInsertedBlocks = autoInsertedBlocksForCurrentBlock.reduce(
( groups, block ) => {
const [ namespace ] = block.name.split( '/' );
if ( ! groups[ namespace ] ) {
groups[ namespace ] = [];
}
groups[ namespace ].push( block );
return groups;
},
{}
);

const insertBlockIntoDesignatedLocation = ( block, relativePosition ) => {
switch ( relativePosition ) {
case 'before':
Expand Down

0 comments on commit 4e40bc6

Please sign in to comment.