Skip to content

Commit

Permalink
Add template replace flow to template inspector (#54609)
Browse files Browse the repository at this point in the history
* Add a modal to allow template switching

* fetch template info

* Allow switching to different patterns

* Allow switching to different patterns

* Add columns

* move availble templates to the actions

* filter for the correct templates

* create the right data structure in the use select

* move to a hook

* inject theme attribute into pattern again

* put the overlay over the top of the dropdown

* fix the pattern to templates hook

* set the template on click

* Also set the blocks

* remove calls to set template with the current template, since setting blocks correctly updates the content in the editor

* serialize blocks so that we have correctly processed template parts

* remove duplicated code

* Remove unnecessary mapping

* refactor

* memoize the patterns

* combine the useSelect

* Update packages/edit-site/src/components/sidebar-edit-mode/page-panels/hooks.js

Co-authored-by: Andrei Draganescu <me@andreidraganescu.info>

* Fix ESLint error

* Only show the button is there is more than 1 pattern

* Copy update

* Move the hook to a subdir

* check that there are patterns

* move the check

* remove useCallback

* change condition to show the button

* change condition

* move to use editEntityRecord

* combine filters

* add comments

* Update packages/edit-site/src/components/sidebar-edit-mode/template-panel/replace-template-button.js

Co-authored-by: Andrei Draganescu <me@andreidraganescu.info>

---------

Co-authored-by: Andrei Draganescu <andrei.draganescu@automattic.com>
Co-authored-by: Andrei Draganescu <me@andreidraganescu.info>
Co-authored-by: George Mamadashvili <georgemamadashvili@gmail.com>
  • Loading branch information
4 people authored and mikachan committed Oct 4, 2023
1 parent d36bd6d commit 283f4e7
Show file tree
Hide file tree
Showing 5 changed files with 232 additions and 12 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 @@ -128,6 +128,7 @@ $z-layers: (
".block-editor-block-rename-modal": 1000001,
".edit-site-list__rename-modal": 1000001,
".edit-site-swap-template-modal": 1000001,
".edit-site-template-panel__replace-template-modal": 1000001,

// Note: The ConfirmDialog component's z-index is being set to 1000001 in packages/components/src/confirm-dialog/styles.ts
// because it uses emotion and not sass. We need it to render on top its parent popover.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/**
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data';
import { useMemo } from '@wordpress/element';
import { store as coreStore } from '@wordpress/core-data';
import { parse } from '@wordpress/blocks';

/**
* Internal dependencies
*/
import { store as editSiteStore } from '../../../store';
import { PATTERN_CORE_SOURCES, PATTERN_TYPES } from '../../../utils/constants';
import { unlock } from '../../../lock-unlock';

function injectThemeAttributeInBlockTemplateContent(
block,
currentThemeStylesheet
) {
block.innerBlocks = block.innerBlocks.map( ( innerBlock ) => {
return injectThemeAttributeInBlockTemplateContent(
innerBlock,
currentThemeStylesheet
);
} );

if (
block.name === 'core/template-part' &&
block.attributes.theme === undefined
) {
block.attributes.theme = currentThemeStylesheet;
}
return block;
}

function preparePatterns( patterns, template, currentThemeStylesheet ) {
// Filter out duplicates.
const filterOutDuplicatesByName = ( currentItem, index, items ) =>
index === items.findIndex( ( item ) => currentItem.name === item.name );

// Filter out core patterns.
const filterOutCorePatterns = ( pattern ) =>
! PATTERN_CORE_SOURCES.includes( pattern.source );

// Filter only the patterns that are compatible with the current template.
const filterCompatiblePatterns = ( pattern ) =>
pattern.templateTypes?.includes( template.slug );

return patterns
.filter(
filterOutCorePatterns &&
filterOutDuplicatesByName &&
filterCompatiblePatterns
)
.map( ( pattern ) => ( {
...pattern,
keywords: pattern.keywords || [],
type: PATTERN_TYPES.theme,
blocks: parse( pattern.content, {
__unstableSkipMigrationLogs: true,
} ).map( ( block ) =>
injectThemeAttributeInBlockTemplateContent(
block,
currentThemeStylesheet
)
),
} ) );
}

export function useAvailablePatterns( template ) {
const { blockPatterns, restBlockPatterns, currentThemeStylesheet } =
useSelect( ( select ) => {
const { getSettings } = unlock( select( editSiteStore ) );
const settings = getSettings();

return {
blockPatterns:
settings.__experimentalAdditionalBlockPatterns ??
settings.__experimentalBlockPatterns,
restBlockPatterns: select( coreStore ).getBlockPatterns(),
currentThemeStylesheet:
select( coreStore ).getCurrentTheme().stylesheet,
};
}, [] );

return useMemo( () => {
const mergedPatterns = [
...( blockPatterns || [] ),
...( restBlockPatterns || [] ),
];
return preparePatterns(
mergedPatterns,
template,
currentThemeStylesheet
);
}, [ blockPatterns, restBlockPatterns, template, currentThemeStylesheet ] );
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/**
* WordPress dependencies
*/
import { useSelect, useDispatch } from '@wordpress/data';
import { useState } from '@wordpress/element';
import { __experimentalBlockPatternsList as BlockPatternsList } from '@wordpress/block-editor';
import { MenuItem, Modal } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { store as coreStore } from '@wordpress/core-data';
import { useAsyncList } from '@wordpress/compose';
import { serialize } from '@wordpress/blocks';

/**
* Internal dependencies
*/
import { store as editSiteStore } from '../../../store';

export default function ReplaceTemplateButton( {
onClick,
availableTemplates,
} ) {
const { editEntityRecord } = useDispatch( coreStore );
const [ showModal, setShowModal ] = useState( false );
const onClose = () => {
setShowModal( false );
};

const { postId, postType } = useSelect( ( select ) => {
return {
postId: select( editSiteStore ).getEditedPostId(),
postType: select( editSiteStore ).getEditedPostType(),
};
}, [] );

const onTemplateSelect = async ( selectedTemplate ) => {
onClose(); // Close the template suggestions modal first.
onClick();
await editEntityRecord( 'postType', postType, postId, {
blocks: selectedTemplate.blocks,
content: serialize( selectedTemplate.blocks ),
} );
};

if ( ! availableTemplates.length || availableTemplates.length < 1 ) {
return null;
}

return (
<>
<MenuItem
info={ __(
'Replace the contents of this template with another.'
) }
onClick={ () => setShowModal( true ) }
>
{ __( 'Replace template' ) }
</MenuItem>

{ showModal && (
<Modal
title={ __( 'Choose a template' ) }
onRequestClose={ onClose }
overlayClassName="edit-site-template-panel__replace-template-modal"
isFullScreen
>
<div className="edit-site-template-panel__replace-template-modal__content">
<TemplatesList
availableTemplates={ availableTemplates }
onSelect={ onTemplateSelect }
/>
</div>
</Modal>
) }
</>
);
}

function TemplatesList( { availableTemplates, onSelect } ) {
const shownTemplates = useAsyncList( availableTemplates );

return (
<BlockPatternsList
label={ __( 'Templates' ) }
blockPatterns={ availableTemplates }
shownPatterns={ shownTemplates }
onClickPattern={ onSelect }
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,21 @@ h3.edit-site-template-card__template-areas-title {
font-weight: 500;
margin: 0 0 $grid-unit-10;
}


.edit-site-template-panel__replace-template-modal {
z-index: z-index(".edit-site-template-panel__replace-template-modal");
}

.edit-site-template-panel__replace-template-modal__content {
column-count: 2;
column-gap: $grid-unit-30;

@include break-medium() {
column-count: 3;
}

@include break-wide() {
column-count: 4;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,21 @@ import { moreVertical } from '@wordpress/icons';
*/
import { store as editSiteStore } from '../../../store';
import isTemplateRevertable from '../../../utils/is-template-revertable';
import ReplaceTemplateButton from './replace-template-button';
import { useAvailablePatterns } from './hooks';

export default function Actions( { template } ) {
const availablePatterns = useAvailablePatterns( template );
const { revertTemplate } = useDispatch( editSiteStore );
const isRevertable = isTemplateRevertable( template );
if ( ! isRevertable ) {

if (
! isRevertable &&
( ! availablePatterns.length || availablePatterns.length < 1 )
) {
return null;
}

return (
<DropdownMenu
icon={ moreVertical }
Expand All @@ -27,17 +35,24 @@ export default function Actions( { template } ) {
>
{ ( { onClose } ) => (
<MenuGroup>
<MenuItem
info={ __(
'Use the template as supplied by the theme.'
) }
onClick={ () => {
revertTemplate( template );
onClose();
} }
>
{ __( 'Clear customizations' ) }
</MenuItem>
{ isRevertable && (
<MenuItem
info={ __(
'Use the template as supplied by the theme.'
) }
onClick={ () => {
revertTemplate( template );
onClose();
} }
>
{ __( 'Clear customizations' ) }
</MenuItem>
) }
<ReplaceTemplateButton
availableTemplates={ availablePatterns }
template={ template }
onClick={ onClose }
/>
</MenuGroup>
) }
</DropdownMenu>
Expand Down

0 comments on commit 283f4e7

Please sign in to comment.