Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/block-editor/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
### Bug Fixes

- `BlockCompare`: Show whitespace differences in the Resolve Block dialog. The markup's spaces and blank lines are kept instead of collapsed by the browser, and each added or removed line break gets a visible marker, so a diff made only of whitespace no longer looks identical on both sides. The dialog also shows the markup of a converted Custom HTML block, which is kept in its `innerContent` rather than produced by `save`, instead of an empty diff ([#82397](https://github.com/WordPress/gutenberg/pull/82397)).
- Inserter: Stop re-rendering the block list when the block selection changes while the inserter is open ([#83284](https://github.com/WordPress/gutenberg/pull/83284)).

## 18.0.0 (2026-09-23)

Expand Down
40 changes: 22 additions & 18 deletions packages/block-editor/src/components/inserter/block-types-tab.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,29 +166,33 @@ export function BlockTypesTab(
onInsert
);

if ( ! items.length ) {
return <InserterNoResults />;
}
const [ itemsForCurrentRoot, itemsRemaining ] = useMemo( () => {
const forCurrentRoot = [];
const remaining = [];

for ( const item of items ) {
// Skip reusable blocks, they moved to the patterns tab.
if ( item.category === 'reusable' ) {
continue;
}

const itemsForCurrentRoot = [];
const itemsRemaining = [];
// Skip search-only items from browse view (they're still searchable).
if ( item.isSearchOnly ) {
continue;
}

for ( const item of items ) {
// Skip reusable blocks, they moved to the patterns tab.
if ( item.category === 'reusable' ) {
continue;
if ( item.isAllowedInCurrentRoot ) {
forCurrentRoot.push( item );
} else {
remaining.push( item );
}
}

// Skip search-only items from browse view (they're still searchable).
if ( item.isSearchOnly ) {
continue;
}
return [ forCurrentRoot, remaining ];
}, [ items ] );

if ( item.isAllowedInCurrentRoot ) {
itemsForCurrentRoot.push( item );
} else {
itemsRemaining.push( item );
}
if ( ! items.length ) {
return <InserterNoResults />;
}

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import {
getBlockType,
createBlock,
createBlocksFromInnerBlocksTemplate,
store as blocksStore,
parse,
} from '@wordpress/blocks';
import { useSelect, useDispatch } from '@wordpress/data';
import { useCallback, useMemo } from '@wordpress/element';
import { store as noticesStore } from '@wordpress/notices';
import { __, sprintf } from '@wordpress/i18n';
import { useSelect } from '@wordpress/data';
import { useCallback } from '@wordpress/element';
import { store as blockEditorStore } from '../../../store';
import { isFiltered } from '../../../store/utils';
import { unlock } from '../../../lock-unlock';

// Shared so the selector cache survives the inserter closing and reopening.
const FILTERED_OPTIONS = { [ isFiltered ]: true };
const UNFILTERED_OPTIONS = { [ isFiltered ]: false };

/**
* Retrieves the block types inserter state.
Expand All @@ -22,23 +22,16 @@ import { unlock } from '../../../lock-unlock';
* @return {Array} Returns the block types state. (block types, categories, collections, onSelect handler)
*/
const useBlockTypesState = ( rootClientId, onInsert, isQuick ) => {
const options = useMemo(
() => ( { [ isFiltered ]: !! isQuick } ),
[ isQuick ]
);
const [ items ] = useSelect(
( select ) => [
const options = isQuick ? FILTERED_OPTIONS : UNFILTERED_OPTIONS;
// Not wrapped in a tuple, so `useSelect` can return the previous array when the items match.
const items = useSelect(
( select ) =>
select( blockEditorStore ).getInserterItems(
rootClientId,
options
),
],
[ rootClientId, options ]
);
const { getClosestAllowedInsertionPoint } = unlock(
useSelect( blockEditorStore )
);
const { createErrorNotice } = useDispatch( noticesStore );

const [ categories, collections ] = useSelect( ( select ) => {
const { getCategories, getCollections } = select( blocksStore );
Expand All @@ -57,26 +50,6 @@ const useBlockTypesState = ( rootClientId, onInsert, isQuick ) => {
},
shouldFocusBlock
) => {
const destinationClientId = getClosestAllowedInsertionPoint(
name,
rootClientId
);
if ( destinationClientId === null ) {
const title = getBlockType( name )?.title ?? name;
createErrorNotice(
sprintf(
/* translators: %s: block pattern title. */
__( 'Block "%s" can\'t be inserted.' ),
title
),
{
type: 'snackbar',
id: 'inserter-notice',
}
);
return;
}

const insertedBlock =
syncStatus === 'unsynced'
? parse( content, {
Expand All @@ -88,19 +61,9 @@ const useBlockTypesState = ( rootClientId, onInsert, isQuick ) => {
createBlocksFromInnerBlocksTemplate( innerBlocks ),
innerContent
);
onInsert(
insertedBlock,
undefined,
shouldFocusBlock,
destinationClientId
);
onInsert( insertedBlock, undefined, shouldFocusBlock );
},
[
getClosestAllowedInsertionPoint,
rootClientId,
onInsert,
createErrorNotice,
]
[ onInsert ]
);

return [ items, categories, collections, onSelectItem ];
Expand Down
Loading
Loading