Skip to content

Commit

Permalink
Editor: Restore starter content modal (WordPress#69081)
Browse files Browse the repository at this point in the history
* Editor: Restore starter content modal
* Restore limit to page post type
* Add don't show the option to the modal again
* Adjust logic for opening modal
* Feedback
* Don't re-open the starter content modal after closing Preferences

Co-authored-by: Mamaduka <[email protected]>
Co-authored-by: carolinan <[email protected]>
Co-authored-by: t-hamano <[email protected]>
Co-authored-by: afercia <[email protected]>
Co-authored-by: stokesman <[email protected]>
Co-authored-by: yogeshbhutkar <[email protected]>
Co-authored-by: andreawetzel <[email protected]>
Co-authored-by: fabiankaegy <[email protected]>
  • Loading branch information
9 people authored Feb 18, 2025
1 parent c031b36 commit c0f3cd9
Show file tree
Hide file tree
Showing 12 changed files with 193 additions and 55 deletions.
1 change: 1 addition & 0 deletions packages/base-styles/_z-index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ $z-layers: (

// Ensure modal footer actions appear above modal contents
".editor-start-template-options__modal__actions": 1,
".editor-start-page-options__modal__actions": 1,

// Ensure checkbox + actions don't overlap table header
".dataviews-view-table thead": 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ import { usePatternCategories } from '../block-patterns-tab/use-pattern-categori

function PatternsExplorer( { initialCategory, rootClientId } ) {
const [ searchValue, setSearchValue ] = useState( '' );
const [ selectedCategory, setSelectedCategory ] =
useState( initialCategory );
const [ selectedCategory, setSelectedCategory ] = useState(
initialCategory?.name
);

const patternCategories = usePatternCategories( rootClientId );

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,7 @@ function BlockPatternsTab( {
) }
{ showPatternsExplorer && (
<PatternsExplorerModal
initialCategory={
selectedCategory?.name || categories[ 0 ]?.name
}
initialCategory={ selectedCategory || categories[ 0 ] }
patternCategories={ categories }
onModalClose={ () => setShowPatternsExplorer( false ) }
rootClientId={ rootClientId }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,19 +69,19 @@ export function PatternCategoryPreviews( {
return false;
}

if ( category.name === allPatternsCategory?.name ) {
if ( category.name === allPatternsCategory.name ) {
return true;
}

if (
category.name === myPatternsCategory?.name &&
category.name === myPatternsCategory.name &&
pattern.type === INSERTER_PATTERN_TYPES.user
) {
return true;
}

if (
category.name === starterPatternsCategory?.name &&
category.name === starterPatternsCategory.name &&
pattern.blockTypes?.includes( 'core/post-content' )
) {
return true;
Expand Down Expand Up @@ -149,7 +149,7 @@ export function PatternCategoryPreviews( {
level={ 4 }
as="div"
>
{ category?.label }
{ category.label }
</Heading>
</FlexBlock>
<PatternsFilter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
const getShouldDisableSyncFilter = ( sourceFilter ) =>
sourceFilter !== 'all' && sourceFilter !== 'user';
const getShouldHideSourcesFilter = ( category ) => {
return category?.name === myPatternsCategory.name;
return category.name === myPatternsCategory.name;
};

const PATTERN_SOURCE_MENU_OPTIONS = [
Expand Down Expand Up @@ -60,7 +60,7 @@ export function PatternsFilter( {
// the user may be confused when switching to another category if the haven't explicitly set
// this filter themselves.
const currentPatternSourceFilter =
category?.name === myPatternsCategory.name
category.name === myPatternsCategory.name
? INSERTER_PATTERN_TYPES.user
: patternSourceFilter;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,7 @@ function CategoryTabs( {
key={ category.name }
tabId={ category.name }
aria-current={
category.name === selectedCategory?.name
? 'true'
: undefined
category === selectedCategory ? 'true' : undefined
}
>
{ category.label }
Expand Down
12 changes: 2 additions & 10 deletions packages/block-editor/src/hooks/use-zoom-out.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@
* WordPress dependencies
*/
import { useSelect, useDispatch } from '@wordpress/data';
import { useEffect, useRef, useContext } from '@wordpress/element';
import { useEffect, useRef } from '@wordpress/element';

/**
* Internal dependencies
*/
import { store as blockEditorStore } from '../store';
import { unlock } from '../lock-unlock';
import BlockContext from '../components/block-context';

/**
* A hook used to set the editor mode to zoomed out mode, invoking the hook sets the mode.
Expand All @@ -20,7 +19,6 @@ import BlockContext from '../components/block-context';
* @param {boolean} enabled If we should enter into zoomOut mode or not
*/
export function useZoomOut( enabled = true ) {
const { postId } = useContext( BlockContext );
const { setZoomLevel, resetZoomLevel } = unlock(
useDispatch( blockEditorStore )
);
Expand All @@ -39,7 +37,6 @@ export function useZoomOut( enabled = true ) {

const controlZoomLevelRef = useRef( false );
const isEnabledRef = useRef( enabled );
const postIdRef = useRef( postId );

/**
* This hook tracks if the zoom state was changed manually by the user via clicking
Expand All @@ -58,11 +55,6 @@ export function useZoomOut( enabled = true ) {
useEffect( () => {
isEnabledRef.current = enabled;

// If the user created a new post/page, we should take control of the zoom level.
if ( postIdRef.current !== postId ) {
controlZoomLevelRef.current = true;
}

if ( enabled !== isZoomOut() ) {
controlZoomLevelRef.current = true;

Expand All @@ -79,5 +71,5 @@ export function useZoomOut( enabled = true ) {
resetZoomLevel();
}
};
}, [ enabled, isZoomOut, postId, resetZoomLevel, setZoomLevel ] );
}, [ enabled, isZoomOut, resetZoomLevel, setZoomLevel ] );
}
172 changes: 149 additions & 23 deletions packages/editor/src/components/start-page-options/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
/**
* WordPress dependencies
*/
import { useEffect } from '@wordpress/element';
import { Flex, FlexItem, Modal, ToggleControl } 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';

Expand All @@ -11,48 +19,166 @@ import { store as interfaceStore } from '@wordpress/interface';
*/
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-end"
expanded={ false }
>
<FlexItem>
<ToggleControl
__nextHasNoMarginBottom
checked={ showStartPatterns }
label={ __( 'Show starter patterns' ) }
help={ __(
'Shows starter patterns when creating a new page.'
) }
onChange={ ( newValue ) => {
setShowStartPatterns( newValue );
} }
/>
</FlexItem>
</Flex>
</Modal>
);
}

export default function StartPageOptions() {
const { postId, enabled } = useSelect( ( select ) => {
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 preferencesModalActive =
select( interfaceStore ).isModalActive( 'editor/preferences' );
const choosePatternModalEnabled = select( preferencesStore ).get(
'core',
'enableChoosePatternModal'
);
return {
postId: getCurrentPostId(),
enabled:
choosePatternModalEnabled &&
! preferencesModalActive &&
'page' === getCurrentPostType(),
choosePatternModalEnabled && 'page' === getCurrentPostType(),
};
}, [] );
const { isEditedPostDirty, isEditedPostEmpty } = useSelect( editorStore );
const { setIsInserterOpened } = useDispatch( editorStore );

// 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( () => {
if ( ! enabled ) {
return;
}

const isFreshPage = ! isEditedPostDirty() && isEditedPostEmpty();
if ( isFreshPage ) {
setIsInserterOpened( {
tab: 'patterns',
category: 'core/starter-content',
} );
// Prevents immediately opening when features is enabled via preferences modal.
const isPreferencesModalActive = isModalActive( 'editor/preferences' );
if ( ! enabled || ! isFreshPage || isPreferencesModalActive ) {
return;
}

// 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.
// Open the modal after the initial render for a new page.
setIsOpen( true );
}, [
postId,
enabled,
setIsInserterOpened,
postId,
isEditedPostDirty,
isEditedPostEmpty,
isModalActive,
] );

return null;
if ( ! isOpen ) {
return null;
}

return <StartPageOptionsModal onClose={ () => setIsOpen( false ) } />;
}
27 changes: 27 additions & 0 deletions packages/editor/src/components/start-page-options/style.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,30 @@
$actions-height: 92px;

.editor-start-page-options__modal {
.editor-start-page-options__modal__actions {
position: absolute;
bottom: 0;
width: 100%;
height: $actions-height;
background-color: $white;
margin-left: - $grid-unit-40;
margin-right: - $grid-unit-40;
padding-left: $grid-unit-40;
padding-right: $grid-unit-40;
border-top: 1px solid $gray-300;
z-index: z-index(".editor-start-page-options__modal__actions");
}

.block-editor-block-patterns-list {
// Since the actions container is positioned absolutely,
// this padding bottom ensures that the content wrapper will properly
// detect overflowing content and start showing scrollbars at the right
// moment. Without this padding, the content would render under the actions
// bar without causing the wrapper to show a scrollbar.
padding-bottom: $actions-height;
}
}

// 2 column masonry layout.
.editor-start-page-options__modal-content .block-editor-block-patterns-list {
column-count: 2;
Expand Down
Loading

0 comments on commit c0f3cd9

Please sign in to comment.