diff --git a/packages/edit-site/src/components/page-patterns/dataviews-pattern-actions.js b/packages/edit-site/src/components/page-patterns/dataviews-pattern-actions.js
index 50baed5658e92..69e300cc2ffcf 100644
--- a/packages/edit-site/src/components/page-patterns/dataviews-pattern-actions.js
+++ b/packages/edit-site/src/components/page-patterns/dataviews-pattern-actions.js
@@ -1,397 +1,23 @@
-/**
- * External dependencies
- */
-import { paramCase as kebabCase } from 'change-case';
-import { downloadZip } from 'client-zip';
-
/**
* WordPress dependencies
*/
import { getQueryArgs } from '@wordpress/url';
-import { downloadBlob } from '@wordpress/blob';
import { __, _x, sprintf } from '@wordpress/i18n';
-import {
- Button,
- TextControl,
- __experimentalHStack as HStack,
- __experimentalVStack as VStack,
- __experimentalText as Text,
-} from '@wordpress/components';
-import { store as coreStore } from '@wordpress/core-data';
import { useDispatch } from '@wordpress/data';
-import { useState } from '@wordpress/element';
import { store as noticesStore } from '@wordpress/notices';
-import { decodeEntities } from '@wordpress/html-entities';
-import { store as reusableBlocksStore } from '@wordpress/reusable-blocks';
-import { store as editorStore } from '@wordpress/editor';
import { privateApis as routerPrivateApis } from '@wordpress/router';
-import { privateApis as patternsPrivateApis } from '@wordpress/patterns';
/**
* Internal dependencies
*/
import { unlock } from '../../lock-unlock';
-import { store as editSiteStore } from '../../store';
import {
- PATTERN_TYPES,
TEMPLATE_PART_POST_TYPE,
PATTERN_DEFAULT_CATEGORY,
} from '../../utils/constants';
import { CreateTemplatePartModalContents } from '../create-template-part-modal';
const { useHistory } = unlock( routerPrivateApis );
-const { CreatePatternModalContents, useDuplicatePatternProps } =
- unlock( patternsPrivateApis );
-
-function getJsonFromItem( item ) {
- return JSON.stringify(
- {
- __file: item.type,
- title: item.title || item.name,
- content: item.patternPost.content.raw,
- syncStatus: item.patternPost.wp_pattern_sync_status,
- },
- null,
- 2
- );
-}
-
-export const exportJSONaction = {
- id: 'export-pattern',
- label: __( 'Export as JSON' ),
- supportsBulk: true,
- isEligible: ( item ) => item.type === PATTERN_TYPES.user,
- callback: async ( items ) => {
- if ( items.length === 1 ) {
- return downloadBlob(
- `${ kebabCase( items[ 0 ].title || items[ 0 ].name ) }.json`,
- getJsonFromItem( items[ 0 ] ),
- 'application/json'
- );
- }
- const nameCount = {};
- const filesToZip = items.map( ( item ) => {
- const name = kebabCase( item.title || item.name );
- nameCount[ name ] = ( nameCount[ name ] || 0 ) + 1;
- return {
- name: `${
- name +
- ( nameCount[ name ] > 1
- ? '-' + ( nameCount[ name ] - 1 )
- : '' )
- }.json`,
- lastModified: new Date(),
- input: getJsonFromItem( item ),
- };
- } );
- return downloadBlob(
- __( 'patterns-export' ) + '.zip',
- await downloadZip( filesToZip ).blob(),
- 'application/zip'
- );
- },
-};
-
-export const renameAction = {
- id: 'rename-pattern',
- label: __( 'Rename' ),
- isEligible: ( item ) => {
- const isTemplatePart = item.type === TEMPLATE_PART_POST_TYPE;
- const isUserPattern = item.type === PATTERN_TYPES.user;
- const isCustomPattern =
- isUserPattern || ( isTemplatePart && item.isCustom );
- const hasThemeFile = isTemplatePart && item.templatePart.has_theme_file;
- return isCustomPattern && ! hasThemeFile;
- },
- RenderModal: ( { items, closeModal } ) => {
- const [ item ] = items;
- const [ title, setTitle ] = useState( () => item.title );
- const { editEntityRecord, saveEditedEntityRecord } =
- useDispatch( coreStore );
- const { createSuccessNotice, createErrorNotice } =
- useDispatch( noticesStore );
- async function onRename( event ) {
- event.preventDefault();
- try {
- await editEntityRecord( 'postType', item.type, item.id, {
- title,
- } );
- // Update state before saving rerenders the list.
- setTitle( '' );
- closeModal();
- // Persist edited entity.
- await saveEditedEntityRecord( 'postType', item.type, item.id, {
- throwOnError: true,
- } );
- createSuccessNotice(
- item.type === TEMPLATE_PART_POST_TYPE
- ? __( 'Template part renamed.' )
- : __( 'Pattern renamed.' ),
- { type: 'snackbar' }
- );
- } catch ( error ) {
- const fallbackErrorMessage =
- item.type === TEMPLATE_PART_POST_TYPE
- ? __(
- 'An error occurred while renaming the template part.'
- )
- : __( 'An error occurred while renaming the pattern.' );
- const errorMessage =
- error.message && error.code !== 'unknown_error'
- ? error.message
- : fallbackErrorMessage;
- createErrorNotice( errorMessage, { type: 'snackbar' } );
- }
- }
- return (
-
- );
- },
-};
-
-const canDeleteOrReset = ( item ) => {
- const isTemplatePart = item.type === TEMPLATE_PART_POST_TYPE;
- const isUserPattern = item.type === PATTERN_TYPES.user;
- return isUserPattern || ( isTemplatePart && item.isCustom );
-};
-
-export const deleteAction = {
- id: 'delete-pattern',
- label: __( 'Delete' ),
- isEligible: ( item ) => {
- const isTemplatePart = item.type === TEMPLATE_PART_POST_TYPE;
- const hasThemeFile = isTemplatePart && item.templatePart.has_theme_file;
- return canDeleteOrReset( item ) && ! hasThemeFile;
- },
- hideModalHeader: true,
- supportsBulk: true,
- RenderModal: ( { items, closeModal, onPerform } ) => {
- const { __experimentalDeleteReusableBlock } =
- useDispatch( reusableBlocksStore );
- const { createErrorNotice, createSuccessNotice } =
- useDispatch( noticesStore );
- const { removeTemplates } = unlock( useDispatch( editorStore ) );
-
- const deletePattern = async () => {
- const promiseResult = await Promise.allSettled(
- items.map( ( item ) => {
- return __experimentalDeleteReusableBlock( item.id );
- } )
- );
- // If all the promises were fulfilled with success.
- if (
- promiseResult.every( ( { status } ) => status === 'fulfilled' )
- ) {
- let successMessage;
- if ( promiseResult.length === 1 ) {
- successMessage = sprintf(
- /* translators: The posts's title. */
- __( '"%s" deleted.' ),
- items[ 0 ].title
- );
- } else {
- successMessage = __( 'The patterns were deleted.' );
- }
- createSuccessNotice( successMessage, {
- type: 'snackbar',
- id: 'edit-site-page-trashed',
- } );
- } else {
- // If there was at lease one failure.
- let errorMessage;
- // If we were trying to delete a single pattern.
- if ( promiseResult.length === 1 ) {
- if ( promiseResult[ 0 ].reason?.message ) {
- errorMessage = promiseResult[ 0 ].reason.message;
- } else {
- errorMessage = __(
- 'An error occurred while deleting the pattern.'
- );
- }
- // If we were trying to delete multiple patterns.
- } else {
- const errorMessages = new Set();
- const failedPromises = promiseResult.filter(
- ( { status } ) => status === 'rejected'
- );
- for ( const failedPromise of failedPromises ) {
- if ( failedPromise.reason?.message ) {
- errorMessages.add( failedPromise.reason.message );
- }
- }
- if ( errorMessages.size === 0 ) {
- errorMessage = __(
- 'An error occurred while deleting the patterns.'
- );
- } else if ( errorMessages.size === 1 ) {
- errorMessage = sprintf(
- /* translators: %s: an error message */
- __(
- 'An error occurred while deleting the patterns: %s'
- ),
- [ ...errorMessages ][ 0 ]
- );
- } else {
- errorMessage = sprintf(
- /* translators: %s: a list of comma separated error messages */
- __(
- 'Some errors occurred while deleting the patterns: %s'
- ),
- [ ...errorMessages ].join( ',' )
- );
- }
- createErrorNotice( errorMessage, {
- type: 'snackbar',
- } );
- }
- }
- };
- const deleteItem = () => {
- if ( items[ 0 ].type === TEMPLATE_PART_POST_TYPE ) {
- removeTemplates( items );
- } else {
- deletePattern();
- }
- if ( onPerform ) {
- onPerform();
- }
- closeModal();
- };
- let questionMessage;
- if ( items.length === 1 ) {
- questionMessage = sprintf(
- // translators: %s: The page's title.
- __( 'Are you sure you want to delete "%s"?' ),
- decodeEntities( items[ 0 ].title || items[ 0 ].name )
- );
- } else if (
- items.length > 1 &&
- items[ 0 ].type === TEMPLATE_PART_POST_TYPE
- ) {
- questionMessage = sprintf(
- // translators: %d: The number of template parts (2 or more).
- __( 'Are you sure you want to delete %d template parts?' ),
- items.length
- );
- } else {
- questionMessage = sprintf(
- // translators: %d: The number of patterns (2 or more).
- __( 'Are you sure you want to delete %d patterns?' ),
- items.length
- );
- }
- return (
-
- { questionMessage }
-
-
-
-
-
- );
- },
-};
-
-export const resetAction = {
- id: 'reset-action',
- label: __( 'Reset' ),
- isEligible: ( item ) => {
- const isTemplatePart = item.type === TEMPLATE_PART_POST_TYPE;
- const hasThemeFile = isTemplatePart && item.templatePart.has_theme_file;
- return canDeleteOrReset( item ) && hasThemeFile;
- },
- hideModalHeader: true,
- RenderModal: ( { items, closeModal } ) => {
- const [ item ] = items;
- const { removeTemplate } = useDispatch( editSiteStore );
- return (
-
-
- { __( 'Reset to default and clear all customizations?' ) }
-
-
-
-
-
-
- );
- },
-};
-
-export const duplicatePatternAction = {
- id: 'duplicate-pattern',
- label: _x( 'Duplicate', 'action label' ),
- isEligible: ( item ) => item.type !== TEMPLATE_PART_POST_TYPE,
- modalHeader: _x( 'Duplicate pattern', 'action label' ),
- RenderModal: ( { items, closeModal } ) => {
- const [ item ] = items;
- const { categoryId = PATTERN_DEFAULT_CATEGORY } = getQueryArgs(
- window.location.href
- );
- const isThemePattern = item.type === PATTERN_TYPES.theme;
- const history = useHistory();
- function onPatternSuccess( { pattern } ) {
- history.push( {
- categoryType: PATTERN_TYPES.theme,
- categoryId,
- postType: PATTERN_TYPES.user,
- postId: pattern.id,
- } );
- closeModal();
- }
- const duplicatedProps = useDuplicatePatternProps( {
- pattern: isThemePattern ? item : item.patternPost,
- onSuccess: onPatternSuccess,
- } );
- return (
-
- );
- },
-};
export const duplicateTemplatePartAction = {
id: 'duplicate-template-part',
diff --git a/packages/edit-site/src/components/page-patterns/index.js b/packages/edit-site/src/components/page-patterns/index.js
index 6fa84df7a0171..a643e45c429ad 100644
--- a/packages/edit-site/src/components/page-patterns/index.js
+++ b/packages/edit-site/src/components/page-patterns/index.js
@@ -55,14 +55,7 @@ import {
ENUMERATION_TYPE,
OPERATOR_IS,
} from '../../utils/constants';
-import {
- exportJSONaction,
- renameAction,
- resetAction,
- deleteAction,
- duplicatePatternAction,
- duplicateTemplatePartAction,
-} from './dataviews-pattern-actions';
+import { duplicateTemplatePartAction } from './dataviews-pattern-actions';
import usePatternSettings from './use-pattern-settings';
import { unlock } from '../../lock-unlock';
import usePatterns from './use-patterns';
@@ -440,42 +433,78 @@ export default function DataviewsPatterns() {
const history = useHistory();
const onActionPerformed = useCallback(
( actionId, items ) => {
- if ( actionId === 'edit-post' ) {
- const post = items[ 0 ];
- history.push( {
- postId: post.id,
- postType: post.type,
- categoryId,
- categoryType: type,
- canvas: 'edit',
- } );
+ switch ( actionId ) {
+ case 'edit-post':
+ {
+ const post = items[ 0 ];
+ history.push( {
+ postId: post.id,
+ postType: post.type,
+ categoryId,
+ categoryType: type,
+ canvas: 'edit',
+ } );
+ }
+ break;
+ case 'duplicate-pattern':
+ {
+ const pattern = items[ 0 ];
+ history.push( {
+ categoryType: PATTERN_TYPES.theme,
+ categoryId,
+ postType: PATTERN_TYPES.user,
+ postId: pattern.id,
+ } );
+ }
+ break;
}
},
- [ history ]
- );
- const [ editAction, viewRevisionsAction ] = usePostActions(
- onActionPerformed,
- [ 'edit-post', 'view-post-revisions' ]
+ [ categoryId, history, type ]
);
+ const [
+ editAction,
+ renamePatternAction,
+ duplicatePatternAction,
+ exportPatternAsJSONAction,
+ viewRevisionsAction,
+ resetTemplatePartAction,
+ deletePatternAction,
+ ] = usePostActions( onActionPerformed, [
+ 'edit-post',
+ 'rename-pattern',
+ 'duplicate-pattern',
+ 'export-pattern',
+ 'view-post-revisions',
+ 'reset-template-part',
+ 'delete-pattern',
+ ] );
const actions = useMemo( () => {
if ( type === TEMPLATE_PART_POST_TYPE ) {
return [
editAction,
- renameAction,
+ renamePatternAction,
duplicateTemplatePartAction,
viewRevisionsAction,
- resetAction,
- deleteAction,
+ resetTemplatePartAction,
+ deletePatternAction,
];
}
return [
- renameAction,
+ renamePatternAction,
duplicatePatternAction,
- exportJSONaction,
- resetAction,
- deleteAction,
+ exportPatternAsJSONAction,
+ deletePatternAction,
];
- }, [ type, editAction, viewRevisionsAction ] );
+ }, [
+ type,
+ renamePatternAction,
+ duplicatePatternAction,
+ exportPatternAsJSONAction,
+ deletePatternAction,
+ editAction,
+ viewRevisionsAction,
+ resetTemplatePartAction,
+ ] );
const onChangeView = useCallback(
( newView ) => {
if ( newView.type !== view.type ) {
diff --git a/packages/editor/package.json b/packages/editor/package.json
index 9b4068d2cd12c..5fa18d7b98eab 100644
--- a/packages/editor/package.json
+++ b/packages/editor/package.json
@@ -64,7 +64,9 @@
"@wordpress/url": "file:../url",
"@wordpress/warning": "file:../warning",
"@wordpress/wordcount": "file:../wordcount",
+ "change-case": "^4.1.2",
"classnames": "^2.3.1",
+ "client-zip": "^2.4.4",
"date-fns": "^3.6.0",
"memize": "^2.1.0",
"react-autosize-textarea": "^7.1.0",
diff --git a/packages/editor/src/components/post-actions/actions.js b/packages/editor/src/components/post-actions/actions.js
index a6ff1f77486c9..96eedff7a7945 100644
--- a/packages/editor/src/components/post-actions/actions.js
+++ b/packages/editor/src/components/post-actions/actions.js
@@ -1,3 +1,9 @@
+/**
+ * External dependencies
+ */
+import { paramCase as kebabCase } from 'change-case';
+import { downloadZip } from 'client-zip';
+
/**
* WordPress dependencies
*/
@@ -6,9 +12,11 @@ import { addQueryArgs } from '@wordpress/url';
import { useDispatch } from '@wordpress/data';
import { decodeEntities } from '@wordpress/html-entities';
import { store as coreStore } from '@wordpress/core-data';
-import { __, _n, sprintf } from '@wordpress/i18n';
+import { __, _n, sprintf, _x } from '@wordpress/i18n';
import { store as noticesStore } from '@wordpress/notices';
import { useMemo, useState } from '@wordpress/element';
+import { downloadBlob } from '@wordpress/blob';
+import { store as reusableBlocksStore } from '@wordpress/reusable-blocks';
import {
Button,
@@ -17,15 +25,31 @@ import {
__experimentalHStack as HStack,
__experimentalVStack as VStack,
} from '@wordpress/components';
+import { privateApis as patternPrivateApis } from '@wordpress/patterns';
/**
* Internal dependencies
*/
-import { TEMPLATE_ORIGINS, TEMPLATE_POST_TYPE } from '../../store/constants';
+import {
+ TEMPLATE_ORIGINS,
+ TEMPLATE_POST_TYPE,
+ TEMPLATE_PART_POST_TYPE,
+} from '../../store/constants';
import { store as editorStore } from '../../store';
import { unlock } from '../../lock-unlock';
import isTemplateRevertable from '../../store/utils/is-template-revertable';
+// Patterns.
+export const {
+ PATTERN_TYPES,
+ PATTERN_DEFAULT_CATEGORY,
+ PATTERN_USER_CATEGORY,
+ EXCLUDED_PATTERN_SOURCES,
+ PATTERN_SYNC_TYPES,
+ CreatePatternModalContents,
+ useDuplicatePatternProps,
+} = unlock( patternPrivateApis );
+
function getItemTitle( item ) {
if ( typeof item.title === 'string' ) {
return decodeEntities( item.title );
@@ -768,6 +792,348 @@ const renameTemplateAction = {
},
};
+function getJsonFromItem( item ) {
+ return JSON.stringify(
+ {
+ __file: item.type,
+ title: item.title || item.name,
+ content: item.patternPost.content.raw,
+ syncStatus: item.patternPost.wp_pattern_sync_status,
+ },
+ null,
+ 2
+ );
+}
+
+const exportPatternAsJSONAction = {
+ id: 'export-pattern',
+ label: __( 'Export as JSON' ),
+ supportsBulk: true,
+ isEligible: ( item ) => item.type === PATTERN_TYPES.user,
+ callback: async ( items, onActionPerformed ) => {
+ if ( items.length === 1 ) {
+ return downloadBlob(
+ `${ kebabCase( items[ 0 ].title || items[ 0 ].name ) }.json`,
+ getJsonFromItem( items[ 0 ] ),
+ 'application/json'
+ );
+ }
+ const nameCount = {};
+ const filesToZip = items.map( ( item ) => {
+ const name = kebabCase( item.title || item.name );
+ nameCount[ name ] = ( nameCount[ name ] || 0 ) + 1;
+ return {
+ name: `${
+ name +
+ ( nameCount[ name ] > 1
+ ? '-' + ( nameCount[ name ] - 1 )
+ : '' )
+ }.json`,
+ lastModified: new Date(),
+ input: getJsonFromItem( item ),
+ };
+ } );
+ downloadBlob(
+ __( 'patterns-export' ) + '.zip',
+ await downloadZip( filesToZip ).blob(),
+ 'application/zip'
+ );
+ onActionPerformed?.( items );
+ },
+};
+
+const renamePatternAction = {
+ id: 'rename-pattern',
+ label: __( 'Rename' ),
+ isEligible: ( item ) => {
+ const isTemplatePart = item.type === TEMPLATE_PART_POST_TYPE;
+ const isUserPattern = item.type === PATTERN_TYPES.user;
+ const isCustomPattern =
+ isUserPattern || ( isTemplatePart && item.isCustom );
+ const hasThemeFile = isTemplatePart && item.templatePart.has_theme_file;
+ return isCustomPattern && ! hasThemeFile;
+ },
+ RenderModal: ( { items, closeModal, onActionPerformed } ) => {
+ const [ item ] = items;
+ const [ title, setTitle ] = useState( () => item.title );
+ const { editEntityRecord, saveEditedEntityRecord } =
+ useDispatch( coreStore );
+ const { createSuccessNotice, createErrorNotice } =
+ useDispatch( noticesStore );
+ async function onRename( event ) {
+ event.preventDefault();
+ try {
+ await editEntityRecord( 'postType', item.type, item.id, {
+ title,
+ } );
+ // Update state before saving rerenders the list.
+ setTitle( '' );
+ closeModal();
+ // Persist edited entity.
+ await saveEditedEntityRecord( 'postType', item.type, item.id, {
+ throwOnError: true,
+ } );
+ createSuccessNotice(
+ item.type === TEMPLATE_PART_POST_TYPE
+ ? __( 'Template part renamed.' )
+ : __( 'Pattern renamed.' ),
+ { type: 'snackbar' }
+ );
+ onActionPerformed?.( items );
+ } catch ( error ) {
+ const fallbackErrorMessage =
+ item.type === TEMPLATE_PART_POST_TYPE
+ ? __(
+ 'An error occurred while renaming the template part.'
+ )
+ : __( 'An error occurred while renaming the pattern.' );
+ const errorMessage =
+ error.message && error.code !== 'unknown_error'
+ ? error.message
+ : fallbackErrorMessage;
+ createErrorNotice( errorMessage, { type: 'snackbar' } );
+ }
+ }
+ return (
+
+ );
+ },
+};
+
+const canDeleteOrReset = ( item ) => {
+ const isTemplatePart = item.type === TEMPLATE_PART_POST_TYPE;
+ const isUserPattern = item.type === PATTERN_TYPES.user;
+ return isUserPattern || ( isTemplatePart && item.isCustom );
+};
+
+const deletePatternAction = {
+ id: 'delete-pattern',
+ label: __( 'Delete' ),
+ isEligible: ( item ) => {
+ const isTemplatePart = item.type === TEMPLATE_PART_POST_TYPE;
+ const hasThemeFile = isTemplatePart && item.templatePart.has_theme_file;
+ return canDeleteOrReset( item ) && ! hasThemeFile;
+ },
+ hideModalHeader: true,
+ supportsBulk: true,
+ RenderModal: ( { items, closeModal, onActionPerformed } ) => {
+ const { __experimentalDeleteReusableBlock } =
+ useDispatch( reusableBlocksStore );
+ const { createErrorNotice, createSuccessNotice } =
+ useDispatch( noticesStore );
+ const { removeTemplates } = unlock( useDispatch( editorStore ) );
+
+ const deletePattern = async () => {
+ const promiseResult = await Promise.allSettled(
+ items.map( ( item ) => {
+ return __experimentalDeleteReusableBlock( item.id );
+ } )
+ );
+ // If all the promises were fulfilled with success.
+ if (
+ promiseResult.every( ( { status } ) => status === 'fulfilled' )
+ ) {
+ let successMessage;
+ if ( promiseResult.length === 1 ) {
+ successMessage = sprintf(
+ /* translators: The posts's title. */
+ __( '"%s" deleted.' ),
+ items[ 0 ].title
+ );
+ } else {
+ successMessage = __( 'The patterns were deleted.' );
+ }
+ createSuccessNotice( successMessage, {
+ type: 'snackbar',
+ id: 'edit-site-page-trashed',
+ } );
+ } else {
+ // If there was at lease one failure.
+ let errorMessage;
+ // If we were trying to delete a single pattern.
+ if ( promiseResult.length === 1 ) {
+ if ( promiseResult[ 0 ].reason?.message ) {
+ errorMessage = promiseResult[ 0 ].reason.message;
+ } else {
+ errorMessage = __(
+ 'An error occurred while deleting the pattern.'
+ );
+ }
+ // If we were trying to delete multiple patterns.
+ } else {
+ const errorMessages = new Set();
+ const failedPromises = promiseResult.filter(
+ ( { status } ) => status === 'rejected'
+ );
+ for ( const failedPromise of failedPromises ) {
+ if ( failedPromise.reason?.message ) {
+ errorMessages.add( failedPromise.reason.message );
+ }
+ }
+ if ( errorMessages.size === 0 ) {
+ errorMessage = __(
+ 'An error occurred while deleting the patterns.'
+ );
+ } else if ( errorMessages.size === 1 ) {
+ errorMessage = sprintf(
+ /* translators: %s: an error message */
+ __(
+ 'An error occurred while deleting the patterns: %s'
+ ),
+ [ ...errorMessages ][ 0 ]
+ );
+ } else {
+ errorMessage = sprintf(
+ /* translators: %s: a list of comma separated error messages */
+ __(
+ 'Some errors occurred while deleting the patterns: %s'
+ ),
+ [ ...errorMessages ].join( ',' )
+ );
+ }
+ createErrorNotice( errorMessage, {
+ type: 'snackbar',
+ } );
+ }
+ }
+ };
+ const deleteItem = () => {
+ if ( items[ 0 ].type === TEMPLATE_PART_POST_TYPE ) {
+ removeTemplates( items );
+ } else {
+ deletePattern();
+ }
+ if ( onActionPerformed ) {
+ onActionPerformed( items );
+ }
+ closeModal();
+ };
+ let questionMessage;
+ if ( items.length === 1 ) {
+ questionMessage = sprintf(
+ // translators: %s: The page's title.
+ __( 'Are you sure you want to delete "%s"?' ),
+ decodeEntities( items[ 0 ].title || items[ 0 ].name )
+ );
+ } else if (
+ items.length > 1 &&
+ items[ 0 ].type === TEMPLATE_PART_POST_TYPE
+ ) {
+ questionMessage = sprintf(
+ // translators: %d: The number of template parts (2 or more).
+ __( 'Are you sure you want to delete %d template parts?' ),
+ items.length
+ );
+ } else {
+ questionMessage = sprintf(
+ // translators: %d: The number of patterns (2 or more).
+ __( 'Are you sure you want to delete %d patterns?' ),
+ items.length
+ );
+ }
+ return (
+
+ { questionMessage }
+
+
+
+
+
+ );
+ },
+};
+
+const resetTemplatePartAction = {
+ id: 'reset-template-part',
+ label: __( 'Reset' ),
+ isEligible: ( item ) => {
+ const isTemplatePart = item.type === TEMPLATE_PART_POST_TYPE;
+ const hasThemeFile = isTemplatePart && item.templatePart.has_theme_file;
+ return canDeleteOrReset( item ) && hasThemeFile;
+ },
+ hideModalHeader: true,
+ RenderModal: ( { items, closeModal } ) => {
+ const { removeTemplates } = unlock( useDispatch( editorStore ) );
+ return (
+
+
+ { __( 'Reset to default and clear all customizations?' ) }
+
+
+
+
+
+
+ );
+ },
+};
+
+const duplicatePatternAction = {
+ id: 'duplicate-pattern',
+ label: _x( 'Duplicate', 'action label' ),
+ isEligible: ( item ) => item.type !== TEMPLATE_PART_POST_TYPE,
+ modalHeader: _x( 'Duplicate pattern', 'action label' ),
+ RenderModal: ( { items, closeModal, onActionPerformed } ) => {
+ const [ item ] = items;
+ const isThemePattern = item.type === PATTERN_TYPES.theme;
+ function onPatternSuccess( { pattern } ) {
+ onActionPerformed?.( [ pattern ] );
+ closeModal();
+ }
+ const duplicatedProps = useDuplicatePatternProps( {
+ pattern: isThemePattern ? item : item.patternPost,
+ onSuccess: onPatternSuccess,
+ } );
+ return (
+
+ );
+ },
+};
+
export function usePostActions( onActionPerformed, actionIds = null ) {
const permanentlyDeletePostAction = usePermanentlyDeletePostAction();
const restorePostAction = useRestorePostAction();
@@ -777,13 +1143,18 @@ export function usePostActions( onActionPerformed, actionIds = null ) {
const defaultActions = [
editPostAction,
resetTemplateAction,
+ resetTemplatePartAction,
viewPostAction,
+ duplicatePatternAction,
restorePostAction,
+ exportPatternAsJSONAction,
deleteTemplateAction,
+ deletePatternAction,
permanentlyDeletePostAction,
postRevisionsAction,
renamePostAction,
renameTemplateAction,
+ renamePatternAction,
trashPostAction,
];