Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Insert parsed patterns via the slash inserter
  • Loading branch information
talldan committed Sep 10, 2025
commit a418ac255053aa849bcb8f961ce5e76ab0bda84b
17 changes: 6 additions & 11 deletions packages/block-editor/src/autocompleters/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
*/
import { useSelect } from '@wordpress/data';
import {
cloneBlock,
createBlock,
createBlocksFromInnerBlocksTemplate,
parse,
store as blocksStore,
} from '@wordpress/blocks';
import { useMemo } from '@wordpress/element';
Expand Down Expand Up @@ -125,21 +125,16 @@ function createBlockCompleter() {
return ! ( /\S/.test( before ) || /\S/.test( after ) );
},
getOptionCompletion( inserterItem ) {
const {
name,
initialAttributes,
innerBlocks,
syncStatus,
content,
} = inserterItem;
const { name, initialAttributes, innerBlocks, syncStatus, blocks } =
inserterItem;

return {
action: 'replace',
value:
syncStatus === 'unsynced'
? parse( content, {
__unstableSkipMigrationLogs: true,
} )
? ( blocks ?? [] ).map( ( block ) =>
cloneBlock( block )
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is working well now!

One thing that would also be good to fix is having theme patterns appear in the slash inserter; currently they don't. But that can be addressed separately!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, lets treat is as separate, I'm not sure if there's a reason they're omitted.

If we can include them I think it would help tidy up some of the Inserter code.

: createBlock(
name,
initialAttributes,
Expand Down
22 changes: 1 addition & 21 deletions packages/block-editor/src/store/private-selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ import {
getAllPatternsDependants,
getInsertBlockTypeDependants,
getGrammar,
mapUserPattern,
} from './utils';
import { INSERTER_PATTERN_TYPES } from '../components/inserter/block-patterns-tab/utils';
import { STORE_NAME } from './constants';
import { unlock } from '../lock-unlock';
import {
Expand Down Expand Up @@ -349,26 +349,6 @@ export const hasAllowedPatterns = createRegistrySelector( ( select ) =>
)
);

function mapUserPattern(
userPattern,
__experimentalUserPatternCategories = []
) {
return {
name: `core/block/${ userPattern.id }`,
id: userPattern.id,
type: INSERTER_PATTERN_TYPES.user,
title: userPattern.title.raw,
categories: userPattern.wp_pattern_category?.map( ( catId ) => {
const category = __experimentalUserPatternCategories.find(
( { id } ) => id === catId
);
return category ? category.slug : catId;
} ),
content: userPattern.content.raw,
syncStatus: userPattern.wp_pattern_sync_status,
};
}

export const getPatternBySlug = createRegistrySelector( ( select ) =>
createSelector(
( state, patternName ) => {
Expand Down
21 changes: 13 additions & 8 deletions packages/block-editor/src/store/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
getInsertBlockTypeDependants,
getParsedPattern,
getGrammar,
mapUserPattern,
} from './utils';
import { orderBy } from '../utils/sorting';
import { STORE_NAME } from './constants';
Expand Down Expand Up @@ -2155,27 +2156,31 @@ export const getInserterItems = createRegistrySelector( ( select ) =>
foreground: 'var(--wp-block-synced-color)',
}
: symbol;
const id = `core/block/${ reusableBlock.id }`;
const { time, count = 0 } = getInsertUsage( state, id ) || {};
const userPattern = mapUserPattern( reusableBlock );
const { time, count = 0 } =
getInsertUsage( state, userPattern.name ) || {};
const frecency = calculateFrecency( time, count );

return {
id,
id: userPattern.name,
name: 'core/block',
initialAttributes: { ref: reusableBlock.id },
title: reusableBlock.title?.raw,
title: userPattern.title,
icon,
category: 'reusable',
keywords: [ 'reusable' ],
isDisabled: false,
utility: 1, // Deprecated.
frecency,
content: reusableBlock.content?.raw,
syncStatus: reusableBlock.wp_pattern_sync_status,
content: userPattern.content,
get blocks() {
return getParsedPattern( userPattern ).blocks;
},
syncStatus: userPattern.syncStatus,
};
};

const syncedPatternInserterItems = canInsertBlockTypeUnmemoized(
const patternInserterItems = canInsertBlockTypeUnmemoized(
state,
'core/block',
rootClientId
Expand Down Expand Up @@ -2261,7 +2266,7 @@ export const getInserterItems = createRegistrySelector( ( select ) =>
{ core: [], noncore: [] }
);
const sortedBlockTypes = [ ...coreItems, ...nonCoreItems ];
return [ ...sortedBlockTypes, ...syncedPatternInserterItems ];
return [ ...sortedBlockTypes, ...patternInserterItems ];
},
( state, rootClientId ) => [
getBlockTypes(),
Expand Down
21 changes: 21 additions & 0 deletions packages/block-editor/src/store/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,32 @@ import { selectBlockPatternsKey } from './private-keys';
import { unlock } from '../lock-unlock';
import { STORE_NAME } from './constants';
import { getSectionRootClientId } from './private-selectors';
import { INSERTER_PATTERN_TYPES } from '../components/inserter/block-patterns-tab/utils';

export const isFiltered = Symbol( 'isFiltered' );
const parsedPatternCache = new WeakMap();
const grammarMapCache = new WeakMap();

export function mapUserPattern(
userPattern,
__experimentalUserPatternCategories = []
) {
return {
name: `core/block/${ userPattern.id }`,
id: userPattern.id,
type: INSERTER_PATTERN_TYPES.user,
title: userPattern.title.raw,
categories: userPattern.wp_pattern_category?.map( ( catId ) => {
const category = __experimentalUserPatternCategories.find(
( { id } ) => id === catId
);
return category ? category.slug : catId;
} ),
content: userPattern.content.raw,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tiniest of nits, but will content always be truthy? In getInserterItems (before this PR) the accessing of both content and title use optional chaining, whereas this function doesn't.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it's interesting. Does it mean there are patterns without a title or content?

I added the optional chaining anyway to be safe 😅

syncStatus: userPattern.wp_pattern_sync_status,
};
}

function parsePattern( pattern ) {
const blocks = parse( pattern.content, {
__unstableSkipMigrationLogs: true,
Expand Down