From cbe794cfd6b43ef270f2b8f24b9bd5f5f75492cd Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Thu, 30 May 2024 15:01:11 +0200 Subject: [PATCH] Update: Move duplicate pattern and template part actions to the editor package (#61879) Co-authored-by: jorgefilipecosta --- packages/base-styles/_z-index.scss | 2 +- .../src/components/add-new-pattern/index.js | 3 +- .../dataviews-pattern-actions.js | 76 ------------------- .../src/components/page-patterns/index.js | 14 +--- .../convert-to-template-part.js | 5 +- packages/edit-site/src/style.scss | 1 - .../src/utils/template-part-create.js | 67 ---------------- .../create-template-part-modal/index.js | 22 +++--- .../create-template-part-modal/style.scss | 14 ++-- .../create-template-part-modal/test/utils.js} | 5 +- .../create-template-part-modal/utils.js | 67 ++++++++++++++++ .../src/components/post-actions/actions.js | 65 +++++++++++++++- packages/editor/src/private-apis.js | 2 + packages/editor/src/store/constants.js | 1 + packages/editor/src/style.scss | 1 + 15 files changed, 163 insertions(+), 182 deletions(-) delete mode 100644 packages/edit-site/src/components/page-patterns/dataviews-pattern-actions.js rename packages/{edit-site => editor}/src/components/create-template-part-modal/index.js (85%) rename packages/{edit-site => editor}/src/components/create-template-part-modal/style.scss (69%) rename packages/{edit-site/src/utils/test/template-part-create.js => editor/src/components/create-template-part-modal/test/utils.js} (93%) create mode 100644 packages/editor/src/components/create-template-part-modal/utils.js diff --git a/packages/base-styles/_z-index.scss b/packages/base-styles/_z-index.scss index c58976f0964e59..691fff09fb3b5e 100644 --- a/packages/base-styles/_z-index.scss +++ b/packages/base-styles/_z-index.scss @@ -125,7 +125,7 @@ $z-layers: ( // Should be above the popover (dropdown) ".reusable-blocks-menu-items__convert-modal": 1000001, ".patterns-menu-items__convert-modal": 1000001, - ".edit-site-create-template-part-modal": 1000001, + ".editor-create-template-part-modal": 1000001, ".block-editor-block-lock-modal": 1000001, ".block-editor-template-part__selection-modal": 1000001, ".block-editor-block-rename-modal": 1000001, diff --git a/packages/edit-site/src/components/add-new-pattern/index.js b/packages/edit-site/src/components/add-new-pattern/index.js index 95823c20748bd0..33d1b8cbca1c2d 100644 --- a/packages/edit-site/src/components/add-new-pattern/index.js +++ b/packages/edit-site/src/components/add-new-pattern/index.js @@ -13,11 +13,11 @@ import { } from '@wordpress/patterns'; import { store as noticesStore } from '@wordpress/notices'; import { store as coreStore } from '@wordpress/core-data'; +import { privateApis as editorPrivateApis } from '@wordpress/editor'; /** * Internal dependencies */ -import CreateTemplatePartModal from '../create-template-part-modal'; import { unlock } from '../../lock-unlock'; import { PATTERN_TYPES, @@ -29,6 +29,7 @@ const { useHistory } = unlock( routerPrivateApis ); const { CreatePatternModal, useAddPatternCategory } = unlock( editPatternsPrivateApis ); +const { CreateTemplatePartModal } = unlock( editorPrivateApis ); export default function AddNewPattern() { const history = useHistory(); 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 deleted file mode 100644 index e01dca97c3be46..00000000000000 --- a/packages/edit-site/src/components/page-patterns/dataviews-pattern-actions.js +++ /dev/null @@ -1,76 +0,0 @@ -/** - * WordPress dependencies - */ -import { __, _x, sprintf } from '@wordpress/i18n'; - -import { useDispatch } from '@wordpress/data'; -import { store as noticesStore } from '@wordpress/notices'; -import { privateApis as patternsPrivateApis } from '@wordpress/patterns'; - -/** - * Internal dependencies - */ -import { unlock } from '../../lock-unlock'; -import { PATTERN_TYPES, TEMPLATE_PART_POST_TYPE } from '../../utils/constants'; -import { CreateTemplatePartModalContents } from '../create-template-part-modal'; - -const { CreatePatternModalContents, useDuplicatePatternProps } = - unlock( patternsPrivateApis ); - -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 isThemePattern = item.type === PATTERN_TYPES.theme; - const duplicatedProps = useDuplicatePatternProps( { - pattern: isThemePattern ? item : item.patternPost, - onSuccess: () => closeModal(), - } ); - return ( - - ); - }, -}; - -export const duplicateTemplatePartAction = { - id: 'duplicate-template-part', - label: _x( 'Duplicate', 'action label' ), - isEligible: ( item ) => item.type === TEMPLATE_PART_POST_TYPE, - modalHeader: _x( 'Duplicate template part', 'action label' ), - RenderModal: ( { items, closeModal } ) => { - const [ item ] = items; - const { createSuccessNotice } = useDispatch( noticesStore ); - function onTemplatePartSuccess() { - 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' } - ); - closeModal(); - } - return ( - - ); - }, -}; diff --git a/packages/edit-site/src/components/page-patterns/index.js b/packages/edit-site/src/components/page-patterns/index.js index eea2fd61221909..223ede9ac1d988 100644 --- a/packages/edit-site/src/components/page-patterns/index.js +++ b/packages/edit-site/src/components/page-patterns/index.js @@ -46,10 +46,6 @@ import { PATTERN_DEFAULT_CATEGORY, OPERATOR_IS, } from '../../utils/constants'; -import { - duplicatePatternAction, - duplicateTemplatePartAction, -} from './dataviews-pattern-actions'; import usePatternSettings from './use-pattern-settings'; import { unlock } from '../../lock-unlock'; import usePatterns from './use-patterns'; @@ -361,15 +357,9 @@ export default function DataviewsPatterns() { const actions = useMemo( () => { if ( type === TEMPLATE_PART_POST_TYPE ) { - return [ - editAction, - duplicateTemplatePartAction, - ...templatePartActions, - ].filter( Boolean ); + return [ editAction, ...templatePartActions ].filter( Boolean ); } - return [ editAction, duplicatePatternAction, ...patternActions ].filter( - Boolean - ); + return [ editAction, ...patternActions ].filter( Boolean ); }, [ editAction, type, templatePartActions, patternActions ] ); const onChangeView = useCallback( ( newView ) => { diff --git a/packages/edit-site/src/components/template-part-converter/convert-to-template-part.js b/packages/edit-site/src/components/template-part-converter/convert-to-template-part.js index e8a80ee4e299ae..5dc68e07c85f3f 100644 --- a/packages/edit-site/src/components/template-part-converter/convert-to-template-part.js +++ b/packages/edit-site/src/components/template-part-converter/convert-to-template-part.js @@ -9,13 +9,16 @@ import { __ } from '@wordpress/i18n'; import { useState } from '@wordpress/element'; import { store as noticesStore } from '@wordpress/notices'; import { symbolFilled } from '@wordpress/icons'; +import { privateApis as editorPrivateApis } from '@wordpress/editor'; /** * Internal dependencies */ -import CreateTemplatePartModal from '../create-template-part-modal'; +import { unlock } from '../../lock-unlock'; import { store as editSiteStore } from '../../store'; +const { CreateTemplatePartModal } = unlock( editorPrivateApis ); + export default function ConvertToTemplatePart( { clientIds, blocks } ) { const [ isModalOpen, setIsModalOpen ] = useState( false ); const { replaceBlocks } = useDispatch( blockEditorStore ); diff --git a/packages/edit-site/src/style.scss b/packages/edit-site/src/style.scss index 7416b055cae7ce..20eb3397b00ed9 100644 --- a/packages/edit-site/src/style.scss +++ b/packages/edit-site/src/style.scss @@ -13,7 +13,6 @@ @import "./components/page-templates/style.scss"; @import "./components/table/style.scss"; @import "./components/editor/style.scss"; -@import "./components/create-template-part-modal/style.scss"; @import "./components/welcome-guide/style.scss"; @import "./components/layout/style.scss"; @import "./components/save-hub/style.scss"; diff --git a/packages/edit-site/src/utils/template-part-create.js b/packages/edit-site/src/utils/template-part-create.js index 955519a91a2344..e69de29bb2d1d6 100644 --- a/packages/edit-site/src/utils/template-part-create.js +++ b/packages/edit-site/src/utils/template-part-create.js @@ -1,67 +0,0 @@ -/** - * External dependencies - */ -import { paramCase as kebabCase } from 'change-case'; - -/** - * WordPress dependencies - */ -import { useSelect } from '@wordpress/data'; -import { store as coreStore } from '@wordpress/core-data'; - -/** - * Internal dependencies - */ -import { TEMPLATE_PART_POST_TYPE } from './constants'; - -export const useExistingTemplateParts = () => { - return useSelect( - ( select ) => - select( coreStore ).getEntityRecords( - 'postType', - TEMPLATE_PART_POST_TYPE, - { - per_page: -1, - } - ), - [] - ); -}; - -/** - * Return a unique template part title based on - * the given title and existing template parts. - * - * @param {string} title The original template part title. - * @param {Object} templateParts The array of template part entities. - * @return {string} A unique template part title. - */ -export const getUniqueTemplatePartTitle = ( title, templateParts ) => { - const lowercaseTitle = title.toLowerCase(); - const existingTitles = templateParts.map( ( templatePart ) => - templatePart.title.rendered.toLowerCase() - ); - - if ( ! existingTitles.includes( lowercaseTitle ) ) { - return title; - } - - let suffix = 2; - while ( existingTitles.includes( `${ lowercaseTitle } ${ suffix }` ) ) { - suffix++; - } - - return `${ title } ${ suffix }`; -}; - -/** - * Get a valid slug for a template part. - * Currently template parts only allow latin chars. - * The fallback slug will receive suffix by default. - * - * @param {string} title The template part title. - * @return {string} A valid template part slug. - */ -export const getCleanTemplatePartSlug = ( title ) => { - return kebabCase( title ).replace( /[^\w-]+/g, '' ) || 'wp-custom-part'; -}; diff --git a/packages/edit-site/src/components/create-template-part-modal/index.js b/packages/editor/src/components/create-template-part-modal/index.js similarity index 85% rename from packages/edit-site/src/components/create-template-part-modal/index.js rename to packages/editor/src/components/create-template-part-modal/index.js index a74225dd81404f..276ca200d39d0e 100644 --- a/packages/edit-site/src/components/create-template-part-modal/index.js +++ b/packages/editor/src/components/create-template-part-modal/index.js @@ -19,7 +19,6 @@ import { import { __ } from '@wordpress/i18n'; import { useState } from '@wordpress/element'; import { useInstanceId } from '@wordpress/compose'; -import { store as editorStore } from '@wordpress/editor'; import { store as noticesStore } from '@wordpress/notices'; import { store as coreStore } from '@wordpress/core-data'; import { check } from '@wordpress/icons'; @@ -28,15 +27,16 @@ import { serialize } from '@wordpress/blocks'; /** * Internal dependencies */ +import { store as editorStore } from '../../store'; import { TEMPLATE_PART_POST_TYPE, TEMPLATE_PART_AREA_DEFAULT_CATEGORY, -} from '../../utils/constants'; +} from '../../store/constants'; import { useExistingTemplateParts, getUniqueTemplatePartTitle, getCleanTemplatePartSlug, -} from '../../utils/template-part-create'; +} from './utils'; export default function CreateTemplatePartModal( { modalTitle, @@ -52,7 +52,7 @@ export default function CreateTemplatePartModal( { @@ -142,13 +142,13 @@ export function CreateTemplatePartModalContents( { /> @@ -157,18 +157,18 @@ export function CreateTemplatePartModalContents( { - + { label }
{ description }
- + { area === value && ( ) } diff --git a/packages/edit-site/src/components/create-template-part-modal/style.scss b/packages/editor/src/components/create-template-part-modal/style.scss similarity index 69% rename from packages/edit-site/src/components/create-template-part-modal/style.scss rename to packages/editor/src/components/create-template-part-modal/style.scss index 1dba3be142cea2..907e42bf52ea47 100644 --- a/packages/edit-site/src/components/create-template-part-modal/style.scss +++ b/packages/editor/src/components/create-template-part-modal/style.scss @@ -1,5 +1,5 @@ -.edit-site-create-template-part-modal { - z-index: z-index(".edit-site-create-template-part-modal"); +.editor-create-template-part-modal { + z-index: z-index(".editor-create-template-part-modal"); .components-modal__frame { @include break-small { @@ -8,12 +8,12 @@ } } -.edit-site-create-template-part-modal__area-radio-group { +.editor-create-template-part-modal__area-radio-group { width: 100%; border: $border-width solid $gray-700; border-radius: 2px; - .components-button.edit-site-create-template-part-modal__area-radio { + .components-button.editor-create-template-part-modal__area-radio { display: block; width: 100%; height: 100%; @@ -46,12 +46,12 @@ color: $gray-900; cursor: auto; - .edit-site-create-template-part-modal__option-label div { + .editor-create-template-part-modal__option-label div { color: $gray-600; } } - .edit-site-create-template-part-modal__option-label { + .editor-create-template-part-modal__option-label { padding-top: $grid-unit-05; white-space: normal; @@ -61,7 +61,7 @@ } } - .edit-site-create-template-part-modal__checkbox { + .editor-create-template-part-modal__checkbox { margin-left: auto; min-width: $grid-unit-30; } diff --git a/packages/edit-site/src/utils/test/template-part-create.js b/packages/editor/src/components/create-template-part-modal/test/utils.js similarity index 93% rename from packages/edit-site/src/utils/test/template-part-create.js rename to packages/editor/src/components/create-template-part-modal/test/utils.js index e39513ccf7e4f4..7e33c16f46d8e8 100644 --- a/packages/edit-site/src/utils/test/template-part-create.js +++ b/packages/editor/src/components/create-template-part-modal/test/utils.js @@ -1,10 +1,7 @@ /** * Internal dependencies */ -import { - getUniqueTemplatePartTitle, - getCleanTemplatePartSlug, -} from '../template-part-create'; +import { getUniqueTemplatePartTitle, getCleanTemplatePartSlug } from '../utils'; describe( 'getUniqueTemplatePartTitle', () => { it( 'should return the title if it is unique', () => { diff --git a/packages/editor/src/components/create-template-part-modal/utils.js b/packages/editor/src/components/create-template-part-modal/utils.js new file mode 100644 index 00000000000000..02f24cf17d2be9 --- /dev/null +++ b/packages/editor/src/components/create-template-part-modal/utils.js @@ -0,0 +1,67 @@ +/** + * External dependencies + */ +import { paramCase as kebabCase } from 'change-case'; + +/** + * WordPress dependencies + */ +import { useSelect } from '@wordpress/data'; +import { store as coreStore } from '@wordpress/core-data'; + +/** + * Internal dependencies + */ +import { TEMPLATE_PART_POST_TYPE } from '../../store/constants'; + +export const useExistingTemplateParts = () => { + return useSelect( + ( select ) => + select( coreStore ).getEntityRecords( + 'postType', + TEMPLATE_PART_POST_TYPE, + { + per_page: -1, + } + ), + [] + ); +}; + +/** + * Return a unique template part title based on + * the given title and existing template parts. + * + * @param {string} title The original template part title. + * @param {Object} templateParts The array of template part entities. + * @return {string} A unique template part title. + */ +export const getUniqueTemplatePartTitle = ( title, templateParts ) => { + const lowercaseTitle = title.toLowerCase(); + const existingTitles = templateParts.map( ( templatePart ) => + templatePart.title.rendered.toLowerCase() + ); + + if ( ! existingTitles.includes( lowercaseTitle ) ) { + return title; + } + + let suffix = 2; + while ( existingTitles.includes( `${ lowercaseTitle } ${ suffix }` ) ) { + suffix++; + } + + return `${ title } ${ suffix }`; +}; + +/** + * Get a valid slug for a template part. + * Currently template parts only allow latin chars. + * The fallback slug will receive suffix by default. + * + * @param {string} title The template part title. + * @return {string} A valid template part slug. + */ +export const getCleanTemplatePartSlug = ( title ) => { + return kebabCase( title ).replace( /[^\w-]+/g, '' ) || 'wp-custom-part'; +}; diff --git a/packages/editor/src/components/post-actions/actions.js b/packages/editor/src/components/post-actions/actions.js index eafa41741cbe2e..e12195e63c0df3 100644 --- a/packages/editor/src/components/post-actions/actions.js +++ b/packages/editor/src/components/post-actions/actions.js @@ -32,9 +32,11 @@ import { store as editorStore } from '../../store'; import { unlock } from '../../lock-unlock'; import isTemplateRevertable from '../../store/utils/is-template-revertable'; import { exportPatternAsJSONAction } from './export-pattern-action'; +import { CreateTemplatePartModalContents } from '../create-template-part-modal'; // Patterns. -const { PATTERN_TYPES } = unlock( patternsPrivateApis ); +const { PATTERN_TYPES, CreatePatternModalContents, useDuplicatePatternProps } = + unlock( patternsPrivateApis ); /** * Check if a template is removable. @@ -968,6 +970,65 @@ const resetTemplateAction = { }, }; +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 isThemePattern = item.type === PATTERN_TYPES.theme; + const duplicatedProps = useDuplicatePatternProps( { + pattern: + isThemePattern || ! item.patternPost ? item : item.patternPost, + onSuccess: () => closeModal(), + } ); + return ( + + ); + }, +}; + +export const duplicateTemplatePartAction = { + id: 'duplicate-template-part', + label: _x( 'Duplicate', 'action label' ), + isEligible: ( item ) => item.type === TEMPLATE_PART_POST_TYPE, + modalHeader: _x( 'Duplicate template part', 'action label' ), + RenderModal: ( { items, closeModal } ) => { + const [ item ] = items; + const { createSuccessNotice } = useDispatch( noticesStore ); + function onTemplatePartSuccess() { + 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' } + ); + closeModal(); + } + return ( + + ); + }, +}; + export function usePostActions( postType, onActionPerformed ) { const { postTypeObject } = useSelect( ( select ) => { @@ -1001,6 +1062,8 @@ export function usePostActions( postType, onActionPerformed ) { ! isPattern && duplicatePostAction : false, + isTemplateOrTemplatePart && duplicateTemplatePartAction, + isPattern && duplicatePatternAction, renamePostAction, isPattern && exportPatternAsJSONAction, isTemplateOrTemplatePart ? resetTemplateAction : restorePostAction, diff --git a/packages/editor/src/private-apis.js b/packages/editor/src/private-apis.js index 128965bc9e4205..02cf58e1f4c83b 100644 --- a/packages/editor/src/private-apis.js +++ b/packages/editor/src/private-apis.js @@ -12,6 +12,7 @@ import { EntitiesSavedStatesExtensible } from './components/entities-saved-state import useAutoSwitchEditorSidebars from './components/provider/use-auto-switch-editor-sidebars'; import useBlockEditorSettings from './components/provider/use-block-editor-settings'; import Header from './components/header'; +import CreateTemplatePartModal from './components/create-template-part-modal'; import InserterSidebar from './components/inserter-sidebar'; import ListViewSidebar from './components/list-view-sidebar'; import PatternOverridesPanel from './components/pattern-overrides-panel'; @@ -41,6 +42,7 @@ const { store: interfaceStore, ...remainingInterfaceApis } = interfaceApis; export const privateApis = {}; lock( privateApis, { + CreateTemplatePartModal, ExperimentalEditorProvider, EntitiesSavedStatesExtensible, GlobalStylesProvider, diff --git a/packages/editor/src/store/constants.js b/packages/editor/src/store/constants.js index c29493266025ed..78708f00cc9eb7 100644 --- a/packages/editor/src/store/constants.js +++ b/packages/editor/src/store/constants.js @@ -18,6 +18,7 @@ export const TRASH_POST_NOTICE_ID = 'TRASH_POST_NOTICE_ID'; export const PERMALINK_POSTNAME_REGEX = /%(?:postname|pagename)%/; export const ONE_MINUTE_IN_MS = 60 * 1000; export const AUTOSAVE_PROPERTIES = [ 'title', 'excerpt', 'content' ]; +export const TEMPLATE_PART_AREA_DEFAULT_CATEGORY = 'uncategorized'; export const TEMPLATE_POST_TYPE = 'wp_template'; export const TEMPLATE_PART_POST_TYPE = 'wp_template_part'; export const PATTERN_POST_TYPE = 'wp_block'; diff --git a/packages/editor/src/style.scss b/packages/editor/src/style.scss index 21e1d20997fcce..6a014deab1bed0 100644 --- a/packages/editor/src/style.scss +++ b/packages/editor/src/style.scss @@ -3,6 +3,7 @@ @import "./components/autocompleters/style.scss"; @import "./components/block-manager/style.scss"; @import "./components/collapsible-block-toolbar/style.scss"; +@import "./components/create-template-part-modal/style.scss"; @import "./components/block-settings-menu/style.scss"; @import "./components/blog-title/style.scss"; @import "./components/document-bar/style.scss";