- { showMostUsedBlocks && !! suggestedItems.length && (
+
+ { showMostUsedBlocks &&
+ // Only show the most used blocks if the total amount of block
+ // is larger than 1 row, otherwise it is not so useful.
+ items.length > 3 &&
+ !! suggestedItems.length && (
) }
- { currentlyRenderedCategories.map( ( category ) => {
- const categoryItems = itemsPerCategory[ category.slug ];
- if ( ! categoryItems || ! categoryItems.length ) {
+ { currentlyRenderedCategories.map( ( category ) => {
+ const categoryItems = items.filter(
+ ( item ) => item.category === category.slug
+ );
+ if ( ! categoryItems || ! categoryItems.length ) {
+ return null;
+ }
+ return (
+
+
+
+ );
+ } ) }
+
+ { didRenderAllCategories && uncategorizedItems.length > 0 && (
+
+
+
+ ) }
+
+ { currentlyRenderedCollections.map(
+ ( [ namespace, collection ] ) => {
+ const collectionItems = itemsPerCollection[ namespace ];
+ if ( ! collectionItems || ! collectionItems.length ) {
return null;
}
+
return (
);
- } ) }
+ }
+ ) }
+
+ );
+}
- { didRenderAllCategories && uncategorizedItems.length > 0 && (
-
- ;
+ }
+
+ const itemsForCurrentRoot = [];
+ const itemsRemaining = [];
+
+ for ( const item of items ) {
+ // Skip reusable blocks, they moved to the patterns tab.
+ if ( item.category === 'reusable' ) {
+ continue;
+ }
+
+ if ( rootClientId && item.rootClientId === rootClientId ) {
+ itemsForCurrentRoot.push( item );
+ } else {
+ itemsRemaining.push( item );
+ }
+ }
+
+ return (
+
+
+ { !! itemsForCurrentRoot.length && (
+ <>
+
-
- ) }
-
- { currentlyRenderedCollections.map(
- ( [ namespace, collection ] ) => {
- const collectionItems = itemsPerCollection[ namespace ];
- if ( ! collectionItems || ! collectionItems.length ) {
- return null;
- }
-
- return (
-
-
-
- );
- }
+
+ >
) }
+
);
diff --git a/packages/block-editor/src/components/inserter/hooks/use-block-types-state.js b/packages/block-editor/src/components/inserter/hooks/use-block-types-state.js
index 566d0476fbd0f5..6b9e694c1cdf8f 100644
--- a/packages/block-editor/src/components/inserter/hooks/use-block-types-state.js
+++ b/packages/block-editor/src/components/inserter/hooks/use-block-types-state.js
@@ -14,20 +14,24 @@ import { useCallback } from '@wordpress/element';
* Internal dependencies
*/
import { store as blockEditorStore } from '../../../store';
+import { withRootClientIdOptionKey } from '../../../store/utils';
/**
* Retrieves the block types inserter state.
*
* @param {string=} rootClientId Insertion's root client ID.
* @param {Function} onInsert function called when inserter a list of blocks.
+ * @param {boolean} isQuick
* @return {Array} Returns the block types state. (block types, categories, collections, onSelect handler)
*/
-const useBlockTypesState = ( rootClientId, onInsert ) => {
+const useBlockTypesState = ( rootClientId, onInsert, isQuick ) => {
const [ items ] = useSelect(
( select ) => [
- select( blockEditorStore ).getInserterItems( rootClientId ),
+ select( blockEditorStore ).getInserterItems( rootClientId, {
+ [ withRootClientIdOptionKey ]: ! isQuick,
+ } ),
],
- [ rootClientId ]
+ [ rootClientId, isQuick ]
);
const [ categories, collections ] = useSelect( ( select ) => {
@@ -37,7 +41,14 @@ const useBlockTypesState = ( rootClientId, onInsert ) => {
const onSelectItem = useCallback(
(
- { name, initialAttributes, innerBlocks, syncStatus, content },
+ {
+ name,
+ initialAttributes,
+ innerBlocks,
+ syncStatus,
+ content,
+ rootClientId: _rootClientId,
+ },
shouldFocusBlock
) => {
const insertedBlock =
@@ -51,7 +62,12 @@ const useBlockTypesState = ( rootClientId, onInsert ) => {
createBlocksFromInnerBlocksTemplate( innerBlocks )
);
- onInsert( insertedBlock, undefined, shouldFocusBlock );
+ onInsert(
+ insertedBlock,
+ undefined,
+ shouldFocusBlock,
+ _rootClientId
+ );
},
[ onInsert ]
);
diff --git a/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js b/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js
index 0dae090578ab4f..24074ec5004565 100644
--- a/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js
+++ b/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js
@@ -1,7 +1,7 @@
/**
* WordPress dependencies
*/
-import { useDispatch, useSelect } from '@wordpress/data';
+import { useDispatch, useRegistry, useSelect } from '@wordpress/data';
import { isUnmodifiedDefaultBlock } from '@wordpress/blocks';
import { _n, sprintf } from '@wordpress/i18n';
import { speak } from '@wordpress/a11y';
@@ -13,6 +13,34 @@ import { useCallback } from '@wordpress/element';
import { store as blockEditorStore } from '../../../store';
import { unlock } from '../../../lock-unlock';
+function getIndex( {
+ destinationRootClientId,
+ destinationIndex,
+ rootClientId,
+ registry,
+} ) {
+ if ( rootClientId === destinationRootClientId ) {
+ return destinationIndex;
+ }
+ const parents = [
+ '',
+ ...registry
+ .select( blockEditorStore )
+ .getBlockParents( destinationRootClientId ),
+ destinationRootClientId,
+ ];
+ const parentIndex = parents.indexOf( rootClientId );
+ if ( parentIndex !== -1 ) {
+ return (
+ registry
+ .select( blockEditorStore )
+ .getBlockIndex( parents[ parentIndex + 1 ] ) + 1
+ );
+ }
+ return registry.select( blockEditorStore ).getBlockOrder( rootClientId )
+ .length;
+}
+
/**
* @typedef WPInserterConfig
*
@@ -42,6 +70,7 @@ function useInsertionPoint( {
shouldFocusBlock = true,
selectBlockOnInsert = true,
} ) {
+ const registry = useRegistry();
const { getSelectedBlock } = useSelect( blockEditorStore );
const { destinationRootClientId, destinationIndex } = useSelect(
( select ) => {
@@ -91,7 +120,7 @@ function useInsertionPoint( {
} = unlock( useDispatch( blockEditorStore ) );
const onInsertBlocks = useCallback(
- ( blocks, meta, shouldForceFocusBlock = false ) => {
+ ( blocks, meta, shouldForceFocusBlock = false, _rootClientId ) => {
// When we are trying to move focus or select a new block on insert, we also
// need to clear the last focus to avoid the focus being set to the wrong block
// when tabbing back into the canvas if the block was added from outside the
@@ -121,8 +150,17 @@ function useInsertionPoint( {
} else {
insertBlocks(
blocks,
- destinationIndex,
- destinationRootClientId,
+ isAppender || _rootClientId === undefined
+ ? destinationIndex
+ : getIndex( {
+ destinationRootClientId,
+ destinationIndex,
+ rootClientId: _rootClientId,
+ registry,
+ } ),
+ isAppender || _rootClientId === undefined
+ ? destinationRootClientId
+ : _rootClientId,
selectBlockOnInsert,
shouldFocusBlock || shouldForceFocusBlock ? 0 : null,
meta
@@ -154,9 +192,17 @@ function useInsertionPoint( {
);
const onToggleInsertionPoint = useCallback(
- ( show ) => {
- if ( show ) {
- showInsertionPoint( destinationRootClientId, destinationIndex );
+ ( item ) => {
+ if ( item?.hasOwnProperty( 'rootClientId' ) ) {
+ showInsertionPoint(
+ item.rootClientId,
+ getIndex( {
+ destinationRootClientId,
+ destinationIndex,
+ rootClientId: item.rootClientId,
+ registry,
+ } )
+ );
} else {
hideInsertionPoint();
}
diff --git a/packages/block-editor/src/components/inserter/menu.js b/packages/block-editor/src/components/inserter/menu.js
index 3abaee330ed22b..6a4ac798b74900 100644
--- a/packages/block-editor/src/components/inserter/menu.js
+++ b/packages/block-editor/src/components/inserter/menu.js
@@ -81,8 +81,13 @@ function InserterMenu(
const blockTypesTabRef = useRef();
const onInsert = useCallback(
- ( blocks, meta, shouldForceFocusBlock ) => {
- onInsertBlocks( blocks, meta, shouldForceFocusBlock );
+ ( blocks, meta, shouldForceFocusBlock, _rootClientId ) => {
+ onInsertBlocks(
+ blocks,
+ meta,
+ shouldForceFocusBlock,
+ _rootClientId
+ );
onSelect();
// Check for focus loss due to filtering blocks by selected block type
@@ -111,7 +116,7 @@ function InserterMenu(
const onHover = useCallback(
( item ) => {
- onToggleInsertionPoint( !! item );
+ onToggleInsertionPoint( item );
setHoveredItem( item );
},
[ onToggleInsertionPoint, setHoveredItem ]
diff --git a/packages/block-editor/src/components/inserter/quick-inserter.js b/packages/block-editor/src/components/inserter/quick-inserter.js
index 3405ac98b881cc..022957df952cea 100644
--- a/packages/block-editor/src/components/inserter/quick-inserter.js
+++ b/packages/block-editor/src/components/inserter/quick-inserter.js
@@ -44,7 +44,8 @@ export default function QuickInserter( {
} );
const [ blockTypes ] = useBlockTypesState(
destinationRootClientId,
- onInsertBlocks
+ onInsertBlocks,
+ true
);
const [ patterns ] = usePatternsState(
@@ -126,6 +127,7 @@ export default function QuickInserter( {
isDraggable={ false }
prioritizePatterns={ prioritizePatterns }
selectBlockOnInsert={ selectBlockOnInsert }
+ isQuick
/>
diff --git a/packages/block-editor/src/components/inserter/search-results.js b/packages/block-editor/src/components/inserter/search-results.js
index edd99609ea916c..9c001823745e6c 100644
--- a/packages/block-editor/src/components/inserter/search-results.js
+++ b/packages/block-editor/src/components/inserter/search-results.js
@@ -50,6 +50,7 @@ function InserterSearchResults( {
shouldFocusBlock = true,
prioritizePatterns,
selectBlockOnInsert,
+ isQuick,
} ) {
const debouncedSpeak = useDebounce( speak, 500 );
@@ -80,7 +81,7 @@ function InserterSearchResults( {
blockTypeCategories,
blockTypeCollections,
onSelectBlockType,
- ] = useBlockTypesState( destinationRootClientId, onInsertBlocks );
+ ] = useBlockTypesState( destinationRootClientId, onInsertBlocks, isQuick );
const [ patterns, , onClickPattern ] = usePatternsState(
onInsertBlocks,
destinationRootClientId
diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js
index 1c685eb4230ba4..bf7b5125a770e6 100644
--- a/packages/block-editor/src/store/selectors.js
+++ b/packages/block-editor/src/store/selectors.js
@@ -22,6 +22,7 @@ import { createSelector, createRegistrySelector } from '@wordpress/data';
* Internal dependencies
*/
import {
+ withRootClientIdOptionKey,
checkAllowListRecursive,
checkAllowList,
getAllPatternsDependants,
@@ -1995,7 +1996,7 @@ const buildBlockTypeItem =
*/
export const getInserterItems = createRegistrySelector( ( select ) =>
createSelector(
- ( state, rootClientId = null ) => {
+ ( state, rootClientId = null, options = {} ) => {
const buildReusableBlockInserterItem = ( reusableBlock ) => {
const icon = ! reusableBlock.wp_pattern_sync_status
? {
@@ -2037,16 +2038,73 @@ export const getInserterItems = createRegistrySelector( ( select ) =>
buildScope: 'inserter',
} );
- const blockTypeInserterItems = getBlockTypes()
+ let blockTypeInserterItems = getBlockTypes()
.filter( ( blockType ) =>
- canIncludeBlockTypeInInserter(
- state,
- blockType,
- rootClientId
- )
+ hasBlockSupport( blockType, 'inserter', true )
)
.map( buildBlockTypeInserterItem );
+ if ( options[ withRootClientIdOptionKey ] ) {
+ blockTypeInserterItems = blockTypeInserterItems.reduce(
+ ( accumulator, item ) => {
+ item.rootClientId = rootClientId ?? '';
+
+ while (
+ ! canInsertBlockTypeUnmemoized(
+ state,
+ item.name,
+ item.rootClientId
+ )
+ ) {
+ if ( ! item.rootClientId ) {
+ let sectionRootClientId;
+ try {
+ sectionRootClientId = unlock(
+ getSettings( state )
+ ).sectionRootClientId;
+ } catch ( e ) {}
+ if (
+ sectionRootClientId &&
+ canInsertBlockTypeUnmemoized(
+ state,
+ item.name,
+ sectionRootClientId
+ )
+ ) {
+ item.rootClientId = sectionRootClientId;
+ } else {
+ delete item.rootClientId;
+ }
+ break;
+ } else {
+ const parentClientId = getBlockRootClientId(
+ state,
+ item.rootClientId
+ );
+ item.rootClientId = parentClientId;
+ }
+ }
+
+ // We could also add non insertable items and gray them out.
+ if ( item.hasOwnProperty( 'rootClientId' ) ) {
+ accumulator.push( item );
+ }
+
+ return accumulator;
+ },
+ []
+ );
+ } else {
+ blockTypeInserterItems = blockTypeInserterItems.filter(
+ ( blockType ) =>
+ canIncludeBlockTypeInInserter(
+ state,
+ blockType,
+ rootClientId
+ )
+ );
+ }
+
const items = blockTypeInserterItems.reduce(
( accumulator, item ) => {
const { variations = [] } = item;
diff --git a/packages/block-editor/src/store/utils.js b/packages/block-editor/src/store/utils.js
index f236c4a7e56eb8..c94453e99c60a4 100644
--- a/packages/block-editor/src/store/utils.js
+++ b/packages/block-editor/src/store/utils.js
@@ -5,6 +5,8 @@ import { selectBlockPatternsKey } from './private-keys';
import { unlock } from '../lock-unlock';
import { STORE_NAME } from './constants';
+export const withRootClientIdOptionKey = Symbol( 'withRootClientId' );
+
export const checkAllowList = ( list, item, defaultResult = null ) => {
if ( typeof list === 'boolean' ) {
return list;
diff --git a/test/e2e/specs/editor/blocks/columns.spec.js b/test/e2e/specs/editor/blocks/columns.spec.js
index 8ddf7e9377ff20..e322a52eeba10b 100644
--- a/test/e2e/specs/editor/blocks/columns.spec.js
+++ b/test/e2e/specs/editor/blocks/columns.spec.js
@@ -40,7 +40,7 @@ test.describe( 'Columns', () => {
// Verify Column
const inserterOptions = page.locator(
- 'role=region[name="Block Library"i] >> role=option'
+ 'role=region[name="Block Library"i] >> .block-editor-inserter__insertable-blocks-at-selection >> role=option'
);
await expect( inserterOptions ).toHaveCount( 1 );
await expect( inserterOptions ).toHaveText( 'Column' );
diff --git a/test/e2e/specs/editor/plugins/child-blocks.spec.js b/test/e2e/specs/editor/plugins/child-blocks.spec.js
index b3073b70a5409a..0cd043c6a46105 100644
--- a/test/e2e/specs/editor/plugins/child-blocks.spec.js
+++ b/test/e2e/specs/editor/plugins/child-blocks.spec.js
@@ -48,9 +48,13 @@ test.describe( 'Child Blocks', () => {
const blockInserter = page
.getByRole( 'toolbar', { name: 'Document tools' } )
.getByRole( 'button', { name: 'Toggle block inserter' } );
- const blockLibrary = page.getByRole( 'region', {
- name: 'Block Library',
- } );
+ const blockLibrary = page
+ .getByRole( 'region', {
+ name: 'Block Library',
+ } )
+ .locator(
+ '.block-editor-inserter__insertable-blocks-at-selection'
+ );
await blockInserter.click();
await expect( blockLibrary ).toBeVisible();
@@ -82,9 +86,13 @@ test.describe( 'Child Blocks', () => {
const blockInserter = page
.getByRole( 'toolbar', { name: 'Document tools' } )
.getByRole( 'button', { name: 'Toggle block inserter' } );
- const blockLibrary = page.getByRole( 'region', {
- name: 'Block Library',
- } );
+ const blockLibrary = page
+ .getByRole( 'region', {
+ name: 'Block Library',
+ } )
+ .locator(
+ '.block-editor-inserter__insertable-blocks-at-selection'
+ );
await blockInserter.click();
await expect( blockLibrary ).toBeVisible();
diff --git a/test/e2e/specs/editor/plugins/inner-blocks-allowed-blocks.spec.js b/test/e2e/specs/editor/plugins/inner-blocks-allowed-blocks.spec.js
index eaf171adf9313c..d2dc521f0196bd 100644
--- a/test/e2e/specs/editor/plugins/inner-blocks-allowed-blocks.spec.js
+++ b/test/e2e/specs/editor/plugins/inner-blocks-allowed-blocks.spec.js
@@ -46,9 +46,13 @@ test.describe( 'Allowed Blocks Setting on InnerBlocks', () => {
const blockInserter = page
.getByRole( 'toolbar', { name: 'Document tools' } )
.getByRole( 'button', { name: 'Toggle block inserter' } );
- const blockLibrary = page.getByRole( 'region', {
- name: 'Block Library',
- } );
+ const blockLibrary = page
+ .getByRole( 'region', {
+ name: 'Block Library',
+ } )
+ .locator(
+ '.block-editor-inserter__insertable-blocks-at-selection'
+ );
await blockInserter.click();
await expect( blockLibrary ).toBeVisible();
@@ -89,9 +93,13 @@ test.describe( 'Allowed Blocks Setting on InnerBlocks', () => {
const blockInserter = page
.getByRole( 'toolbar', { name: 'Document tools' } )
.getByRole( 'button', { name: 'Toggle block inserter' } );
- const blockLibrary = page.getByRole( 'region', {
- name: 'Block Library',
- } );
+ const blockLibrary = page
+ .getByRole( 'region', {
+ name: 'Block Library',
+ } )
+ .locator(
+ '.block-editor-inserter__insertable-blocks-at-selection'
+ );
await blockInserter.click();
await expect( blockLibrary ).toBeVisible();