diff --git a/packages/edit-site/src/components/editor/index.js b/packages/edit-site/src/components/editor/index.js
index f3ee1bda69719f..dcec39ddc7bedb 100644
--- a/packages/edit-site/src/components/editor/index.js
+++ b/packages/edit-site/src/components/editor/index.js
@@ -44,6 +44,7 @@ import CanvasLoader from '../canvas-loader';
import { unlock } from '../../lock-unlock';
import useEditedEntityRecord from '../use-edited-entity-record';
import { SidebarFixedBottomSlot } from '../sidebar-edit-mode/sidebar-fixed-bottom';
+import PatternModal from '../pattern-modal';
import { POST_TYPE_LABELS, TEMPLATE_POST_TYPE } from '../../utils/constants';
const { BlockRemovalWarningModal } = unlock( blockEditorPrivateApis );
@@ -220,6 +221,7 @@ export default function Editor( { listViewToggleElement, isLoading } ) {
+
>
) }
{ editorMode === 'text' &&
diff --git a/packages/edit-site/src/components/page-patterns/rename-menu-item.js b/packages/edit-site/src/components/page-patterns/rename-menu-item.js
index 5792454a44aa8a..8b1d6771c2e90e 100644
--- a/packages/edit-site/src/components/page-patterns/rename-menu-item.js
+++ b/packages/edit-site/src/components/page-patterns/rename-menu-item.js
@@ -61,9 +61,9 @@ export default function RenameMenuItem( { item, onClose } ) {
const fallbackErrorMessage =
item.type === TEMPLATE_PART_POST_TYPE
? __(
- 'An error occurred while reverting the template part.'
+ 'An error occurred while renaming the template part.'
)
- : __( 'An error occurred while reverting the pattern.' );
+ : __( 'An error occurred while renaming the pattern.' );
const errorMessage =
error.message && error.code !== 'unknown_error'
? error.message
diff --git a/packages/edit-site/src/components/pattern-modal/index.js b/packages/edit-site/src/components/pattern-modal/index.js
new file mode 100644
index 00000000000000..acf96a64877255
--- /dev/null
+++ b/packages/edit-site/src/components/pattern-modal/index.js
@@ -0,0 +1,14 @@
+/**
+ * Internal dependencies
+ */
+import PatternRenameModal from './rename';
+
+export const PATTERN_MODALS = {
+ rename: 'edit-site/pattern-rename',
+};
+
+export default function PatternModal() {
+ // Further modals are likely
+ // e.g. duplicating and switching up sync status etc.
+ return ;
+}
diff --git a/packages/edit-site/src/components/pattern-modal/rename.js b/packages/edit-site/src/components/pattern-modal/rename.js
new file mode 100644
index 00000000000000..3175451bd16460
--- /dev/null
+++ b/packages/edit-site/src/components/pattern-modal/rename.js
@@ -0,0 +1,29 @@
+/**
+ * WordPress dependencies
+ */
+import { useDispatch, useSelect } from '@wordpress/data';
+import { store as interfaceStore } from '@wordpress/interface';
+import { privateApis as patternsPrivateApis } from '@wordpress/patterns';
+
+/**
+ * Internal dependencies
+ */
+import { PATTERN_MODALS } from './';
+import { unlock } from '../../lock-unlock';
+import useEditedEntityRecord from '../use-edited-entity-record';
+
+const { RenamePatternModal } = unlock( patternsPrivateApis );
+
+export default function PatternRenameModal() {
+ const { record: pattern } = useEditedEntityRecord();
+ const { closeModal } = useDispatch( interfaceStore );
+ const isActive = useSelect( ( select ) =>
+ select( interfaceStore ).isModalActive( PATTERN_MODALS.rename )
+ );
+
+ if ( ! isActive ) {
+ return null;
+ }
+
+ 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 a10d518d53644b..107efce384eb72 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
@@ -15,6 +15,7 @@ import {
code,
keyboard,
listView,
+ symbol,
} from '@wordpress/icons';
import { useCommandLoader } from '@wordpress/commands';
import { decodeEntities } from '@wordpress/html-entities';
@@ -32,6 +33,7 @@ import isTemplateRemovable from '../../utils/is-template-removable';
import isTemplateRevertable from '../../utils/is-template-revertable';
import { KEYBOARD_SHORTCUT_HELP_MODAL_NAME } from '../../components/keyboard-shortcut-help-modal';
import { PREFERENCES_MODAL_NAME } from '../../components/preferences-modal';
+import { PATTERN_MODALS } from '../../components/pattern-modal';
import { unlock } from '../../lock-unlock';
import { TEMPLATE_POST_TYPE } from '../../utils/constants';
@@ -359,6 +361,31 @@ function useEditUICommands() {
};
}
+function usePatternCommands() {
+ const { isLoaded, record: pattern } = useEditedEntityRecord();
+ const { openModal } = useDispatch( interfaceStore );
+
+ if ( ! isLoaded ) {
+ return { isLoading: true, commands: [] };
+ }
+
+ const commands = [];
+
+ if ( pattern?.type === 'wp_block' ) {
+ commands.push( {
+ name: 'core/rename-pattern',
+ label: __( 'Rename pattern' ),
+ icon: symbol,
+ callback: ( { close } ) => {
+ openModal( PATTERN_MODALS.rename );
+ close();
+ },
+ } );
+ }
+
+ return { isLoading: false, commands };
+}
+
export function useEditModeCommands() {
useCommandLoader( {
name: 'core/exit-code-editor',
@@ -377,6 +404,11 @@ export function useEditModeCommands() {
hook: useManipulateDocumentCommands,
} );
+ useCommandLoader( {
+ name: 'core/edit-site/patterns',
+ hook: usePatternCommands,
+ } );
+
useCommandLoader( {
name: 'core/edit-site/edit-ui',
hook: useEditUICommands,
diff --git a/packages/patterns/src/components/rename-pattern-modal.js b/packages/patterns/src/components/rename-pattern-modal.js
new file mode 100644
index 00000000000000..9b905c04b1e20e
--- /dev/null
+++ b/packages/patterns/src/components/rename-pattern-modal.js
@@ -0,0 +1,115 @@
+/**
+ * WordPress dependencies
+ */
+import {
+ Button,
+ Modal,
+ TextControl,
+ __experimentalHStack as HStack,
+ __experimentalVStack as VStack,
+} from '@wordpress/components';
+import { store as coreStore } from '@wordpress/core-data';
+import { useDispatch } from '@wordpress/data';
+import { useState } from '@wordpress/element';
+import { decodeEntities } from '@wordpress/html-entities';
+import { __ } from '@wordpress/i18n';
+import { store as noticesStore } from '@wordpress/notices';
+
+export default function RenamePatternModal( {
+ onClose,
+ onError,
+ onSuccess,
+ pattern,
+ ...props
+} ) {
+ const originalName = decodeEntities( pattern.title );
+ const [ name, setName ] = useState( originalName );
+ const [ isSaving, setIsSaving ] = useState( false );
+
+ const {
+ editEntityRecord,
+ __experimentalSaveSpecifiedEntityEdits: saveSpecifiedEntityEdits,
+ } = useDispatch( coreStore );
+
+ const { createSuccessNotice, createErrorNotice } =
+ useDispatch( noticesStore );
+
+ const onRename = async ( event ) => {
+ event.preventDefault();
+
+ if ( ! name || name === pattern.title || isSaving ) {
+ return;
+ }
+
+ try {
+ await editEntityRecord( 'postType', pattern.type, pattern.id, {
+ title: name,
+ } );
+
+ setIsSaving( true );
+ setName( '' );
+ onClose?.();
+
+ const savedRecord = await saveSpecifiedEntityEdits(
+ 'postType',
+ pattern.type,
+ pattern.id,
+ [ 'title' ],
+ { throwOnError: true }
+ );
+
+ onSuccess?.( savedRecord );
+
+ createSuccessNotice( __( 'Pattern renamed' ), {
+ type: 'snackbar',
+ id: 'pattern-update',
+ } );
+ } catch ( error ) {
+ onError?.();
+
+ const errorMessage =
+ error.message && error.code !== 'unknown_error'
+ ? error.message
+ : __( 'An error occurred while renaming the pattern.' );
+
+ createErrorNotice( errorMessage, {
+ type: 'snackbar',
+ id: 'pattern-update',
+ } );
+ } finally {
+ setIsSaving( false );
+ setName( '' );
+ }
+ };
+
+ const onRequestClose = () => {
+ onClose?.();
+ setName( '' );
+ };
+
+ return (
+
+
+
+ );
+}
diff --git a/packages/patterns/src/private-apis.js b/packages/patterns/src/private-apis.js
index cdbf1a6ba29408..3a68cea8794a54 100644
--- a/packages/patterns/src/private-apis.js
+++ b/packages/patterns/src/private-apis.js
@@ -3,6 +3,7 @@
*/
import { lock } from './lock-unlock';
import CreatePatternModal from './components/create-pattern-modal';
+import RenamePatternModal from './components/rename-pattern-modal';
import PatternsMenuItems from './components';
import {
PATTERN_TYPES,
@@ -15,6 +16,7 @@ import {
export const privateApis = {};
lock( privateApis, {
CreatePatternModal,
+ RenamePatternModal,
PatternsMenuItems,
PATTERN_TYPES,
PATTERN_DEFAULT_CATEGORY,