diff --git a/packages/block-editor/README.md b/packages/block-editor/README.md index 9c7a72f0897143..b7ff6ab7b6e791 100644 --- a/packages/block-editor/README.md +++ b/packages/block-editor/README.md @@ -763,6 +763,10 @@ _Related_ - +### StarterPatternsModal + +Undocumented declaration. + ### store Store definition for the block editor namespace. diff --git a/packages/block-editor/src/components/index.js b/packages/block-editor/src/components/index.js index 08247d8cdb014a..b59715f28617fc 100644 --- a/packages/block-editor/src/components/index.js +++ b/packages/block-editor/src/components/index.js @@ -143,6 +143,7 @@ export { useMouseMoveTypingReset as __unstableUseMouseMoveTypingReset, } from './observe-typing'; export { default as SkipToSelectedBlock } from './skip-to-selected-block'; +export { default as StarterPatternsModal } from './starter-patterns-modal'; export { default as Typewriter, useTypewriter as __unstableUseTypewriter, diff --git a/packages/block-editor/src/components/starter-patterns-modal/index.js b/packages/block-editor/src/components/starter-patterns-modal/index.js new file mode 100644 index 00000000000000..e084af9604f24f --- /dev/null +++ b/packages/block-editor/src/components/starter-patterns-modal/index.js @@ -0,0 +1,70 @@ +/** + * WordPress dependencies + */ +import { Modal } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; +import { useMemo } from '@wordpress/element'; +import { useSelect } from '@wordpress/data'; +import { useAsyncList } from '@wordpress/compose'; + +/** + * Internal dependencies + */ +import { store as blockEditorStore } from '../../store'; +import { __experimentalBlockPatternsList as BlockPatternsList } from '../'; + +function useStarterPatterns( postType, rootClientId ) { + // 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 = useSelect( + ( select ) => + select( blockEditorStore ).getPatternsByBlockTypes( + 'core/post-content', + rootClientId + ), + [ rootClientId ] + ); + + return useMemo( () => { + // 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 ] ); +} + +export default function StarterPatternsModal( { + onChoosePattern, + onRequestClose, + postType, + rootClientId, +} ) { + const starterPatterns = useStarterPatterns( postType, rootClientId ); + const shownStarterPatterns = useAsyncList( starterPatterns ); + + if ( starterPatterns.length === 0 ) { + return null; + } + + return ( + +
+ +
+
+ ); +} diff --git a/packages/block-editor/src/components/starter-patterns-modal/style.scss b/packages/block-editor/src/components/starter-patterns-modal/style.scss new file mode 100644 index 00000000000000..f3db5c516bc5b1 --- /dev/null +++ b/packages/block-editor/src/components/starter-patterns-modal/style.scss @@ -0,0 +1,26 @@ +// 2 column masonry layout. +.block-editor-starter-patterns-modal__content .block-editor-block-patterns-list { + column-count: 2; + column-gap: $grid-unit-30; + + @include break-medium() { + column-count: 3; + } + + @include break-wide() { + column-count: 4; + } + + .block-editor-block-patterns-list__list-item { + break-inside: avoid-column; + margin-bottom: $grid-unit-30; + + .block-editor-block-preview__container { + min-height: 100px; + } + + .block-editor-block-preview__content { + width: 100%; + } + } +} diff --git a/packages/block-editor/src/style.scss b/packages/block-editor/src/style.scss index 93ab3b69a7aad3..b56660d0f28921 100644 --- a/packages/block-editor/src/style.scss +++ b/packages/block-editor/src/style.scss @@ -41,6 +41,7 @@ @import "./components/responsive-block-control/style.scss"; @import "./components/rich-text/style.scss"; @import "./components/skip-to-selected-block/style.scss"; +@import "./components/starter-patterns-modal/style.scss"; @import "./components/text-decoration-control/style.scss"; @import "./components/text-transform-control/style.scss"; @import "./components/tool-selector/style.scss"; diff --git a/packages/edit-post/src/components/start-page-options/index.js b/packages/edit-post/src/components/start-page-options/index.js index 77264d27a5e7df..c1a6e3a548883f 100644 --- a/packages/edit-post/src/components/start-page-options/index.js +++ b/packages/edit-post/src/components/start-page-options/index.js @@ -1,108 +1,44 @@ /** * WordPress dependencies */ -import { Modal } from '@wordpress/components'; -import { __ } from '@wordpress/i18n'; -import { useState, useMemo } from '@wordpress/element'; -import { - store as blockEditorStore, - __experimentalBlockPatternsList as BlockPatternsList, -} from '@wordpress/block-editor'; import { useSelect, useDispatch } from '@wordpress/data'; -import { useAsyncList } from '@wordpress/compose'; import { store as editorStore } from '@wordpress/editor'; +import { useState } from '@wordpress/element'; +import { StarterPatternsModal } from '@wordpress/block-editor'; /** * Internal dependencies */ import { store as editPostStore } from '../../store'; -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 } = select( blockEditorStore ); - const { getCurrentPostType } = select( editorStore ); - return { - blockPatternsWithPostContentBlockType: - getPatternsByBlockTypes( 'core/post-content' ), - postType: getCurrentPostType(), - }; - }, - [] - ); - - return useMemo( () => { - // 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 shownBlockPatterns = useAsyncList( blockPatterns ); - const { resetEditorBlocks } = useDispatch( editorStore ); - return ( - { - resetEditorBlocks( blocks ); - onChoosePattern(); - } } - /> - ); -} - -function StartPageOptionsModal( { onClose } ) { - const startPatterns = useStartPatterns(); - const hasStartPattern = startPatterns.length > 0; - - if ( ! hasStartPattern ) { - return null; - } - - return ( - -
- -
-
- ); -} - export default function StartPageOptions() { const [ isClosed, setIsClosed ] = useState( false ); - const shouldEnableModal = useSelect( ( select ) => { - const { isCleanNewPost } = select( editorStore ); + const { resetEditorBlocks } = useDispatch( editorStore ); + const { shouldEnableModal, postType } = useSelect( ( select ) => { + const { isCleanNewPost, getCurrentPostType } = select( editorStore ); const { isEditingTemplate, isFeatureActive } = select( editPostStore ); - return ( - ! isEditingTemplate() && - ! isFeatureActive( 'welcomeGuide' ) && - isCleanNewPost() - ); + return { + shouldEnableModal: + ! isEditingTemplate() && + ! isFeatureActive( 'welcomeGuide' ) && + isCleanNewPost(), + postType: getCurrentPostType(), + }; }, [] ); if ( ! shouldEnableModal || isClosed ) { return null; } - return setIsClosed( true ) } />; + return ( + { + resetEditorBlocks( blocks ); + setIsClosed( true ); + } } + onRequestClose={ () => setIsClosed( true ) } + /> + ); } diff --git a/packages/edit-site/src/components/editor/index.js b/packages/edit-site/src/components/editor/index.js index 70d033d14188c7..8860207ae28ae9 100644 --- a/packages/edit-site/src/components/editor/index.js +++ b/packages/edit-site/src/components/editor/index.js @@ -37,6 +37,7 @@ import InserterSidebar from '../secondary-sidebar/inserter-sidebar'; import ListViewSidebar from '../secondary-sidebar/list-view-sidebar'; import WelcomeGuide from '../welcome-guide'; import StartTemplateOptions from '../start-template-options'; +import StartPageOptions from '../start-page-options'; import { store as editSiteStore } from '../../store'; import { GlobalStylesRenderer } from '../global-styles-renderer'; import useTitle from '../routes/use-title'; @@ -194,6 +195,7 @@ export default function Editor( { listViewToggleElement, isLoading } ) { { isEditMode && } + { isEditMode && } { + const { hasPageContentFocus, getEditedPostContext } = + select( editSiteStore ); + const context = getEditedPostContext(); + const isEditingPage = + context?.postType === 'page' && + context?.postId && + hasPageContentFocus(); + const isWelcomeGuideOpen = select( preferencesStore ).get( + 'core/edit-site', + 'welcomeGuide' + ); + if ( ! isEditingPage || isWelcomeGuideOpen ) { + return { shouldOpenModal: false }; + } + + const { __experimentalGetGlobalBlocksByName, getBlock } = + select( blockEditorStore ); + const [ contentBlockClientId ] = + __experimentalGetGlobalBlocksByName( 'core/post-content' ); + if ( ! contentBlockClientId ) { + return { shouldOpenModal: false }; + } + + const contentBlock = getBlock( contentBlockClientId ); + if ( contentBlock?.innerBlocks?.length ) { + return { shouldOpenModal: false }; + } + + return { + shouldOpenModal: true, + postType: context.postType, + rootClientId: contentBlockClientId, + }; + }, + [] + ); + + if ( isClosed || ! shouldOpenModal ) { + return null; + } + + return ( + { + setIsClosed( true ); + const selectInsertedBlocks = true; + replaceInnerBlocks( + rootClientId, + blocks, + selectInsertedBlocks + ); + } } + onRequestClose={ () => setIsClosed( true ) } + /> + ); +}