@wordpress/editor
Version:
Enhanced block editor for WordPress posts.
192 lines (176 loc) • 5.72 kB
JavaScript
/**
* WordPress dependencies
*/
import { Flex, FlexItem, Modal, CheckboxControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { useState, useMemo, useEffect } from '@wordpress/element';
import {
store as blockEditorStore,
__experimentalBlockPatternsList as BlockPatternsList,
} from '@wordpress/block-editor';
import { useSelect, useDispatch } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';
import { __unstableSerializeAndClean } from '@wordpress/blocks';
import { store as preferencesStore } from '@wordpress/preferences';
import { store as interfaceStore } from '@wordpress/interface';
/**
* Internal dependencies
*/
import {
ATTACHMENT_POST_TYPE,
TEMPLATE_POST_TYPE,
TEMPLATE_PART_POST_TYPE,
} from '../../store/constants';
import { store as editorStore } from '../../store';
export function useStartPatterns() {
// A pattern is a start pattern if it includes 'core/post-content' in its blockTypes,
// and it has no postTypes declared and the current post type is page or if
// the current post type is part of the postTypes declared.
const { blockPatternsWithPostContentBlockType, postType } = useSelect(
( select ) => {
const { getPatternsByBlockTypes, getBlocksByName } =
select( blockEditorStore );
const { getCurrentPostType, getRenderingMode } =
select( editorStore );
const rootClientId =
getRenderingMode() === 'post-only'
? ''
: getBlocksByName( 'core/post-content' )?.[ 0 ];
return {
blockPatternsWithPostContentBlockType: getPatternsByBlockTypes(
'core/post-content',
rootClientId
),
postType: getCurrentPostType(),
};
},
[]
);
return useMemo( () => {
if ( ! blockPatternsWithPostContentBlockType?.length ) {
return [];
}
/*
* Filter patterns without postTypes declared if the current postType is page
* or patterns that declare the current postType in its post type array.
*/
return blockPatternsWithPostContentBlockType.filter( ( pattern ) => {
return (
( postType === 'page' && ! pattern.postTypes ) ||
( Array.isArray( pattern.postTypes ) &&
pattern.postTypes.includes( postType ) )
);
} );
}, [ postType, blockPatternsWithPostContentBlockType ] );
}
function PatternSelection( { blockPatterns, onChoosePattern } ) {
const { editEntityRecord } = useDispatch( coreStore );
const { postType, postId } = useSelect( ( select ) => {
const { getCurrentPostType, getCurrentPostId } = select( editorStore );
return {
postType: getCurrentPostType(),
postId: getCurrentPostId(),
};
}, [] );
return (
<BlockPatternsList
blockPatterns={ blockPatterns }
onClickPattern={ ( _pattern, blocks ) => {
editEntityRecord( 'postType', postType, postId, {
blocks,
content: ( { blocks: blocksForSerialization = [] } ) =>
__unstableSerializeAndClean( blocksForSerialization ),
} );
onChoosePattern();
} }
/>
);
}
function StartPageOptionsModal( { onClose } ) {
const [ showStartPatterns, setShowStartPatterns ] = useState( true );
const { set: setPreference } = useDispatch( preferencesStore );
const startPatterns = useStartPatterns();
const hasStartPattern = startPatterns.length > 0;
if ( ! hasStartPattern ) {
return null;
}
function handleClose() {
onClose();
setPreference( 'core', 'enableChoosePatternModal', showStartPatterns );
}
return (
<Modal
className="editor-start-page-options__modal"
title={ __( 'Choose a pattern' ) }
isFullScreen
onRequestClose={ handleClose }
>
<div className="editor-start-page-options__modal-content">
<PatternSelection
blockPatterns={ startPatterns }
onChoosePattern={ handleClose }
/>
</div>
<Flex
className="editor-start-page-options__modal__actions"
justify="flex-start"
expanded={ false }
>
<FlexItem>
<CheckboxControl
checked={ showStartPatterns }
label={ __(
'Always show starter patterns for new pages'
) }
onChange={ ( newValue ) => {
setShowStartPatterns( newValue );
} }
/>
</FlexItem>
</Flex>
</Modal>
);
}
export default function StartPageOptions() {
const [ isOpen, setIsOpen ] = useState( false );
const { isEditedPostDirty, isEditedPostEmpty } = useSelect( editorStore );
const { isModalActive } = useSelect( interfaceStore );
const { enabled, postId } = useSelect( ( select ) => {
const { getCurrentPostId, getCurrentPostType } = select( editorStore );
const choosePatternModalEnabled = select( preferencesStore ).get(
'core',
'enableChoosePatternModal'
);
const currentPostType = getCurrentPostType();
return {
postId: getCurrentPostId(),
enabled:
choosePatternModalEnabled &&
ATTACHMENT_POST_TYPE !== currentPostType &&
TEMPLATE_POST_TYPE !== currentPostType &&
TEMPLATE_PART_POST_TYPE !== currentPostType,
};
}, [] );
// Note: The `postId` ensures the effect re-runs when pages are switched without remounting the component.
// Examples: changing pages in the List View, creating a new page via Command Palette.
useEffect( () => {
const isFreshPage = ! isEditedPostDirty() && isEditedPostEmpty();
// Prevents immediately opening when features is enabled via preferences modal.
const isPreferencesModalActive = isModalActive( 'editor/preferences' );
if ( ! enabled || ! isFreshPage || isPreferencesModalActive ) {
return;
}
// Open the modal after the initial render for a new page.
setIsOpen( true );
}, [
enabled,
postId,
isEditedPostDirty,
isEditedPostEmpty,
isModalActive,
] );
if ( ! isOpen ) {
return null;
}
return <StartPageOptionsModal onClose={ () => setIsOpen( false ) } />;
}