@wordpress/editor
Version:
Enhanced block editor for WordPress posts.
134 lines (121 loc) • 3.94 kB
JavaScript
/**
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data';
import { useMemo } from '@wordpress/element';
import { store as coreStore } from '@wordpress/core-data';
import { parse } from '@wordpress/blocks';
import { privateApis as patternsPrivateApis } from '@wordpress/patterns';
/**
* Internal dependencies
*/
import { unlock } from '../../lock-unlock';
import { store as editorStore } from '../../store';
const { EXCLUDED_PATTERN_SOURCES, PATTERN_TYPES } =
unlock( patternsPrivateApis );
function injectThemeAttributeInBlockTemplateContent(
block,
currentThemeStylesheet
) {
block.innerBlocks = block.innerBlocks.map( ( innerBlock ) => {
return injectThemeAttributeInBlockTemplateContent(
innerBlock,
currentThemeStylesheet
);
} );
if (
block.name === 'core/template-part' &&
block.attributes.theme === undefined
) {
block.attributes.theme = currentThemeStylesheet;
}
return block;
}
/**
* Filter all patterns and return only the ones that are compatible with the current template.
*
* @param {Array} patterns An array of patterns.
* @param {Object} template The current template. Required values are `area`, `name`, and `slug`.
* @return {Array} Array of patterns that are compatible with the current template.
*/
function filterPatterns( patterns, template ) {
// Filter out duplicates.
const filterOutDuplicatesByName = ( currentItem, index, items ) =>
index === items.findIndex( ( item ) => currentItem.name === item.name );
// Filter out core/directory patterns not included in theme.json.
// Exception: navigation-overlay patterns should always show core patterns.
// We only want them to show here, we want them excluded everywhere else
// to avoid showing them in the inserter or the patterns page.
const filterOutExcludedPatternSources = ( pattern ) => {
if (
template.area === 'navigation-overlay' &&
pattern.blockTypes?.includes(
'core/template-part/navigation-overlay'
)
) {
return true;
}
return ! EXCLUDED_PATTERN_SOURCES.includes( pattern.source );
};
// Looks for patterns that have the same template type as the current template,
// or have a block type that matches the current template area.
const filterCompatiblePatterns = ( pattern ) =>
pattern.templateTypes?.includes( template.slug ) ||
pattern.blockTypes?.includes( 'core/template-part/' + template.area );
return patterns.filter( ( pattern, index, items ) => {
return (
filterOutDuplicatesByName( pattern, index, items ) &&
filterOutExcludedPatternSources( pattern ) &&
filterCompatiblePatterns( pattern )
);
} );
}
function preparePatterns( patterns, currentThemeStylesheet ) {
return patterns.map( ( pattern ) => ( {
...pattern,
keywords: pattern.keywords || [],
type: PATTERN_TYPES.theme,
blocks: parse( pattern.content, {
__unstableSkipMigrationLogs: true,
} ).map( ( block ) =>
injectThemeAttributeInBlockTemplateContent(
block,
currentThemeStylesheet
)
),
} ) );
}
export function useAvailablePatterns( { area, name, slug } ) {
const { blockPatterns, restBlockPatterns, currentThemeStylesheet } =
useSelect( ( select ) => {
const { getEditorSettings } = select( editorStore );
const settings = getEditorSettings();
return {
blockPatterns:
settings.__experimentalAdditionalBlockPatterns ??
settings.__experimentalBlockPatterns,
restBlockPatterns: select( coreStore ).getBlockPatterns(),
currentThemeStylesheet:
select( coreStore ).getCurrentTheme().stylesheet,
};
}, [] );
return useMemo( () => {
const mergedPatterns = [
...( blockPatterns || [] ),
...( restBlockPatterns || [] ),
];
const filteredPatterns = filterPatterns( mergedPatterns, {
area,
name,
slug,
} );
return preparePatterns( filteredPatterns, currentThemeStylesheet );
}, [
area,
name,
slug,
blockPatterns,
restBlockPatterns,
currentThemeStylesheet,
] );
}