From 81d86ad230fcc8021b9e266bd99c4f2a1db73d8a Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Thu, 4 Apr 2024 22:27:59 +0100 Subject: [PATCH 1/4] Reuse and unify post and page actions, accross the different use cases. --- packages/base-styles/_z-index.scss | 1 + .../sidebar/settings-sidebar/index.js | 20 +- .../src/components/page-actions/index.js | 40 ---- .../page-actions/trash-page-menu-item.js | 61 ------ .../sidebar-edit-mode/page-panels/index.js | 69 ++++--- .../sidebar-navigation-screen-page/index.js | 28 ++- .../sidebar-navigation-screen-page/style.scss | 4 + .../src/components/post-actions/index.js | 193 ++++++++++++++++++ .../src/components/post-actions/style.scss | 3 + packages/editor/src/private-apis.js | 2 + packages/editor/src/style.scss | 1 + 11 files changed, 280 insertions(+), 142 deletions(-) delete mode 100644 packages/edit-site/src/components/page-actions/index.js delete mode 100644 packages/edit-site/src/components/page-actions/trash-page-menu-item.js create mode 100644 packages/editor/src/components/post-actions/index.js create mode 100644 packages/editor/src/components/post-actions/style.scss diff --git a/packages/base-styles/_z-index.scss b/packages/base-styles/_z-index.scss index 945755f512ae7d..a3d8b39be84d25 100644 --- a/packages/base-styles/_z-index.scss +++ b/packages/base-styles/_z-index.scss @@ -130,6 +130,7 @@ $z-layers: ( ".block-editor-block-rename-modal": 1000001, ".edit-site-list__rename-modal": 1000001, ".dataviews-action-modal": 1000001, + ".editor-action-modal": 1000001, ".editor-post-template__swap-template-modal": 1000001, ".edit-site-template-panel__replace-template-modal": 1000001, diff --git a/packages/edit-post/src/components/sidebar/settings-sidebar/index.js b/packages/edit-post/src/components/sidebar/settings-sidebar/index.js index dd1a6a048d965e..6ea62974ebbbe0 100644 --- a/packages/edit-post/src/components/sidebar/settings-sidebar/index.js +++ b/packages/edit-post/src/components/sidebar/settings-sidebar/index.js @@ -27,6 +27,7 @@ import { PostTaxonomiesPanel, privateApis as editorPrivateApis, } from '@wordpress/editor'; +import { addQueryArgs } from '@wordpress/url'; /** * Internal dependencies @@ -39,7 +40,7 @@ import { store as editPostStore } from '../../../store'; import { privateApis as componentsPrivateApis } from '@wordpress/components'; import { unlock } from '../../../lock-unlock'; -const { PostCardPanel } = unlock( editorPrivateApis ); +const { PostCardPanel, PostActions } = unlock( editorPrivateApis ); const { Tabs } = unlock( componentsPrivateApis ); const { PatternOverridesPanel } = unlock( editorPrivateApis ); @@ -53,6 +54,15 @@ export const sidebars = { block: 'edit-post/block', }; +function onActionPerformed( actionId, items ) { + if ( actionId === 'move-to-trash' ) { + const postType = items[ 0 ].type; + document.location.href = addQueryArgs( 'edit.php', { + post_type: postType, + } ); + } +} + const SidebarContent = ( { sidebarName, keyboardShortcut, @@ -113,7 +123,13 @@ const SidebarContent = ( { > - + + } + /> { ! isEditingTemplate && ( <> diff --git a/packages/edit-site/src/components/page-actions/index.js b/packages/edit-site/src/components/page-actions/index.js deleted file mode 100644 index 546b90a7dd1501..00000000000000 --- a/packages/edit-site/src/components/page-actions/index.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * WordPress dependencies - */ -import { __ } from '@wordpress/i18n'; -import { DropdownMenu, MenuGroup } from '@wordpress/components'; -import { moreVertical } from '@wordpress/icons'; - -/** - * Internal dependencies - */ -import TrashPageMenuItem from './trash-page-menu-item'; -import RenamePostMenuItem from '../rename-post-menu-item'; - -export default function PageActions( { - className, - onRemove, - page, - toggleProps, -} ) { - return ( - - { () => ( - - - { !! onRemove && ( - - ) } - - ) } - - ); -} diff --git a/packages/edit-site/src/components/page-actions/trash-page-menu-item.js b/packages/edit-site/src/components/page-actions/trash-page-menu-item.js deleted file mode 100644 index 4015378c48b83b..00000000000000 --- a/packages/edit-site/src/components/page-actions/trash-page-menu-item.js +++ /dev/null @@ -1,61 +0,0 @@ -/** - * WordPress dependencies - */ -import { useDispatch } from '@wordpress/data'; -import { decodeEntities } from '@wordpress/html-entities'; -import { store as coreStore } from '@wordpress/core-data'; -import { __, sprintf } from '@wordpress/i18n'; -import { MenuItem } from '@wordpress/components'; -import { store as noticesStore } from '@wordpress/notices'; - -export default function TrashPageMenuItem( { page, onRemove } ) { - const { createSuccessNotice, createErrorNotice } = - useDispatch( noticesStore ); - const { deleteEntityRecord } = useDispatch( coreStore ); - - if ( page?.status === 'trash' ) { - return; - } - - const title = decodeEntities( - typeof page.title === 'string' ? page.title : page.title.rendered - ); - - async function removePage() { - try { - await deleteEntityRecord( - 'postType', - 'page', - page.id, - {}, - { throwOnError: true } - ); - createSuccessNotice( - sprintf( - /* translators: The page's title. */ - __( '"%s" moved to the Trash.' ), - title - ), - { - type: 'snackbar', - id: 'edit-site-page-trashed', - } - ); - onRemove?.(); - } catch ( error ) { - const errorMessage = - error.message && error.code !== 'unknown_error' - ? error.message - : __( - 'An error occurred while moving the page to the trash.' - ); - - createErrorNotice( errorMessage, { type: 'snackbar' } ); - } - } - return ( - removePage() } isDestructive> - { __( 'Move to Trash' ) } - - ); -} diff --git a/packages/edit-site/src/components/sidebar-edit-mode/page-panels/index.js b/packages/edit-site/src/components/sidebar-edit-mode/page-panels/index.js index 0a004cfc7a7923..f42a1bd590f2c3 100644 --- a/packages/edit-site/src/components/sidebar-edit-mode/page-panels/index.js +++ b/packages/edit-site/src/components/sidebar-edit-mode/page-panels/index.js @@ -16,6 +16,8 @@ import { privateApis as editorPrivateApis, } from '@wordpress/editor'; import { privateApis as routerPrivateApis } from '@wordpress/router'; +import { useCallback } from '@wordpress/element'; +import { privateApis as routerPrivateApis } from '@wordpress/router'; /** * Internal dependencies @@ -27,29 +29,47 @@ import PageSummary from './page-summary'; import { unlock } from '../../../lock-unlock'; -const { PostCardPanel } = unlock( editorPrivateApis ); +const { PostCardPanel, PostActions } = unlock( editorPrivateApis ); const { useHistory } = unlock( routerPrivateApis ); export default function PagePanels() { - const { hasResolved, page, renderingMode } = useSelect( ( select ) => { - const { getEditedPostContext } = select( editSiteStore ); - const { getEditedEntityRecord, hasFinishedResolution } = - select( coreStore ); - const { getRenderingMode } = select( editorStore ); - const context = getEditedPostContext(); - const queryArgs = [ 'postType', context.postType, context.postId ]; - return { - hasResolved: hasFinishedResolution( - 'getEditedEntityRecord', - queryArgs - ), - page: getEditedEntityRecord( ...queryArgs ), - renderingMode: getRenderingMode(), - }; - }, [] ); - const history = useHistory(); + const { id, type, hasResolved, status, date, password, renderingMode } = + useSelect( ( select ) => { + const { getEditedPostContext } = select( editSiteStore ); + const { getEditedEntityRecord, hasFinishedResolution } = + select( coreStore ); + const { getRenderingMode } = select( editorStore ); + const context = getEditedPostContext(); + const queryArgs = [ 'postType', context.postType, context.postId ]; + const page = getEditedEntityRecord( ...queryArgs ); + return { + hasResolved: hasFinishedResolution( + 'getEditedEntityRecord', + queryArgs + ), + id: page?.id, + type: page?.type, + status: page?.status, + date: page?.date, + password: page?.password, + renderingMode: getRenderingMode(), + }; + }, [] ); - const { id, type, status, date, password } = page; + const history = useHistory(); + const onActionPerformed = useCallback( + ( actionId, items ) => { + if ( actionId === 'move-to-trash' ) { + history.push( { + path: '/' + items[ 0 ].type, + postId: undefined, + postType: undefined, + canvas: 'view', + } ); + } + }, + [ history ] + ); if ( ! hasResolved ) { return null; @@ -59,16 +79,7 @@ export default function PagePanels() { <> { - history.push( { - path: '/page', - } ); - } } - /> + } /> diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-page/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-page/index.js index 10ceccd994966c..3ac1e00127c0fb 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-page/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-page/index.js @@ -15,8 +15,9 @@ import { pencil } from '@wordpress/icons'; import { __unstableStripHTML as stripHTML } from '@wordpress/dom'; import { escapeAttribute } from '@wordpress/escape-html'; import { safeDecodeURIComponent, filterURLForDisplay } from '@wordpress/url'; -import { useEffect } from '@wordpress/element'; +import { useEffect, useCallback } from '@wordpress/element'; import { privateApis as routerPrivateApis } from '@wordpress/router'; +import { privateApis as editorPrivateApis } from '@wordpress/editor'; /** * Internal dependencies @@ -26,17 +27,16 @@ import { unlock } from '../../lock-unlock'; import { store as editSiteStore } from '../../store'; import SidebarButton from '../sidebar-button'; import PageDetails from './page-details'; -import PageActions from '../page-actions'; import SidebarNavigationScreenDetailsFooter from '../sidebar-navigation-screen-details-footer'; const { useHistory } = unlock( routerPrivateApis ); +const { PostActions } = unlock( editorPrivateApis ); export default function SidebarNavigationScreenPage( { backPath } ) { const { setCanvasMode } = unlock( useDispatch( editSiteStore ) ); const history = useHistory(); const { params: { postId }, - goTo, } = useNavigator(); const { record, hasResolved } = useEntityRecord( 'postType', @@ -82,6 +82,20 @@ export default function SidebarNavigationScreenPage( { backPath } ) { } }, [ hasResolved, history ] ); + const onActionPerformed = useCallback( + ( actionId, items ) => { + if ( actionId === 'move-to-trash' ) { + history.push( { + path: '/' + items[ 0 ].type, + postId: undefined, + postType: undefined, + canvas: 'view', + } ); + } + }, + [ history ] + ); + const featureImageAltText = featuredMediaAltText ? decodeEntities( featuredMediaAltText ) : decodeEntities( record?.title?.rendered || __( 'Featured image' ) ); @@ -94,13 +108,7 @@ export default function SidebarNavigationScreenPage( { backPath } ) { ) } actions={ <> - { - goTo( '/page' ); - } } - /> + setCanvasMode( 'edit' ) } label={ __( 'Edit' ) } diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-page/style.scss b/packages/edit-site/src/components/sidebar-navigation-screen-page/style.scss index 66efe31726e8b7..7faa1f7f9b47de 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-page/style.scss +++ b/packages/edit-site/src/components/sidebar-navigation-screen-page/style.scss @@ -61,3 +61,7 @@ fill: $alert-green; } } + +.edit-site-sidebar-navigation-screen__actions .editor-all-actions-button { + color: inherit; +} \ No newline at end of file diff --git a/packages/editor/src/components/post-actions/index.js b/packages/editor/src/components/post-actions/index.js new file mode 100644 index 00000000000000..b06baad157ab04 --- /dev/null +++ b/packages/editor/src/components/post-actions/index.js @@ -0,0 +1,193 @@ +/** + * WordPress dependencies + */ +import { useSelect } from '@wordpress/data'; +import { store as coreStore } from '@wordpress/core-data'; +import { useMemo, useState, Fragment, Children } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { + privateApis as componentsPrivateApis, + Button, + Modal, +} from '@wordpress/components'; +import { moreVertical } from '@wordpress/icons'; + +/** + * Internal dependencies + */ +import { unlock } from '../../lock-unlock'; +import { usePostActions } from './actions'; +import { store as editorStore } from '../../store'; + +const { + DropdownMenuV2: DropdownMenu, + DropdownMenuGroupV2: DropdownMenuGroup, + DropdownMenuItemV2: DropdownMenuItem, + DropdownMenuItemLabelV2: DropdownMenuItemLabel, + DropdownMenuSeparatorV2: DropdownMenuSeparator, + kebabCase, +} = unlock( componentsPrivateApis ); + +const POST_ACTIONS_WHILE_EDITING = [ + 'view-post', + 'view-post-revisions', + 'rename-post', + 'move-to-trash', +]; + +export default function PostActions( { onActionPerformed } ) { + const { postType, postId } = useSelect( ( select ) => { + const { getCurrentPostType, getCurrentPostId } = select( editorStore ); + return { + postType: getCurrentPostType(), + postId: getCurrentPostId(), + }; + } ); + const actions = usePostActions( + onActionPerformed, + POST_ACTIONS_WHILE_EDITING + ); + const item = useSelect( + ( select ) => { + const { getEditedEntityRecord } = select( coreStore ); + return getEditedEntityRecord( 'postType', postType, postId ); + }, + [ postType, postId ] + ); + + const { primaryActions, secondaryActions } = useMemo( () => { + return actions.reduce( + ( accumulator, action ) => { + if ( action.isEligible && ! action.isEligible( item ) ) { + return accumulator; + } + if ( action.isPrimary ) { + accumulator.primaryActions.push( action ); + } else { + accumulator.secondaryActions.push( action ); + } + return accumulator; + }, + { primaryActions: [], secondaryActions: [] } + ); + }, [ actions, item ] ); + return ( + + } + placement="bottom-end" + > + + { !! primaryActions.length && ( + + ) } + { !! secondaryActions.length && ( + + ) } + + + ); +} + +// From now on all the functions on this file are copied as from the dataviews packages, +// The editor packages should not be using the dataviews packages directly, +// and the dataviews package should not be using the editor packages directly, +// so duplicating the code here seems like the least bad option. + +// Copied as is from packages/dataviews/src/item-actions.js +function DropdownMenuItemTrigger( { action, onClick } ) { + return ( + + { action.label } + + ); +} + +// Copied as is from packages/dataviews/src/item-actions.js +function ActionWithModal( { action, item, ActionTrigger } ) { + const [ isModalOpen, setIsModalOpen ] = useState( false ); + const actionTriggerProps = { + action, + onClick: () => setIsModalOpen( true ), + }; + const { RenderModal, hideModalHeader } = action; + return ( + <> + + { isModalOpen && ( + { + setIsModalOpen( false ); + } } + overlayClassName={ `editor-action-modal editor-action-modal__${ kebabCase( + action.id + ) }` } + > + setIsModalOpen( false ) } + /> + + ) } + + ); +} + +// Copied as is from packages/dataviews/src/utils.js +function WithDropDownMenuSeparators( { children } ) { + return Children.toArray( children ) + .filter( Boolean ) + .map( ( child, i ) => ( + + { i > 0 && } + { child } + + ) ); +} + +// Copied as is from packages/dataviews/src/item-actions.js +function ActionsDropdownMenuGroup( { actions, item } ) { + return ( + + { actions.map( ( action ) => { + if ( action.RenderModal ) { + return ( + + ); + } + return ( + action.callback( [ item ] ) } + /> + ); + } ) } + + ); +} diff --git a/packages/editor/src/components/post-actions/style.scss b/packages/editor/src/components/post-actions/style.scss new file mode 100644 index 00000000000000..7d0ade90caeed6 --- /dev/null +++ b/packages/editor/src/components/post-actions/style.scss @@ -0,0 +1,3 @@ +.editor-action-modal { + z-index: z-index(".editor-action-modal"); +} \ No newline at end of file diff --git a/packages/editor/src/private-apis.js b/packages/editor/src/private-apis.js index ea42d6ad5fde5b..4bdcbb1042b228 100644 --- a/packages/editor/src/private-apis.js +++ b/packages/editor/src/private-apis.js @@ -16,6 +16,7 @@ import PostPanelRow from './components/post-panel-row'; import PostViewLink from './components/post-view-link'; import PreviewDropdown from './components/preview-dropdown'; import PreferencesModal from './components/preferences-modal'; +import PostActions from './components/post-actions'; import { usePostActions } from './components/post-actions/actions'; import PostCardPanel from './components/post-card-panel'; @@ -30,6 +31,7 @@ lock( privateApis, { ModeSwitcher, PatternOverridesPanel, PluginPostExcerpt, + PostActions, PostPanelRow, PostViewLink, PreviewDropdown, diff --git a/packages/editor/src/style.scss b/packages/editor/src/style.scss index fd8f5821392c68..865635f228891e 100644 --- a/packages/editor/src/style.scss +++ b/packages/editor/src/style.scss @@ -9,6 +9,7 @@ @import "./components/inserter-sidebar/style.scss"; @import "./components/list-view-sidebar/style.scss"; @import "./components/post-author/style.scss"; +@import "./components/post-actions/style.scss"; @import "./components/post-card-panel/style.scss"; @import "./components/post-excerpt/style.scss"; @import "./components/post-featured-image/style.scss"; From 3704169bfa8b288f14c24129b81218abbe804171 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Fri, 5 Apr 2024 22:14:49 +0100 Subject: [PATCH 2/4] post rebase fixe --- .../src/components/sidebar-edit-mode/page-panels/index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/edit-site/src/components/sidebar-edit-mode/page-panels/index.js b/packages/edit-site/src/components/sidebar-edit-mode/page-panels/index.js index f42a1bd590f2c3..021050d2c18f5a 100644 --- a/packages/edit-site/src/components/sidebar-edit-mode/page-panels/index.js +++ b/packages/edit-site/src/components/sidebar-edit-mode/page-panels/index.js @@ -17,13 +17,11 @@ import { } from '@wordpress/editor'; import { privateApis as routerPrivateApis } from '@wordpress/router'; import { useCallback } from '@wordpress/element'; -import { privateApis as routerPrivateApis } from '@wordpress/router'; /** * Internal dependencies */ import { store as editSiteStore } from '../../../store'; -import PageActions from '../../page-actions'; import PageContent from './page-content'; import PageSummary from './page-summary'; From e0969437896060472a4a6c50d7cd1c7df755c720 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Fri, 5 Apr 2024 22:17:48 +0100 Subject: [PATCH 3/4] lint fixes --- .../src/components/sidebar-navigation-screen-page/style.scss | 2 +- packages/editor/src/components/post-actions/style.scss | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-page/style.scss b/packages/edit-site/src/components/sidebar-navigation-screen-page/style.scss index 7faa1f7f9b47de..3e2ff86fa17ccc 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-page/style.scss +++ b/packages/edit-site/src/components/sidebar-navigation-screen-page/style.scss @@ -64,4 +64,4 @@ .edit-site-sidebar-navigation-screen__actions .editor-all-actions-button { color: inherit; -} \ No newline at end of file +} diff --git a/packages/editor/src/components/post-actions/style.scss b/packages/editor/src/components/post-actions/style.scss index 7d0ade90caeed6..2e33975685a8bb 100644 --- a/packages/editor/src/components/post-actions/style.scss +++ b/packages/editor/src/components/post-actions/style.scss @@ -1,3 +1,3 @@ .editor-action-modal { z-index: z-index(".editor-action-modal"); -} \ No newline at end of file +} From 9d4cb8c74e4dda0abb1ee867f55e71d370dd06ae Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Mon, 8 Apr 2024 11:26:31 +0100 Subject: [PATCH 4/4] Update packages/editor/src/components/post-actions/index.js Co-authored-by: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> --- packages/editor/src/components/post-actions/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/editor/src/components/post-actions/index.js b/packages/editor/src/components/post-actions/index.js index b06baad157ab04..953975c3aafeca 100644 --- a/packages/editor/src/components/post-actions/index.js +++ b/packages/editor/src/components/post-actions/index.js @@ -153,7 +153,7 @@ function ActionWithModal( { action, item, ActionTrigger } ) { ); } -// Copied as is from packages/dataviews/src/utils.js +// Copied as is from packages/dataviews/src/view-table.js function WithDropDownMenuSeparators( { children } ) { return Children.toArray( children ) .filter( Boolean )