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 b44446da0c068..1de94832878cd 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
@@ -57,6 +57,7 @@ export function CreateTemplatePartModalContents( {
defaultArea = TEMPLATE_PART_AREA_DEFAULT_CATEGORY,
blocks = [],
confirmLabel = __( 'Create' ),
+ content,
closeModal,
onCreate,
onError,
@@ -95,7 +96,7 @@ export function CreateTemplatePartModalContents( {
{
slug: cleanSlug,
title: uniqueTitle,
- content: serialize( blocks ),
+ content: content || serialize( blocks ),
area,
},
{ throwOnError: true }
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..329334ce67d5d
--- /dev/null
+++ b/packages/edit-site/src/components/template-part-modal/duplicate.js
@@ -0,0 +1,93 @@
+/**
+ * WordPress dependencies
+ */
+import { useDispatch, useSelect } from '@wordpress/data';
+import { __, sprintf } from '@wordpress/i18n';
+import { store as interfaceStore } from '@wordpress/interface';
+import { store as noticesStore } from '@wordpress/notices';
+import { privateApis as routerPrivateApis } from '@wordpress/router';
+import { getQueryArgs } from '@wordpress/url';
+
+/**
+ * Internal dependencies
+ */
+import CreateTemplatePartModal from '../create-template-part-modal';
+import useEditedEntityRecord from '../use-edited-entity-record';
+import { TEMPLATE_PART_MODALS } from './';
+import { unlock } from '../../lock-unlock';
+import { TEMPLATE_PART_POST_TYPE } from '../../utils/constants';
+
+const { useHistory } = unlock( routerPrivateApis );
+
+function DuplicateModal( { 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 (
+
+ );
+}
+
+export default function TemplatePartDuplicateModal() {
+ 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 e5a756aed101f..6e6f6a87dad12 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
@@ -290,7 +290,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();
@@ -298,7 +298,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 };