/
githubmirror
/
gutenberg
Обзор
Документация
Войти
/
githubmirror
/
gutenberg
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
trunk
packages/patterns/src/components/pattern-convert-button.js
188 строк
6 KB
Marco Ciampini
ESLint: Replace strict config with bulk suppressions (#81248)
07 авг 2026, 14:34
Не верифицирован
07 авг 2026, 14:34
7f43eaf
Код
Авторство
О чём код?
import { hasBlockSupport, isReusableBlock, createBlock, serialize, getBlockType, } from '@wordpress/blocks'; import { store as blockEditorStore } from '@wordpress/block-editor'; import { useState, useCallback } from '@wordpress/element'; import { MenuItem } from '@wordpress/components'; import { symbol } from '@wordpress/icons'; import { useSelect, useDispatch } from '@wordpress/data'; import { store as coreStore } from '@wordpress/core-data'; import { __, sprintf } from '@wordpress/i18n'; import { store as noticesStore } from '@wordpress/notices'; import { store as patternsStore } from '../store'; import CreatePatternModal from './create-pattern-modal'; import { unlock } from '../lock-unlock'; import { PATTERN_SYNC_TYPES } from '../constants'; /** * Menu control to convert block(s) to a pattern block. * * @param {Object} props Component props. * @param {string[]} props.clientIds Client ids of selected blocks. * @param {string} props.rootClientId ID of the currently selected top-level block. * @param {()=>void} props.closeBlockSettingsMenu Callback to close the block settings menu dropdown. * @return {React.ComponentType} The menu control or null. */ export default function PatternConvertButton( { clientIds, rootClientId, closeBlockSettingsMenu, } ) { const { createSuccessNotice } = useDispatch( noticesStore ); const { replaceBlocks, updateBlockAttributes } = useDispatch( blockEditorStore ); // Ignore reason: false positive of the lint rule. // eslint-disable-next-line @wordpress/no-unused-vars-before-return const { setEditingPattern } = unlock( useDispatch( patternsStore ) ); const [ isModalOpen, setIsModalOpen ] = useState( false ); const { getBlockAttributes } = useSelect( blockEditorStore ); const canConvert = useSelect( ( select ) => { const { canUser } = select( coreStore ); const { getBlocksByClientId, canInsertBlockType, getBlockRootClientId, } = select( blockEditorStore ); const rootId = rootClientId || ( clientIds.length > 0 ? getBlockRootClientId( clientIds[ 0 ] ) : undefined ); const blocks = getBlocksByClientId( clientIds ) ?? []; // Check if the block has reusable support defined. const hasReusableBlockSupport = ( blockName ) => { const blockType = getBlockType( blockName ); const hasParent = blockType && 'parent' in blockType; // If the block has a parent, check with false as default, otherwise with true. return hasBlockSupport( blockName, 'reusable', ! hasParent ); }; const isSyncedPattern = blocks.length === 1 && blocks[ 0 ] && isReusableBlock( blocks[ 0 ] ) && !! select( coreStore ).getEntityRecord( 'postType', 'wp_block', blocks[ 0 ].attributes.ref ); const isUnsyncedPattern = blocks.length === 1 && blocks?.[ 0 ]?.attributes?.metadata?.patternName; const _canConvert = // Hide when this is already a pattern. ! isUnsyncedPattern && ! isSyncedPattern && // Hide when patterns are disabled. canInsertBlockType( 'core/block', rootId ) && blocks.every( ( block ) => // Guard against the case where a regular block has *just* been converted. !! block && // Hide on invalid blocks. block.isValid && // Hide when block doesn't support being made into a pattern. hasReusableBlockSupport( block.name ) ) && // Hide when current doesn't have permission to do that. // Blocks refers to the wp_block post type, this checks the ability to create a post of that type. !! canUser( 'create', { kind: 'postType', name: 'wp_block', } ); return _canConvert; }, [ clientIds, rootClientId ] ); const { getBlocksByClientId } = useSelect( blockEditorStore ); const getContent = useCallback( () => serialize( getBlocksByClientId( clientIds ) ), [ getBlocksByClientId, clientIds ] ); if ( ! canConvert ) { return null; } const handleSuccess = ( { pattern } ) => { if ( pattern.wp_pattern_sync_status === PATTERN_SYNC_TYPES.unsynced ) { if ( clientIds?.length === 1 ) { const existingAttributes = getBlockAttributes( clientIds[ 0 ] ); updateBlockAttributes( clientIds[ 0 ], { metadata: { ...( existingAttributes?.metadata ? existingAttributes.metadata : {} ), patternName: `core/block/${ pattern.id }`, name: pattern.title.raw, }, } ); } } else { const newBlock = createBlock( 'core/block', { ref: pattern.id, } ); replaceBlocks( clientIds, newBlock ); setEditingPattern( newBlock.clientId, true ); } createSuccessNotice( pattern.wp_pattern_sync_status === PATTERN_SYNC_TYPES.unsynced ? sprintf( // translators: %s: the name the user has given to the pattern. __( 'Unsynced pattern created: %s' ), pattern.title.raw ) : sprintf( // translators: %s: the name the user has given to the pattern. __( 'Synced pattern created: %s' ), pattern.title.raw ), { type: 'snackbar', id: 'convert-to-pattern-success', } ); setIsModalOpen( false ); closeBlockSettingsMenu(); }; return ( <> <MenuItem icon={ symbol } onClick={ () => setIsModalOpen( true ) } aria-expanded={ isModalOpen } aria-haspopup="dialog" > { __( 'Create pattern' ) } </MenuItem> { isModalOpen && ( <CreatePatternModal content={ getContent } onSuccess={ ( pattern ) => { handleSuccess( pattern ); } } onError={ () => { setIsModalOpen( false ); } } onClose={ () => { setIsModalOpen( false ); } } /> ) } </> ); }