From e9d6403919a3e10c1db55490c9969670fac3a275 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Tue, 4 Jul 2023 14:10:10 +1000
Subject: [PATCH 01/18] Add renaming and deletion of custom patterns and
template parts
---
.../src/components/page-patterns/grid-item.js | 22 +++-
.../page-patterns/rename-menu-item.js | 115 ++++++++++++++++++
.../components/page-patterns/use-patterns.js | 2 +
3 files changed, 135 insertions(+), 4 deletions(-)
create mode 100644 packages/edit-site/src/components/page-patterns/rename-menu-item.js
diff --git a/packages/edit-site/src/components/page-patterns/grid-item.js b/packages/edit-site/src/components/page-patterns/grid-item.js
index 8795e41eedd4f..dd2eae6b1f28d 100644
--- a/packages/edit-site/src/components/page-patterns/grid-item.js
+++ b/packages/edit-site/src/components/page-patterns/grid-item.js
@@ -35,7 +35,9 @@ import { DELETE, BACKSPACE } from '@wordpress/keycodes';
/**
* Internal dependencies
*/
-import { PATTERNS, USER_PATTERNS } from './utils';
+import RenameMenuItem from './rename-menu-item';
+import { PATTERNS, TEMPLATE_PARTS, USER_PATTERNS } from './utils';
+import { store as editSiteStore } from '../../store';
import { useLink } from '../routes/link';
const THEME_PATTERN_TOOLTIP = __( 'Theme patterns cannot be edited.' );
@@ -44,6 +46,7 @@ export default function GridItem( { categoryId, composite, icon, item } ) {
const descriptionId = useId();
const [ isDeleteDialogOpen, setIsDeleteDialogOpen ] = useState( false );
+ const { removeTemplate } = useDispatch( editSiteStore );
const { __experimentalDeleteReusableBlock } =
useDispatch( reusableBlocksStore );
const { createErrorNotice, createSuccessNotice } =
@@ -85,6 +88,9 @@ export default function GridItem( { categoryId, composite, icon, item } ) {
}
};
+ const deleteItem = () =>
+ item.type === TEMPLATE_PARTS ? removeTemplate( item ) : deletePattern();
+
const isUserPattern = item.type === USER_PATTERNS;
const ariaDescriptions = [];
if ( isUserPattern ) {
@@ -108,6 +114,10 @@ export default function GridItem( { categoryId, composite, icon, item } ) {
itemIcon = symbolFilled;
}
+ const isCustomPattern =
+ item.type === USER_PATTERNS ||
+ ( item.type === TEMPLATE_PARTS && item.isCustom );
+
return (
<>
@@ -179,7 +189,7 @@ export default function GridItem( { categoryId, composite, icon, item } ) {
) }
- { item.type === USER_PATTERNS && (
+ { isCustomPattern && (
- { () => (
+ { ( { onClose } ) => (
+
{ isDeleteDialogOpen && (
setIsDeleteDialogOpen( false ) }
>
{ __( 'Are you sure you want to delete this pattern?' ) }
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
new file mode 100644
index 0000000000000..938023a62cefd
--- /dev/null
+++ b/packages/edit-site/src/components/page-patterns/rename-menu-item.js
@@ -0,0 +1,115 @@
+/**
+ * WordPress dependencies
+ */
+import {
+ Button,
+ MenuItem,
+ 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 { __ } from '@wordpress/i18n';
+import { store as noticesStore } from '@wordpress/notices';
+
+/**
+ * Internal dependencies
+ */
+import { TEMPLATE_PARTS } from './utils';
+
+export default function RenameMenuItem( { item, onClose } ) {
+ const [ title, setTitle ] = useState( () => item.title );
+ const [ isModalOpen, setIsModalOpen ] = useState( false );
+
+ const { editEntityRecord, saveEditedEntityRecord } =
+ useDispatch( coreStore );
+ const { createSuccessNotice, createErrorNotice } =
+ useDispatch( noticesStore );
+
+ if ( item.type === TEMPLATE_PARTS && ! item.isCustom ) {
+ return null;
+ }
+
+ async function onRename( event ) {
+ event.preventDefault();
+
+ try {
+ await editEntityRecord( 'postType', item.type, item.id, { title } );
+
+ // Update state before saving rerenders the list.
+ setTitle( '' );
+ setIsModalOpen( false );
+ onClose();
+
+ // Persist edited entity.
+ await saveEditedEntityRecord( 'postType', item.type, item.id, {
+ throwOnError: true,
+ } );
+
+ createSuccessNotice( __( 'Entity renamed.' ), {
+ type: 'snackbar',
+ } );
+ } catch ( error ) {
+ const errorMessage =
+ error.message && error.code !== 'unknown_error'
+ ? error.message
+ : __( 'An error occurred while renaming the entity.' );
+
+ createErrorNotice( errorMessage, { type: 'snackbar' } );
+ }
+ }
+
+ return (
+ <>
+
+ { isModalOpen && (
+ {
+ setIsModalOpen( false );
+ onClose();
+ } }
+ overlayClassName="edit-site-list__rename_modal"
+ >
+
+
+ ) }
+ >
+ );
+}
diff --git a/packages/edit-site/src/components/page-patterns/use-patterns.js b/packages/edit-site/src/components/page-patterns/use-patterns.js
index a394aabf572c4..295d1eee8e410 100644
--- a/packages/edit-site/src/components/page-patterns/use-patterns.js
+++ b/packages/edit-site/src/components/page-patterns/use-patterns.js
@@ -31,7 +31,9 @@ const templatePartToPattern = ( templatePart ) => ( {
blocks: parse( templatePart.content.raw ),
categories: [ templatePart.area ],
description: templatePart.description || '',
+ isCustom: templatePart.source === 'custom',
keywords: templatePart.keywords || [],
+ id: createTemplatePartId( templatePart.theme, templatePart.slug ),
name: createTemplatePartId( templatePart.theme, templatePart.slug ),
title: templatePart.title.rendered,
type: templatePart.type,
From 35a429d38539026050e7722aba7de71969c5f18b Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Tue, 4 Jul 2023 14:59:11 +1000
Subject: [PATCH 02/18] Add duplication for custom patterns and template parts
---
.../components/create-pattern-modal/index.js | 7 +-
.../page-patterns/duplicate-menu-item.js | 77 +++++++++++++++++++
.../src/components/page-patterns/grid-item.js | 5 ++
3 files changed, 87 insertions(+), 2 deletions(-)
create mode 100644 packages/edit-site/src/components/page-patterns/duplicate-menu-item.js
diff --git a/packages/edit-site/src/components/create-pattern-modal/index.js b/packages/edit-site/src/components/create-pattern-modal/index.js
index 46d734b86fdd1..753dccfb961dd 100644
--- a/packages/edit-site/src/components/create-pattern-modal/index.js
+++ b/packages/edit-site/src/components/create-pattern-modal/index.js
@@ -14,6 +14,7 @@ import { __ } from '@wordpress/i18n';
import { useState } from '@wordpress/element';
import { store as noticesStore } from '@wordpress/notices';
import { useDispatch } from '@wordpress/data';
+import { serialize } from '@wordpress/blocks';
/**
* Internal dependencies
@@ -21,9 +22,11 @@ import { useDispatch } from '@wordpress/data';
import { SYNC_TYPES, USER_PATTERN_CATEGORY } from '../page-patterns/utils';
export default function CreatePatternModal( {
+ blocks = [],
closeModal,
onCreate,
onError,
+ title,
} ) {
const [ name, setName ] = useState( '' );
const [ syncType, setSyncType ] = useState( SYNC_TYPES.unsynced );
@@ -52,7 +55,7 @@ export default function CreatePatternModal( {
'wp_block',
{
title: name || __( 'Untitled Pattern' ),
- content: '',
+ content: blocks?.length ? serialize( blocks ) : '',
status: 'publish',
meta:
syncType === SYNC_TYPES.unsynced
@@ -76,7 +79,7 @@ export default function CreatePatternModal( {
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
new file mode 100644
index 0000000000000..c38ad609df77e
--- /dev/null
+++ b/packages/edit-site/src/components/page-patterns/duplicate-menu-item.js
@@ -0,0 +1,77 @@
+/**
+ * WordPress dependencies
+ */
+import { MenuItem } from '@wordpress/components';
+import { useState } from '@wordpress/element';
+import { __ } from '@wordpress/i18n';
+import { privateApis as routerPrivateApis } from '@wordpress/router';
+
+/**
+ * Internal dependencies
+ */
+import CreatePatternModal from '../create-pattern-modal';
+import CreateTemplatePartModal from '../create-template-part-modal';
+import { TEMPLATE_PARTS, USER_PATTERNS } from './utils';
+import { unlock } from '../../lock-unlock';
+
+const { useHistory } = unlock( routerPrivateApis );
+
+export default function DuplicateMenuItem( { item, onClose } ) {
+ const history = useHistory();
+ const [ isModalOpen, setIsModalOpen ] = useState( false );
+
+ function handleCreatePattern( { pattern, categoryId } ) {
+ setIsModalOpen( false );
+ onClose();
+
+ history.push( {
+ postId: pattern.id,
+ postType: 'wp_block',
+ categoryType: 'wp_block',
+ categoryId,
+ canvas: 'edit',
+ } );
+ }
+
+ function handleCreateTemplatePart( templatePart ) {
+ setIsModalOpen( false );
+ onClose();
+
+ // Navigate to the created template part editor.
+ history.push( {
+ postId: templatePart.id,
+ postType: 'wp_template_part',
+ canvas: 'edit',
+ } );
+ }
+
+ function handleError() {
+ setIsModalOpen( false );
+ onClose();
+ }
+
+ return (
+ <>
+
+ { isModalOpen && item.type === USER_PATTERNS && (
+ setIsModalOpen( false ) }
+ onCreate={ handleCreatePattern }
+ onError={ handleError }
+ blocks={ item.blocks || [] }
+ title={ __( 'Duplicate pattern' ) }
+ />
+ ) }
+ { isModalOpen && item.type === TEMPLATE_PARTS && (
+ setIsModalOpen( false ) }
+ blocks={ item.blocks || [] }
+ onCreate={ handleCreateTemplatePart }
+ onError={ handleError }
+ />
+ ) }
+ >
+ );
+}
diff --git a/packages/edit-site/src/components/page-patterns/grid-item.js b/packages/edit-site/src/components/page-patterns/grid-item.js
index dd2eae6b1f28d..25a7a6c7ed3d9 100644
--- a/packages/edit-site/src/components/page-patterns/grid-item.js
+++ b/packages/edit-site/src/components/page-patterns/grid-item.js
@@ -36,6 +36,7 @@ import { DELETE, BACKSPACE } from '@wordpress/keycodes';
* Internal dependencies
*/
import RenameMenuItem from './rename-menu-item';
+import DuplicateMenuItem from './duplicate-menu-item';
import { PATTERNS, TEMPLATE_PARTS, USER_PATTERNS } from './utils';
import { store as editSiteStore } from '../../store';
import { useLink } from '../routes/link';
@@ -216,6 +217,10 @@ export default function GridItem( { categoryId, composite, icon, item } ) {
item={ item }
onClose={ onClose }
/>
+