diff --git a/packages/block-editor/src/components/inserter/block-patterns-tab/index.js b/packages/block-editor/src/components/inserter/block-patterns-tab/index.js index 141ebf8cc84016..400753254e73f0 100644 --- a/packages/block-editor/src/components/inserter/block-patterns-tab/index.js +++ b/packages/block-editor/src/components/inserter/block-patterns-tab/index.js @@ -13,7 +13,6 @@ import { useSelect } from '@wordpress/data'; import PatternsExplorerModal from '../block-patterns-explorer'; import MobileTabNavigation from '../mobile-tab-navigation'; import { PatternCategoryPreviews } from './pattern-category-previews'; -import { usePatternCategories } from './use-pattern-categories'; import CategoryTabs from '../category-tabs'; import InserterNoResults from '../no-results'; import { store as blockEditorStore } from '../../../store'; @@ -22,14 +21,12 @@ import { unlock } from '../../../lock-unlock'; function BlockPatternsTab( { onSelectCategory, selectedCategory, + categories, onInsert, rootClientId, children, } ) { const [ showPatternsExplorer, setShowPatternsExplorer ] = useState( false ); - - const categories = usePatternCategories( rootClientId ); - const isMobile = useViewportMatch( 'medium', '<' ); const isResolvingPatterns = useSelect( ( select ) => diff --git a/packages/block-editor/src/components/inserter/block-patterns-tab/use-pattern-categories.js b/packages/block-editor/src/components/inserter/block-patterns-tab/use-pattern-categories.js index cff5fbf3413820..939ee5b1d1ff20 100644 --- a/packages/block-editor/src/components/inserter/block-patterns-tab/use-pattern-categories.js +++ b/packages/block-editor/src/components/inserter/block-patterns-tab/use-pattern-categories.js @@ -14,6 +14,7 @@ import { isPatternFiltered, allPatternsCategory, myPatternsCategory, + starterContentCategory, INSERTER_PATTERN_TYPES, } from './utils'; @@ -67,6 +68,15 @@ export function usePatternCategories( rootClientId, sourceFilter = 'all' ) { label: _x( 'Uncategorized' ), } ); } + + if ( + patterns.find( ( pattern ) => + pattern.categories.includes( 'core/content' ) + ) + ) { + categories.unshift( starterContentCategory ); + } + if ( filteredPatterns.some( ( pattern ) => pattern.type === INSERTER_PATTERN_TYPES.user diff --git a/packages/block-editor/src/components/inserter/block-patterns-tab/utils.js b/packages/block-editor/src/components/inserter/block-patterns-tab/utils.js index f8ba47f3790e14..75590e0ed87bd3 100644 --- a/packages/block-editor/src/components/inserter/block-patterns-tab/utils.js +++ b/packages/block-editor/src/components/inserter/block-patterns-tab/utils.js @@ -22,7 +22,12 @@ export const allPatternsCategory = { export const myPatternsCategory = { name: 'myPatterns', - label: __( 'My patterns' ), + label: __( 'My Patterns' ), +}; + +export const starterContentCategory = { + name: 'core/content', + label: __( 'Starter Content' ), }; export function isPatternFiltered( pattern, sourceFilter, syncFilter ) { diff --git a/packages/block-editor/src/components/inserter/hooks/use-patterns-state.js b/packages/block-editor/src/components/inserter/hooks/use-patterns-state.js index 6483dc58ae8b97..20d564bb28d5ba 100644 --- a/packages/block-editor/src/components/inserter/hooks/use-patterns-state.js +++ b/packages/block-editor/src/components/inserter/hooks/use-patterns-state.js @@ -13,6 +13,17 @@ import { store as noticesStore } from '@wordpress/notices'; import { store as blockEditorStore } from '../../../store'; import { INSERTER_PATTERN_TYPES } from '../block-patterns-tab/utils'; +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. + return useSelect( ( select ) => + select( blockEditorStore ).getPatternsByBlockTypes( + 'core/post-content' + ) + ); +} + /** * Retrieves the block patterns inserter state. * @@ -40,6 +51,29 @@ const usePatternsState = ( onInsert, rootClientId, selectedCategory ) => { [ rootClientId ] ); + const starterPatterns = useStartPatterns(); + const starterPatternsNames = starterPatterns.map( + ( pattern ) => pattern.name + ); + const newPatterns = useMemo( + () => + patterns.map( ( pattern ) => { + if ( starterPatternsNames.includes( pattern.name ) ) { + // TODO - I'm not sure why we can't just use the pattern? + return { + ...pattern, + categories: [ + ...( pattern.categories ?? [] ), + 'core/content', + ], + }; + } + + return pattern; + } ), + [ patterns, starterPatterns ] + ); + const allCategories = useMemo( () => { const categories = [ ...patternCategories ]; userPatternCategories?.forEach( ( userCategory ) => { @@ -94,7 +128,7 @@ const usePatternsState = ( onInsert, rootClientId, selectedCategory ) => { [ createSuccessNotice, onInsert, selectedCategory ] ); - return [ patterns, allCategories, onClickPattern ]; + return [ newPatterns, allCategories, onClickPattern ]; }; export default usePatternsState; diff --git a/packages/block-editor/src/components/inserter/menu.js b/packages/block-editor/src/components/inserter/menu.js index faf2c20514f67a..744ea23cbc6cfc 100644 --- a/packages/block-editor/src/components/inserter/menu.js +++ b/packages/block-editor/src/components/inserter/menu.js @@ -32,6 +32,9 @@ import InserterSearchResults from './search-results'; import useInsertionPoint from './hooks/use-insertion-point'; import { store as blockEditorStore } from '../../store'; import TabbedSidebar from '../tabbed-sidebar'; +import { useZoomOut } from '../../hooks/use-zoom-out'; +import { usePatternCategories } from './block-patterns-tab/use-pattern-categories'; +import { unlock } from '../../lock-unlock'; const NOOP = () => {}; function InserterMenu( @@ -60,8 +63,11 @@ function InserterMenu( const [ filterValue, setFilterValue, delayedFilterValue ] = useDebouncedInput( __experimentalFilterValue ); const [ hoveredItem, setHoveredItem ] = useState( null ); + const categories = usePatternCategories( rootClientId ); const [ selectedPatternCategory, setSelectedPatternCategory ] = useState( - __experimentalInitialCategory + categories.find( + ( category ) => category.name === __experimentalInitialCategory + ) ); const [ patternFilter, setPatternFilter ] = useState( 'all' ); const [ selectedMediaCategory, setSelectedMediaCategory ] = @@ -237,6 +243,7 @@ function InserterMenu( onInsert={ onInsertPattern } onSelectCategory={ onClickPatternCategory } selectedCategory={ selectedPatternCategory } + categories={ categories } > { showPatternPanel && ( if ( ! blockNames ) { return EMPTY_ARRAY; } - const patterns = - select( STORE_NAME ).__experimentalGetAllowedPatterns( - rootClientId - ); + let patterns; + + if ( rootClientId ) { + patterns = + select( STORE_NAME ).__experimentalGetAllowedPatterns( + rootClientId + ); + } else { + const { + getAllPatterns, + __experimentalGetParsedPattern: getParsedPattern, + } = unlock( select( STORE_NAME ) ); + patterns = getAllPatterns() + .filter( ( { inserter = true } ) => !! inserter ) + .map( ( { name } ) => getParsedPattern( name ) ); + } + const normalizedBlockNames = Array.isArray( blockNames ) ? blockNames : [ blockNames ]; @@ -2444,7 +2457,11 @@ export const getPatternsByBlockTypes = createRegistrySelector( ( select ) => return filteredPatterns; }, ( state, blockNames, rootClientId ) => - getAllowedPatternsDependants( select )( state, rootClientId ) + ! rootClientId + ? unlock( select( STORE_NAME ) ).getAllPatterns() + : select( STORE_NAME ).__experimentalGetAllowedPatterns( + rootClientId + ) ) ); diff --git a/packages/editor/src/components/inserter-sidebar/index.js b/packages/editor/src/components/inserter-sidebar/index.js index b98770b7afe8fa..cabeacc4b5e051 100644 --- a/packages/editor/src/components/inserter-sidebar/index.js +++ b/packages/editor/src/components/inserter-sidebar/index.js @@ -33,31 +33,26 @@ export default function InserterSidebar() { getInsertionPoint, isPublishSidebarOpened, } = unlock( select( editorStore ) ); - const { - getBlockRootClientId, - __unstableGetEditorMode, - getSectionRootClientId, - } = unlock( select( blockEditorStore ) ); + + const { getSectionRootClientId } = unlock( select( blockEditorStore ) ); const { get } = select( preferencesStore ); const { getActiveComplementaryArea } = select( interfaceStore ); + const getBlockSectionRootClientId = () => { - if ( __unstableGetEditorMode() === 'zoom-out' ) { - const sectionRootClientId = getSectionRootClientId(); + const sectionRootClientId = getSectionRootClientId(); - if ( sectionRootClientId ) { - return sectionRootClientId; - } - } - return getBlockRootClientId(); + // '' is equiavlent to calling getBlockRootClientId() with no arguments. + return sectionRootClientId ?? ''; }; + return { inserterSidebarToggleRef: getInserterSidebarToggleRef(), insertionPoint: getInsertionPoint(), showMostUsedBlocks: get( 'core', 'mostUsedBlocks' ), - blockSectionRootClientId: getBlockSectionRootClientId(), sidebarIsOpened: !! ( getActiveComplementaryArea( 'core' ) || isPublishSidebarOpened() ), + blockSectionRootClientId: getBlockSectionRootClientId(), }; }, [] ); const { setIsInserterOpened } = useDispatch( editorStore ); @@ -89,7 +84,7 @@ export default function InserterSidebar() { showInserterHelpPanel shouldFocusBlock={ isMobileViewport } rootClientId={ - blockSectionRootClientId ?? insertionPoint.rootClientId + insertionPoint.rootClientId ?? blockSectionRootClientId } __experimentalInsertionIndex={ insertionPoint.insertionIndex } onSelect={ insertionPoint.onSelect } diff --git a/packages/editor/src/components/start-page-options/index.js b/packages/editor/src/components/start-page-options/index.js index 07fee67fbed19b..a8ec9020aec6cc 100644 --- a/packages/editor/src/components/start-page-options/index.js +++ b/packages/editor/src/components/start-page-options/index.js @@ -3,13 +3,13 @@ */ import { Modal } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; -import { useState, useMemo } from '@wordpress/element'; +import { useMemo, useState } 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 { useAsyncList, usePrevious } from '@wordpress/compose'; import { store as coreStore } from '@wordpress/core-data'; import { __unstableSerializeAndClean } from '@wordpress/blocks'; import { store as preferencesStore } from '@wordpress/preferences'; @@ -118,6 +118,8 @@ function StartPageOptionsModal( { onClose } ) { export default function StartPageOptions() { const [ isClosed, setIsClosed ] = useState( false ); + + // Select for starter page modal. const shouldEnableModal = useSelect( ( select ) => { const { isEditedPostDirty, isEditedPostEmpty, getCurrentPostType } = select( editorStore ); @@ -136,9 +138,67 @@ export default function StartPageOptions() { ); }, [] ); - if ( ! shouldEnableModal || isClosed ) { - return null; + // Select for starter pattern inserter. + const { shouldEnableStartPage, postType, postId } = useSelect( + ( select ) => { + const { + isEditedPostDirty, + isEditedPostEmpty, + getCurrentPostType, + getCurrentPostId, + } = select( editorStore ); + const _postType = getCurrentPostType(); + + return { + shouldEnableStartPage: + ! isEditedPostDirty() && + isEditedPostEmpty() && + TEMPLATE_POST_TYPE !== _postType, + postType: _postType, + postId: getCurrentPostId(), + }; + }, + [] + ); + + const previousPostType = usePrevious( postType ); + const previousPostId = usePrevious( postId ); + + // Reset the isClosed state when navigating to a new page/post. + if ( + ( previousPostType && previousPostType !== postType ) || + ( previousPostId && previousPostId !== postId ) + ) { + setIsClosed( false ); + } + + // 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 hasStarterPatterns = useSelect( + ( select ) => + !! select( blockEditorStore ).getPatternsByBlockTypes( + 'core/post-content' + ).length + ); + + const { setIsInserterOpened } = useDispatch( editorStore ); + const { __unstableSetEditorMode } = useDispatch( blockEditorStore ); + + const showInserterOnNewPage = + shouldEnableStartPage && ! isClosed && hasStarterPatterns; + + if ( showInserterOnNewPage ) { + setIsInserterOpened( { + tab: 'patterns', + category: 'core/content', + } ); + __unstableSetEditorMode( 'zoom-out' ); + } + + if ( shouldEnableModal && ! isClosed ) { + return setIsClosed( true ) } />; } - return setIsClosed( true ) } />; + return null; }