diff --git a/packages/edit-site/src/components/create-template-part-modal/index.js b/packages/edit-site/src/components/create-template-part-modal/index.js
index 31f12b6cab56d..cf8d9e599e34f 100644
--- a/packages/edit-site/src/components/create-template-part-modal/index.js
+++ b/packages/edit-site/src/components/create-template-part-modal/index.js
@@ -42,6 +42,7 @@ export default function CreateTemplatePartModal( {
defaultArea = TEMPLATE_PART_AREA_DEFAULT_CATEGORY,
blocks = [],
confirmLabel = __( 'Create' ),
+ content,
closeModal,
modalTitle = __( 'Create template part' ),
onCreate,
@@ -82,7 +83,7 @@ export default function CreateTemplatePartModal( {
{
slug: cleanSlug,
title: uniqueTitle,
- content: serialize( blocks ),
+ content: content || serialize( blocks ),
area,
},
{ throwOnError: true }
diff --git a/packages/edit-site/src/components/duplicate-template-part-modal/index.js b/packages/edit-site/src/components/duplicate-template-part-modal/index.js
new file mode 100644
index 0000000000000..cb6f5e0b4a8a1
--- /dev/null
+++ b/packages/edit-site/src/components/duplicate-template-part-modal/index.js
@@ -0,0 +1,54 @@
+/**
+ * WordPress dependencies
+ */
+import { useDispatch } from '@wordpress/data';
+import { __, sprintf } from '@wordpress/i18n';
+import { store as noticesStore } from '@wordpress/notices';
+
+/**
+ * Internal dependencies
+ */
+import CreateTemplatePartModal from '../create-template-part-modal';
+
+export default function DuplicateTemplatePartModal( {
+ templatePart,
+ onClose,
+ onSuccess,
+} ) {
+ const { createSuccessNotice } = useDispatch( noticesStore );
+
+ function handleOnSuccess( newTemplatePart ) {
+ createSuccessNotice(
+ sprintf(
+ // translators: %s: The new template part's title e.g. 'Call to action (copy)'.
+ __( '"%s" duplicated.' ),
+ templatePart.title
+ ),
+ {
+ type: 'snackbar',
+ id: 'template-parts-create',
+ }
+ );
+
+ onSuccess?.( newTemplatePart );
+ }
+
+ return (
+
+ );
+}
diff --git a/packages/edit-site/src/components/page-patterns/duplicate-menu-item.js b/packages/edit-site/src/components/page-patterns/duplicate-menu-item.js
index 118c954a851f3..4a067465fa2f1 100644
--- a/packages/edit-site/src/components/page-patterns/duplicate-menu-item.js
+++ b/packages/edit-site/src/components/page-patterns/duplicate-menu-item.js
@@ -2,10 +2,8 @@
* WordPress dependencies
*/
import { MenuItem } from '@wordpress/components';
-import { useDispatch } from '@wordpress/data';
import { useState } from '@wordpress/element';
-import { __, sprintf } from '@wordpress/i18n';
-import { store as noticesStore } from '@wordpress/notices';
+import { __ } from '@wordpress/i18n';
import { privateApis as patternsPrivateApis } from '@wordpress/patterns';
import { privateApis as routerPrivateApis } from '@wordpress/router';
@@ -14,7 +12,7 @@ import { privateApis as routerPrivateApis } from '@wordpress/router';
*/
import { TEMPLATE_PART_POST_TYPE, PATTERN_TYPES } from '../../utils/constants';
import { unlock } from '../../lock-unlock';
-import CreateTemplatePartModal from '../create-template-part-modal';
+import DuplicateTemplatePartModal from '../duplicate-template-part-modal';
const { DuplicatePatternModal } = unlock( patternsPrivateApis );
const { useHistory } = unlock( routerPrivateApis );
@@ -25,7 +23,6 @@ export default function DuplicateMenuItem( {
label = __( 'Duplicate' ),
onClose,
} ) {
- const { createSuccessNotice } = useDispatch( noticesStore );
const [ isModalOpen, setIsModalOpen ] = useState( false );
const history = useHistory();
@@ -35,18 +32,6 @@ export default function DuplicateMenuItem( {
const isThemePattern = item.type === PATTERN_TYPES.theme;
async function onTemplatePartSuccess( templatePart ) {
- createSuccessNotice(
- sprintf(
- // translators: %s: The new template part's title e.g. 'Call to action (copy)'.
- __( '"%s" duplicated.' ),
- item.title
- ),
- {
- type: 'snackbar',
- id: 'edit-site-patterns-success',
- }
- );
-
history.push( {
postType: TEMPLATE_PART_POST_TYPE,
postId: templatePart?.id,
@@ -85,19 +70,10 @@ export default function DuplicateMenuItem( {
/>
) }
{ isModalOpen && isTemplatePart && (
-
) }
>
diff --git a/packages/edit-site/src/components/template-part-modal/duplicate.js b/packages/edit-site/src/components/template-part-modal/duplicate.js
new file mode 100644
index 0000000000000..192ec93a42016
--- /dev/null
+++ b/packages/edit-site/src/components/template-part-modal/duplicate.js
@@ -0,0 +1,52 @@
+/**
+ * WordPress dependencies
+ */
+import { useDispatch, useSelect } from '@wordpress/data';
+import { store as interfaceStore } from '@wordpress/interface';
+import { privateApis as routerPrivateApis } from '@wordpress/router';
+import { getQueryArgs } from '@wordpress/url';
+
+/**
+ * Internal dependencies
+ */
+import useEditedEntityRecord from '../use-edited-entity-record';
+import DuplicateTemplatePartModal from '../duplicate-template-part-modal';
+import { TEMPLATE_PART_MODALS } from './';
+import { unlock } from '../../lock-unlock';
+import { TEMPLATE_PART_POST_TYPE } from '../../utils/constants';
+
+const { useHistory } = unlock( routerPrivateApis );
+
+export default function PatternDuplicateModal() {
+ const { record } = useEditedEntityRecord();
+ const { categoryType, categoryId } = getQueryArgs( window.location.href );
+ const { closeModal } = useDispatch( interfaceStore );
+ const history = useHistory();
+
+ const isActive = useSelect( ( select ) =>
+ select( interfaceStore ).isModalActive( TEMPLATE_PART_MODALS.duplicate )
+ );
+
+ if ( ! isActive ) {
+ return null;
+ }
+
+ function onSuccess( newTemplatePart ) {
+ history.push( {
+ postType: TEMPLATE_PART_POST_TYPE,
+ postId: newTemplatePart.id,
+ categoryType,
+ categoryId,
+ } );
+
+ closeModal();
+ }
+
+ return (
+
+ );
+}
diff --git a/packages/edit-site/src/components/template-part-modal/index.js b/packages/edit-site/src/components/template-part-modal/index.js
index 632470483186a..aea7e909a29cc 100644
--- a/packages/edit-site/src/components/template-part-modal/index.js
+++ b/packages/edit-site/src/components/template-part-modal/index.js
@@ -1,6 +1,7 @@
/**
* Internal dependencies
*/
+import TemplatePartDuplicateModal from './duplicate';
import TemplatePartRenameModal from './rename';
export const TEMPLATE_PART_MODALS = {
@@ -9,11 +10,10 @@ export const TEMPLATE_PART_MODALS = {
};
export default function TemplatePartModal() {
- // Duplication command and modal is in separate follow-up.
return (
<>
- { /* */ }
+
>
);
}
diff --git a/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js b/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js
index b079c5e956121..e5e1d32cc029b 100644
--- a/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js
+++ b/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js
@@ -4,6 +4,7 @@
import { useSelect, useDispatch } from '@wordpress/data';
import { __, sprintf, isRTL } from '@wordpress/i18n';
import {
+ edit,
trash,
rotateLeft,
rotateRight,
@@ -398,7 +399,7 @@ function usePatternCommands() {
commands.push( {
name: 'core/rename-template-part',
label: __( 'Rename template part' ),
- icon: symbol,
+ icon: edit,
callback: ( { close } ) => {
openModal( TEMPLATE_PART_MODALS.rename );
close();
@@ -406,7 +407,15 @@ function usePatternCommands() {
} );
}
- // All template parts will be eligible for duplication in a follow-up.
+ commands.push( {
+ name: 'core/duplicate-template-part',
+ label: __( 'Duplicate template part' ),
+ icon: symbol,
+ callback: ( { close } ) => {
+ openModal( TEMPLATE_PART_MODALS.duplicate );
+ close();
+ },
+ } );
}
return { isLoading: false, commands };