diff --git a/packages/edit-site/src/components/block-editor/block-editor-provider/default-block-editor-provider.js b/packages/edit-site/src/components/block-editor/block-editor-provider/default-block-editor-provider.js deleted file mode 100644 index c91c687ce5284..0000000000000 --- a/packages/edit-site/src/components/block-editor/block-editor-provider/default-block-editor-provider.js +++ /dev/null @@ -1,75 +0,0 @@ -/** - * WordPress dependencies - */ -import { useEntityBlockEditor } from '@wordpress/core-data'; -import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; -import { useSelect } from '@wordpress/data'; - -/** - * Internal dependencies - */ -import { store as editSiteStore } from '../../../store'; -import { unlock } from '../../../lock-unlock'; -import useSiteEditorSettings from '../use-site-editor-settings'; -import usePageContentBlocks from './use-page-content-blocks'; - -const { ExperimentalBlockEditorProvider } = unlock( blockEditorPrivateApis ); - -const noop = () => {}; - -/** - * The default block editor provider for the site editor. Typically used when - * the post type is `'wp_template_part'` or `'wp_template'` and allows editing - * of the template and its nested entities. - * - * If the page content focus type is `'hideTemplate'`, the provider will provide - * a set of page content blocks wrapped in a container that, together, - * mimic the look and feel of the post editor and - * allow editing of the page content only. - * - * @param {Object} props - * @param {Element} props.children - */ -export default function DefaultBlockEditorProvider( { children } ) { - const settings = useSiteEditorSettings(); - - const { templateType, isTemplateHidden } = useSelect( ( select ) => { - const { getEditedPostType } = select( editSiteStore ); - const { getPageContentFocusType, getCanvasMode } = unlock( - select( editSiteStore ) - ); - return { - templateType: getEditedPostType(), - isTemplateHidden: - getCanvasMode() === 'edit' && - getPageContentFocusType() === 'hideTemplate', - canvasMode: unlock( select( editSiteStore ) ).getCanvasMode(), - }; - }, [] ); - - const [ blocks, onInput, onChange ] = useEntityBlockEditor( - 'postType', - templateType - ); - const pageContentBlocks = usePageContentBlocks( { - blocks, - isPageContentFocused: isTemplateHidden, - wrapPageContent: true, - } ); - - return ( - - { children } - - ); -} diff --git a/packages/edit-site/src/components/block-editor/block-editor-provider/index.js b/packages/edit-site/src/components/block-editor/block-editor-provider/index.js deleted file mode 100644 index ab07a3fefc5d3..0000000000000 --- a/packages/edit-site/src/components/block-editor/block-editor-provider/index.js +++ /dev/null @@ -1,29 +0,0 @@ -/** - * WordPress dependencies - */ -import { useSelect } from '@wordpress/data'; - -/** - * Internal dependencies - */ -import { store as editSiteStore } from '../../../store'; -import DefaultBlockEditorProvider from './default-block-editor-provider'; -import NavigationBlockEditorProvider from './navigation-block-editor-provider'; -import { NAVIGATION_POST_TYPE } from '../../../utils/constants'; - -export default function BlockEditorProvider( { children } ) { - const entityType = useSelect( - ( select ) => select( editSiteStore ).getEditedPostType(), - [] - ); - if ( entityType === NAVIGATION_POST_TYPE ) { - return ( - - { children } - - ); - } - return ( - { children } - ); -} diff --git a/packages/edit-site/src/components/block-editor/block-editor-provider/navigation-block-editor-provider.js b/packages/edit-site/src/components/block-editor/block-editor-provider/navigation-block-editor-provider.js deleted file mode 100644 index 9927cbb040c7b..0000000000000 --- a/packages/edit-site/src/components/block-editor/block-editor-provider/navigation-block-editor-provider.js +++ /dev/null @@ -1,114 +0,0 @@ -/** - * WordPress dependencies - */ -import { useSelect, useDispatch } from '@wordpress/data'; -import { useMemo, useEffect } from '@wordpress/element'; -import { useEntityId } from '@wordpress/core-data'; -import { - store as blockEditorStore, - privateApis as blockEditorPrivateApis, -} from '@wordpress/block-editor'; -import { createBlock } from '@wordpress/blocks'; - -/** - * Internal dependencies - */ -import { unlock } from '../../../lock-unlock'; -import useSiteEditorSettings from '../use-site-editor-settings'; -import { store as editSiteStore } from '../../../store'; -import { NAVIGATION_POST_TYPE } from '../../../utils/constants'; - -const { ExperimentalBlockEditorProvider } = unlock( blockEditorPrivateApis ); - -const noop = () => {}; - -/** - * Block editor component for editing navigation menus. - * - * Note: Navigation entities require a wrapping Navigation block to provide - * them with some basic layout and styling. Therefore we create a "ghost" block - * and provide it will a reference to the navigation entity ID being edited. - * - * In this scenario it is the **block** that handles syncing the entity content - * whereas for other entities this is handled by entity block editor. - * - * @param {number} navigationMenuId the navigation menu ID - * @return {[WPBlock[], Function, Function]} The block array and setters. - */ -export default function NavigationBlockEditorProvider( { children } ) { - const defaultSettings = useSiteEditorSettings(); - - const navigationMenuId = useEntityId( 'postType', NAVIGATION_POST_TYPE ); - - const blocks = useMemo( () => { - return [ - createBlock( 'core/navigation', { - ref: navigationMenuId, - // As the parent editor is locked with `templateLock`, the template locking - // must be explicitly "unset" on the block itself to allow the user to modify - // the block's content. - templateLock: false, - } ), - ]; - }, [ navigationMenuId ] ); - - const { isEditMode } = useSelect( ( select ) => { - const { getCanvasMode } = unlock( select( editSiteStore ) ); - - return { - isEditMode: getCanvasMode() === 'edit', - }; - }, [] ); - - const { selectBlock, setBlockEditingMode, unsetBlockEditingMode } = - useDispatch( blockEditorStore ); - - const navigationBlockClientId = blocks && blocks[ 0 ]?.clientId; - - const settings = useMemo( () => { - return { - ...defaultSettings, - // Lock the editor to allow the root ("ghost") Navigation block only. - templateLock: 'insert', - template: [ [ 'core/navigation', {}, [] ] ], - }; - }, [ defaultSettings ] ); - - // Auto-select the Navigation block when entering Navigation focus mode. - useEffect( () => { - if ( navigationBlockClientId && isEditMode ) { - selectBlock( navigationBlockClientId ); - } - }, [ navigationBlockClientId, isEditMode, selectBlock ] ); - - // Set block editing mode to contentOnly when entering Navigation focus mode. - // This ensures that non-content controls on the block will be hidden and thus - // the user can focus on editing the Navigation Menu content only. - useEffect( () => { - if ( ! navigationBlockClientId ) { - return; - } - - setBlockEditingMode( navigationBlockClientId, 'contentOnly' ); - - return () => { - unsetBlockEditingMode( navigationBlockClientId ); - }; - }, [ - navigationBlockClientId, - unsetBlockEditingMode, - setBlockEditingMode, - ] ); - - return ( - - { children } - - ); -} diff --git a/packages/edit-site/src/components/block-editor/index.js b/packages/edit-site/src/components/block-editor/index.js deleted file mode 100644 index 2c635ff860a5b..0000000000000 --- a/packages/edit-site/src/components/block-editor/index.js +++ /dev/null @@ -1,28 +0,0 @@ -/** - * WordPress dependencies - */ -import { BlockInspector } from '@wordpress/block-editor'; -import { privateApis as editPatternsPrivateApis } from '@wordpress/patterns'; - -/** - * Internal dependencies - */ -import TemplatePartConverter from '../template-part-converter'; -import { SidebarInspectorFill } from '../sidebar-edit-mode'; -import SiteEditorCanvas from './site-editor-canvas'; -import BlockEditorProvider from './block-editor-provider'; - -import { unlock } from '../../lock-unlock'; -const { PatternsMenuItems } = unlock( editPatternsPrivateApis ); -export default function BlockEditor() { - return ( - - - - - - - - - ); -} diff --git a/packages/edit-site/src/components/block-editor/block-editor-provider/test/use-page-content-blocks.js b/packages/edit-site/src/components/block-editor/test/use-page-content-blocks.js similarity index 100% rename from packages/edit-site/src/components/block-editor/block-editor-provider/test/use-page-content-blocks.js rename to packages/edit-site/src/components/block-editor/test/use-page-content-blocks.js diff --git a/packages/edit-site/src/components/block-editor/block-editor-provider/use-page-content-blocks.js b/packages/edit-site/src/components/block-editor/use-page-content-blocks.js similarity index 97% rename from packages/edit-site/src/components/block-editor/block-editor-provider/use-page-content-blocks.js rename to packages/edit-site/src/components/block-editor/use-page-content-blocks.js index dd05f90a56bbb..9201b7816286e 100644 --- a/packages/edit-site/src/components/block-editor/block-editor-provider/use-page-content-blocks.js +++ b/packages/edit-site/src/components/block-editor/use-page-content-blocks.js @@ -7,7 +7,7 @@ import { createBlock } from '@wordpress/blocks'; /** * Internal dependencies */ -import { PAGE_CONTENT_BLOCK_TYPES } from '../../../utils/constants'; +import { PAGE_CONTENT_BLOCK_TYPES } from '../../utils/constants'; /** * Helper method to iterate through all blocks, recursing into allowed inner blocks. diff --git a/packages/edit-site/src/components/block-editor/use-site-editor-settings.js b/packages/edit-site/src/components/block-editor/use-site-editor-settings.js index f597abee0726d..1587b8ba4a2d4 100644 --- a/packages/edit-site/src/components/block-editor/use-site-editor-settings.js +++ b/packages/edit-site/src/components/block-editor/use-site-editor-settings.js @@ -87,7 +87,7 @@ function useArchiveLabel( templateSlug ) { ); } -export default function useSiteEditorSettings() { +export function useSpecificEditorSettings() { const { setIsInserterOpened } = useDispatch( editSiteStore ); const { templateSlug, @@ -97,8 +97,6 @@ export default function useSiteEditorSettings() { keepCaretInsideBlock, canvasMode, settings, - postType, - postId, } = useSelect( ( select ) => { const { getEditedPostType, @@ -164,5 +162,21 @@ export default function useSiteEditorSettings() { archiveLabels.archiveNameLabel, ] ); + return defaultEditorSettings; +} + +export default function useSiteEditorSettings() { + const defaultEditorSettings = useSpecificEditorSettings(); + const { postType, postId } = useSelect( ( select ) => { + const { getEditedPostType, getEditedPostId } = unlock( + select( editSiteStore ) + ); + const usedPostType = getEditedPostType(); + const usedPostId = getEditedPostId(); + return { + postType: usedPostType, + postId: usedPostId, + }; + }, [] ); return useBlockEditorSettings( defaultEditorSettings, postType, postId ); } diff --git a/packages/edit-site/src/components/editor/index.js b/packages/edit-site/src/components/editor/index.js index 70d033d14188c..a47ef3c33817c 100644 --- a/packages/edit-site/src/components/editor/index.js +++ b/packages/edit-site/src/components/editor/index.js @@ -6,31 +6,36 @@ import classnames from 'classnames'; /** * WordPress dependencies */ -import { useMemo } from '@wordpress/element'; import { useSelect } from '@wordpress/data'; import { Notice } from '@wordpress/components'; import { useInstanceId } from '@wordpress/compose'; -import { EntityProvider } from '@wordpress/core-data'; import { store as preferencesStore } from '@wordpress/preferences'; import { - BlockContextProvider, BlockBreadcrumb, store as blockEditorStore, privateApis as blockEditorPrivateApis, + BlockInspector, } from '@wordpress/block-editor'; import { InterfaceSkeleton, ComplementaryArea, store as interfaceStore, } from '@wordpress/interface'; -import { EditorNotices, EditorSnackbars } from '@wordpress/editor'; +import { + EditorNotices, + EditorSnackbars, + privateApis as editorPrivateApis, +} from '@wordpress/editor'; import { __, sprintf } from '@wordpress/i18n'; +import { store as coreDataStore } from '@wordpress/core-data'; /** * Internal dependencies */ -import { SidebarComplementaryAreaFills } from '../sidebar-edit-mode'; -import BlockEditor from '../block-editor'; +import { + SidebarComplementaryAreaFills, + SidebarInspectorFill, +} from '../sidebar-edit-mode'; import CodeEditor from '../code-editor'; import KeyboardShortcutsEditMode from '../keyboard-shortcuts/edit-mode'; import InserterSidebar from '../secondary-sidebar/inserter-sidebar'; @@ -46,8 +51,13 @@ 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'; +import SiteEditorCanvas from '../block-editor/site-editor-canvas'; +import TemplatePartConverter from '../template-part-converter'; +import { useSpecificEditorSettings } from '../block-editor/use-site-editor-settings'; const { BlockRemovalWarningModal } = unlock( blockEditorPrivateApis ); +const { ExperimentalEditorProvider: EditorProvider } = + unlock( editorPrivateApis ); const interfaceLabels = { /* translators: accessibility text for the editor content landmark region. */ @@ -79,10 +89,11 @@ export default function Editor( { listViewToggleElement, isLoading } ) { isLoaded: hasLoadedPost, } = useEditedEntityRecord(); - const { id: editedPostId, type: editedPostType } = editedPost; + const { type: editedPostType } = editedPost; const { context, + contextPost, editorMode, canvasMode, blockEditorMode, @@ -103,11 +114,20 @@ export default function Editor( { listViewToggleElement, isLoading } ) { } = unlock( select( editSiteStore ) ); const { __unstableGetEditorMode } = select( blockEditorStore ); const { getActiveComplementaryArea } = select( interfaceStore ); + const { getEntityRecord } = select( coreDataStore ); + const _context = getEditedPostContext(); // The currently selected entity to display. // Typically template or template part in the site editor. return { - context: getEditedPostContext(), + context: _context, + contextPost: _context?.postId + ? getEntityRecord( + 'postType', + _context.postType, + _context.postId + ) + : undefined, editorMode: getEditorMode(), canvasMode: getCanvasMode(), blockEditorMode: __unstableGetEditorMode(), @@ -141,18 +161,7 @@ export default function Editor( { listViewToggleElement, isLoading } ) { const secondarySidebarLabel = isListViewOpen ? __( 'List View' ) : __( 'Block Library' ); - const blockContext = useMemo( () => { - const { postType, postId, ...nonPostFields } = context ?? {}; - - return { - ...( hasPageContentFocus ? context : nonPostFields ), - // Ideally this context should be removed. However, it is currently used by the Query Loop block. - templateSlug: - editedPostType === TEMPLATE_POST_TYPE - ? editedPost.slug - : undefined, - }; - }, [ editedPost.slug, editedPostType, hasPageContentFocus, context ] ); + const hasTemplate = hasPageContentFocus && context?.postId; let title; if ( hasLoadedPost ) { @@ -180,104 +189,102 @@ export default function Editor( { listViewToggleElement, isLoading } ) { 'aria-describedby': loadingProgressId, } : undefined; + const settings = useSpecificEditorSettings(); return ( <> { isLoading ? : null } { isEditMode && } - - - - - { isEditMode && } - } - content={ + + + { isEditMode && } + } + content={ + <> + + { isEditMode && } + { showVisualEditor && editedPost && ( <> - - { isEditMode && } - { showVisualEditor && editedPost && ( - <> - - - - - ) } - { editorMode === 'text' && - editedPost && - isEditMode && } - { hasLoadedPost && ! editedPost && ( - - { __( - "You attempted to edit an item that doesn't exist. Perhaps it was deleted?" - ) } - - ) } - { isEditMode && ( - - ) } - - } - contentProps={ contentProps } - secondarySidebar={ - isEditMode && - ( ( shouldShowInserter && ( - - ) ) || - ( shouldShowListView && ( - - ) ) ) - } - sidebar={ - isEditMode && - isRightSidebarOpen && ( - <> - - - - ) - } - footer={ - shouldShowBlockBreadcrumbs && ( - + + + + + - ) - } - labels={ { - ...interfaceLabels, - secondarySidebar: secondarySidebarLabel, - } } - /> - - - + + + ) } + { editorMode === 'text' && + editedPost && + isEditMode && } + { hasLoadedPost && ! editedPost && ( + + { __( + "You attempted to edit an item that doesn't exist. Perhaps it was deleted?" + ) } + + ) } + { isEditMode && } + + } + contentProps={ contentProps } + secondarySidebar={ + isEditMode && + ( ( shouldShowInserter && ) || + ( shouldShowListView && ( + + ) ) ) + } + sidebar={ + isEditMode && + isRightSidebarOpen && ( + <> + + + + ) + } + footer={ + shouldShowBlockBreadcrumbs && ( + + ) + } + labels={ { + ...interfaceLabels, + secondarySidebar: secondarySidebarLabel, + } } + /> + ); } diff --git a/packages/edit-site/src/components/sidebar-edit-mode/page-panels/edit-template.js b/packages/edit-site/src/components/sidebar-edit-mode/page-panels/edit-template.js index 8067453a51821..e9b18c4c72496 100644 --- a/packages/edit-site/src/components/sidebar-edit-mode/page-panels/edit-template.js +++ b/packages/edit-site/src/components/sidebar-edit-mode/page-panels/edit-template.js @@ -21,7 +21,7 @@ import { store as editSiteStore } from '../../../store'; import SwapTemplateButton from './swap-template-button'; import ResetDefaultTemplate from './reset-default-template'; import { unlock } from '../../../lock-unlock'; -import usePageContentBlocks from '../../block-editor/block-editor-provider/use-page-content-blocks'; +import usePageContentBlocks from '../../block-editor/use-page-content-blocks'; const POPOVER_PROPS = { className: 'edit-site-page-panels-edit-template__dropdown', diff --git a/packages/editor/src/components/provider/index.js b/packages/editor/src/components/provider/index.js index 6037c6496cc0c..9334ff99de998 100644 --- a/packages/editor/src/components/provider/index.js +++ b/packages/editor/src/components/provider/index.js @@ -1,7 +1,12 @@ /** * WordPress dependencies */ -import { useEffect, useLayoutEffect, useMemo } from '@wordpress/element'; +import { + useEffect, + useLayoutEffect, + useMemo, + useState, +} from '@wordpress/element'; import { useDispatch, useSelect } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; import { EntityProvider, useEntityBlockEditor } from '@wordpress/core-data'; @@ -9,9 +14,11 @@ import { BlockEditorProvider, BlockContextProvider, privateApis as blockEditorPrivateApis, + store as blockEditorStore, } from '@wordpress/block-editor'; import { store as noticesStore } from '@wordpress/notices'; import { privateApis as editPatternsPrivateApis } from '@wordpress/patterns'; +import { createBlock } from '@wordpress/blocks'; /** * Internal dependencies @@ -24,9 +31,86 @@ import { unlock } from '../../lock-unlock'; const { ExperimentalBlockEditorProvider } = unlock( blockEditorPrivateApis ); const { PatternsMenuItems } = unlock( editPatternsPrivateApis ); +const noop = () => {}; + +/** + * For the Navigation block editor, we need to force the block editor to contentOnly for that block. + * + * Set block editing mode to contentOnly when entering Navigation focus mode. + * this ensures that non-content controls on the block will be hidden and thus + * the user can focus on editing the Navigation Menu content only. + * + * @param {string} navigationBlockClientId ClientId. + */ +function useForceFocusModeForNavigation( navigationBlockClientId ) { + const { setBlockEditingMode, unsetBlockEditingMode } = + useDispatch( blockEditorStore ); + + useEffect( () => { + if ( ! navigationBlockClientId ) { + return; + } + + setBlockEditingMode( navigationBlockClientId, 'contentOnly' ); + + return () => { + unsetBlockEditingMode( navigationBlockClientId ); + }; + }, [ + navigationBlockClientId, + unsetBlockEditingMode, + setBlockEditingMode, + ] ); +} + +/** + * Depending on the post, template and template mode, + * returns the appropriate blocks and change handlers for the block editor provider. + * + * @param {Array} post Block list. + * @param {boolean} template Whether the page content has focus (and the surrounding template is inert). If `true` return page content blocks. Default `false`. + * @param {boolean} templateMode Whether to wrap the page content blocks in a group block to mimic the post editor. Default `false`. + * @return {Array} Block editor props. + */ +function useBlockEditorProps( post, template, templateMode ) { + const rootLevelPost = + !! template && templateMode !== 'hidden' ? template : post; + const { type, id } = rootLevelPost; + const [ blocks, onInput, onChange ] = useEntityBlockEditor( + 'postType', + type, + { id } + ); + const actualBlocks = useMemo( () => { + if ( type === 'wp_navigation' ) { + return [ + createBlock( 'core/navigation', { + ref: id, + // As the parent editor is locked with `templateLock`, the template locking + // must be explicitly "unset" on the block itself to allow the user to modify + // the block's content. + templateLock: false, + } ), + ]; + } + }, [ type, id ] ); + const allowRootLevelChanges = + !! template && templateMode !== 'disabled' && type === 'wp_navigation'; + const navigationBlockClientId = + type === 'wp_navigation' && actualBlocks && actualBlocks[ 0 ]?.clientId; + useForceFocusModeForNavigation( navigationBlockClientId ); + + return [ + actualBlocks ?? blocks, + allowRootLevelChanges ? onInput : noop, + allowRootLevelChanges ? onChange : noop, + ]; +} + export const ExperimentalEditorProvider = withRegistryProvider( ( { __unstableTemplate, + templateMode = 'all', post, settings, recovery, @@ -34,12 +118,24 @@ export const ExperimentalEditorProvider = withRegistryProvider( children, BlockEditorProviderComponent = ExperimentalBlockEditorProvider, } ) => { + const rootLevelPost = + !! __unstableTemplate && templateMode !== 'hidden' + ? __unstableTemplate + : post; const defaultBlockContext = useMemo( () => { - if ( post.type === 'wp_template' ) { - return {}; - } - return { postId: post.id, postType: post.type }; - }, [ post.id, post.type ] ); + const postContext = + post.type !== 'wp_template' + ? { postId: post.id, postType: post.type } + : {}; + + return { + ...postContext, + templateSlug: + rootLevelPost.type === 'wp_template' + ? rootLevelPost.slug + : undefined, + }; + }, [ post.id, post.type, rootLevelPost.type, rootLevelPost?.slug ] ); const { editorSettings, selection, isReady } = useSelect( ( select ) => { const { @@ -55,17 +151,18 @@ export const ExperimentalEditorProvider = withRegistryProvider( }, [] ); - const { id, type } = __unstableTemplate ?? post; - const [ blocks, onInput, onChange ] = useEntityBlockEditor( - 'postType', - type, - { id } - ); + const { id, type } = rootLevelPost; const blockEditorSettings = useBlockEditorSettings( editorSettings, type, id ); + const [ blocks, onInput, onChange ] = useBlockEditorProps( + post, + __unstableTemplate, + templateMode + ); + const { updatePostLock, setupEditor, @@ -109,7 +206,7 @@ export const ExperimentalEditorProvider = withRegistryProvider( // Synchronize the editor settings as they change. useEffect( () => { updateEditorSettings( settings ); - }, [ settings ] ); + }, [ settings, updateEditorSettings ] ); if ( ! isReady ) { return null; diff --git a/packages/editor/src/components/provider/use-block-editor-settings.js b/packages/editor/src/components/provider/use-block-editor-settings.js index c0804febab0db..e2cbba7e6a757 100644 --- a/packages/editor/src/components/provider/use-block-editor-settings.js +++ b/packages/editor/src/components/provider/use-block-editor-settings.js @@ -67,8 +67,6 @@ const BLOCK_EDITOR_SETTINGS = [ 'postsPerPage', 'readOnly', 'styles', - 'template', - 'templateLock', 'titlePlaceholder', 'supportsLayout', 'widgetTypesToHideFromLegacyWidgetBlock', @@ -237,6 +235,12 @@ function useBlockEditorSettings( settings, postType, postId ) { pageOnFront, pageForPosts, __experimentalPreferPatternsOnRoot: postType === 'wp_template', + templateLock: + postType === 'wp_navigation' ? 'insert' : settings.templateLock, + template: + postType === 'wp_navigation' + ? [ [ 'core/navigation', {}, [] ] ] + : settings.template, } ), [ settings,