diff --git a/packages/block-editor/src/components/block-edit/context.js b/packages/block-editor/src/components/block-edit/context.js index e1a1c1646eb53a..38394410686536 100644 --- a/packages/block-editor/src/components/block-edit/context.js +++ b/packages/block-editor/src/components/block-edit/context.js @@ -5,6 +5,9 @@ import { createContext, useContext } from '@wordpress/element'; export const mayDisplayControlsKey = Symbol( 'mayDisplayControls' ); export const mayDisplayParentControlsKey = Symbol( 'mayDisplayParentControls' ); +export const mayDisplayPatternEditingControlsKey = Symbol( + 'mayDisplayPatternEditingControls' +); export const blockEditingModeKey = Symbol( 'blockEditingMode' ); export const blockBindingsKey = Symbol( 'blockBindings' ); export const isPreviewModeKey = Symbol( 'isPreviewMode' ); diff --git a/packages/block-editor/src/components/block-edit/index.js b/packages/block-editor/src/components/block-edit/index.js index 0c29c0e98b1bfd..44ccb77fe96d3f 100644 --- a/packages/block-editor/src/components/block-edit/index.js +++ b/packages/block-editor/src/components/block-edit/index.js @@ -13,6 +13,7 @@ import { useBlockEditContext, mayDisplayControlsKey, mayDisplayParentControlsKey, + mayDisplayPatternEditingControlsKey, blockEditingModeKey, blockBindingsKey, isPreviewModeKey, @@ -33,6 +34,7 @@ export { useBlockEditContext }; export default function BlockEdit( { mayDisplayControls, mayDisplayParentControls, + mayDisplayPatternEditingControls, blockEditingMode, isPreviewMode, // The remaining props are passed through the BlockEdit filters and are thus @@ -69,6 +71,9 @@ export default function BlockEdit( { // usage outside of the package (this context is exposed). [ mayDisplayControlsKey ]: mayDisplayControls, [ mayDisplayParentControlsKey ]: mayDisplayParentControls, + [ mayDisplayPatternEditingControlsKey ]: + mayDisplayPatternEditingControls && + blockEditingMode !== 'disabled', [ blockEditingModeKey ]: blockEditingMode, [ blockBindingsKey ]: bindings, [ isPreviewModeKey ]: isPreviewMode, @@ -82,6 +87,7 @@ export default function BlockEdit( { __unstableLayoutClassNames, mayDisplayControls, mayDisplayParentControls, + mayDisplayPatternEditingControls, blockEditingMode, bindings, isPreviewMode, diff --git a/packages/block-editor/src/components/block-inspector/index.js b/packages/block-editor/src/components/block-inspector/index.js index 47829930ff4f20..f029ee4f994cac 100644 --- a/packages/block-editor/src/components/block-inspector/index.js +++ b/packages/block-editor/src/components/block-inspector/index.js @@ -36,14 +36,12 @@ function StyleInspectorSlots( { blockName, showAdvancedControls = true, showPositionControls = true, - showListControls = false, showBindingsControls = true, } ) { const borderPanelLabel = useBorderPanelLabel( { blockName } ); return ( <> - { showListControls && } + { ! isSectionBlock && ( - + ) } { isSectionBlock && isBlockSynced && diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js index 9dca3693a24c50..d782a89f9297e7 100644 --- a/packages/block-editor/src/components/block-list/block.js +++ b/packages/block-editor/src/components/block-list/block.js @@ -106,6 +106,7 @@ function BlockListBlock( { const { mayDisplayControls, mayDisplayParentControls, + isSelectionWithinCurrentSection, themeSupportsLayout, ...context } = useContext( PrivateBlockContext ); @@ -135,6 +136,7 @@ function BlockListBlock( { } mayDisplayControls={ mayDisplayControls } mayDisplayParentControls={ mayDisplayParentControls } + mayDisplayPatternEditingControls={ isSelectionWithinCurrentSection } blockEditingMode={ context.blockEditingMode } isPreviewMode={ context.isPreviewMode } /> @@ -231,6 +233,7 @@ function BlockListBlock( { value={ { wrapperProps: updatedWrapperProps, isAligned, + isSelectionWithinCurrentSection, ...context, } } > diff --git a/packages/block-editor/src/components/block-list/block.native.js b/packages/block-editor/src/components/block-list/block.native.js index 9f34195e50e040..463e4b531608b5 100644 --- a/packages/block-editor/src/components/block-list/block.native.js +++ b/packages/block-editor/src/components/block-list/block.native.js @@ -195,6 +195,7 @@ function BlockListBlock( { isParentSelected, order, mayDisplayControls, + mayDisplayPatternEditingControls, blockEditingMode, } = useSelect( ( select ) => { @@ -263,6 +264,7 @@ function BlockListBlock( { getMultiSelectedBlockClientIds().every( ( id ) => getBlockName( id ) === name ) ), + mayDisplayPatternEditingControls: false, // Section/pattern editing not yet supported on native blockEditingMode: getBlockEditingMode( clientId ), }; }, @@ -403,6 +405,9 @@ function BlockListBlock( { } wrapperProps={ wrapperProps } mayDisplayControls={ mayDisplayControls } + mayDisplayPatternEditingControls={ + mayDisplayPatternEditingControls + } blockEditingMode={ blockEditingMode } /> diff --git a/packages/block-editor/src/components/inspector-controls-tabs/use-inspector-controls-tabs.js b/packages/block-editor/src/components/inspector-controls-tabs/use-inspector-controls-tabs.js index c86496510cc68b..6f12636becc30a 100644 --- a/packages/block-editor/src/components/inspector-controls-tabs/use-inspector-controls-tabs.js +++ b/packages/block-editor/src/components/inspector-controls-tabs/use-inspector-controls-tabs.js @@ -84,26 +84,29 @@ export default function useInspectorControlsTabs( ...( hasListFills && hasStyleFills > 1 ? advancedFills : [] ), ]; + // When the block fields experiment is active, only rely on `hasContentFills` + // to determine whether the content tab to be shown. The tab purely uses slot + // fills in this situation. + const shouldShowBlockFields = + window?.__experimentalContentOnlyInspectorFields; const hasContentTab = hasContentFills || - !! ( contentClientIds && contentClientIds.length > 0 ); + ( ! shouldShowBlockFields && contentClientIds?.length ); - const hasListTab = hasListFills && ! isSectionBlock; + if ( hasContentTab ) { + tabs.push( TAB_CONTENT ); + } // Add the tabs in the order that they will default to if available. // List View > Content > Settings > Styles. - if ( hasListTab ) { + if ( hasListFills ) { tabs.push( TAB_LIST_VIEW ); } - if ( hasContentTab ) { - tabs.push( TAB_CONTENT ); - } - if ( ( settingsFills.length || // Advanded fills who up in settings tab if available or they blend into the default tab, if there's only one tab. - ( advancedFills.length && ( hasContentTab || hasListTab ) ) ) && + ( advancedFills.length && ( hasContentTab || hasListFills ) ) ) && ! isSectionBlock ) { tabs.push( TAB_SETTINGS ); diff --git a/packages/block-editor/src/components/inspector-controls/fill.js b/packages/block-editor/src/components/inspector-controls/fill.js index 1663348e58fe7b..0c613fe387b5f9 100644 --- a/packages/block-editor/src/components/inspector-controls/fill.js +++ b/packages/block-editor/src/components/inspector-controls/fill.js @@ -15,15 +15,15 @@ import { useEffect, useContext } from '@wordpress/element'; import { useBlockEditContext, mayDisplayControlsKey, + mayDisplayPatternEditingControlsKey, } from '../block-edit/context'; import groups from './groups'; -export function PrivateInspectorControlsFill( { +export default function InspectorControlsFill( { children, group = 'default', __experimentalGroup, resetAllFilter, - forceDisplayControls, } ) { if ( __experimentalGroup ) { deprecated( @@ -43,7 +43,14 @@ export function PrivateInspectorControlsFill( { warning( `Unknown InspectorControls group "${ group }" provided.` ); return null; } - if ( ! forceDisplayControls && ! context[ mayDisplayControlsKey ] ) { + const shouldDisplayForPatternEditing = + context[ mayDisplayPatternEditingControlsKey ] && + ( group === 'list' || group === 'content' ); + + if ( + ! context[ mayDisplayControlsKey ] && + ! shouldDisplayForPatternEditing + ) { return null; } @@ -64,23 +71,6 @@ export function PrivateInspectorControlsFill( { ); } -export default function InspectorControlsFill( { - children, - group = 'default', - __experimentalGroup, - resetAllFilter, -} ) { - return ( - - { children } - - ); -} - function RegisterResetAll( { resetAllFilter, children } ) { const { registerResetAllFilter, deregisterResetAllFilter } = useContext( ToolsPanelContext ); diff --git a/packages/block-editor/src/hooks/block-fields/index.js b/packages/block-editor/src/hooks/block-fields/index.js index 82d7b7ddf57069..72fac94ee1ac27 100644 --- a/packages/block-editor/src/hooks/block-fields/index.js +++ b/packages/block-editor/src/hooks/block-fields/index.js @@ -1,13 +1,15 @@ /** * WordPress dependencies */ -import { addFilter } from '@wordpress/hooks'; -import { privateApis as blocksPrivateApis } from '@wordpress/blocks'; +import { + privateApis as blocksPrivateApis, + getBlockType, +} from '@wordpress/blocks'; import { __experimentalHStack as HStack, __experimentalTruncate as Truncate, } from '@wordpress/components'; -import { createHigherOrderComponent } from '@wordpress/compose'; +import { useSelect } from '@wordpress/data'; import { DataForm } from '@wordpress/dataviews'; import { useContext, useState, useMemo } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; @@ -15,6 +17,7 @@ import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ +import { store as blockEditorStore } from '../../store'; import { unlock } from '../../lock-unlock'; import BlockIcon from '../../components/block-icon'; import useBlockDisplayTitle from '../../components/block-title/use-block-display-title'; @@ -22,7 +25,7 @@ import useBlockDisplayInformation from '../../components/use-block-display-infor const { fieldsKey, formKey } = unlock( blocksPrivateApis ); import FieldsDropdownMenu from './fields-dropdown-menu'; import { PrivateBlockContext } from '../../components/block-list/private-block-context'; -import { PrivateInspectorControlsFill } from '../../components/inspector-controls/fill'; +import InspectorControls from '../../components/inspector-controls/fill'; // controls import RichText from './rich-text'; @@ -49,7 +52,6 @@ function createConfiguredControl( ControlComponent, config = {} ) { * @param {Object} props * @param {string} props.clientId The clientId of the block. * @param {Object} props.blockType The blockType definition. - * @param {Object} props.attributes The block's attribute values. * @param {Function} props.setAttributes Action to set the block's attributes. * @param {boolean} props.isCollapsed Whether the DataForm is rendered as 'collapsed' with only the first field * displayed by default. When collapsed a dropdown is displayed to allow @@ -59,7 +61,6 @@ function createConfiguredControl( ControlComponent, config = {} ) { function BlockFields( { clientId, blockType, - attributes, setAttributes, isCollapsed = false, } ) { @@ -71,6 +72,11 @@ function BlockFields( { const blockTypeFields = blockType?.[ fieldsKey ]; + const attributes = useSelect( + ( select ) => select( blockEditorStore ).getBlockAttributes( clientId ), + [ clientId ] + ); + const computedForm = useMemo( () => { if ( ! isCollapsed ) { return blockType?.[ formKey ]; @@ -187,56 +193,34 @@ function BlockFields( { ); } -const withBlockFields = createHigherOrderComponent( - ( BlockEdit ) => ( props ) => { - const { - blockType, - isSelectionWithinCurrentSection, - isSectionBlock, - blockEditingMode, - isSelected, - } = useContext( PrivateBlockContext ); - - const shouldShowBlockFields = - window?.__experimentalContentOnlyInspectorFields; - const blockTypeFields = blockType?.[ fieldsKey ]; - - if ( ! shouldShowBlockFields || ! blockTypeFields?.length ) { - return ; - } +function hasBlockFieldsSupport( blockName ) { + return !! ( + window?.__experimentalContentOnlyInspectorFields && + getBlockType( blockName )?.[ fieldsKey ] + ); +} - return ( - <> - - { - // Display the controls of all inner blocks for section/pattern editing. - isSelectionWithinCurrentSection && - ( isSectionBlock || - blockEditingMode === 'contentOnly' ) && ( - - - - ) - } - { ! isSelectionWithinCurrentSection && isSelected && ( - - - - ) } - - ); - } -); +export function BlockFieldsPanel( props ) { + const { blockType, isSelectionWithinCurrentSection } = + useContext( PrivateBlockContext ); -addFilter( - 'editor.BlockEdit', - 'core/content-only-controls/block-fields', - withBlockFields -); + return ( + + + + ); +} + +/** + * Export block support definition. + */ +export default { + edit: BlockFieldsPanel, + hasSupport: hasBlockFieldsSupport, + attributeKeys: [], + supportsPatternEditing: true, +}; diff --git a/packages/block-editor/src/hooks/index.js b/packages/block-editor/src/hooks/index.js index 21373ae3060768..be980ebd4c4fba 100644 --- a/packages/block-editor/src/hooks/index.js +++ b/packages/block-editor/src/hooks/index.js @@ -14,7 +14,7 @@ import './lock'; import allowedBlocks from './allowed-blocks'; import anchor from './anchor'; import ariaLabel from './aria-label'; -import './block-fields'; +import blockFields from './block-fields'; import customClassName from './custom-class-name'; import './generated-class-name'; import style from './style'; @@ -56,6 +56,7 @@ createBlockEditFilter( blockBindingsPanel, childLayout, allowedBlocks, + blockFields, listView, autoInspectorControls, ].filter( Boolean ) diff --git a/packages/block-editor/src/hooks/list-view.js b/packages/block-editor/src/hooks/list-view.js index 56111b926446d5..cb27e39a86cbd8 100644 --- a/packages/block-editor/src/hooks/list-view.js +++ b/packages/block-editor/src/hooks/list-view.js @@ -5,13 +5,15 @@ import { __ } from '@wordpress/i18n'; import { PanelBody } from '@wordpress/components'; import { useSelect } from '@wordpress/data'; import { store as blocksStore, hasBlockSupport } from '@wordpress/blocks'; +import { useContext } from '@wordpress/element'; /** * Internal dependencies */ -import InspectorControls from '../components/inspector-controls'; import { store as blockEditorStore } from '../store'; import { PrivateListView } from '../components/list-view'; +import InspectorControls from '../components/inspector-controls/fill'; +import { PrivateBlockContext } from '../components/block-list/private-block-context'; export const LIST_VIEW_SUPPORT_KEY = 'listView'; @@ -34,23 +36,50 @@ export function hasListViewSupport( nameOrType ) { * @return {Element|null} List view inspector controls or null. */ export function ListViewPanel( { clientId, name } ) { + const { isSelectionWithinCurrentSection } = + useContext( PrivateBlockContext ); const isEnabled = hasListViewSupport( name ); - const { hasChildren, blockTitle } = useSelect( - ( select ) => ( { - hasChildren: - !! select( blockEditorStore ).getBlockCount( clientId ), - blockTitle: select( blocksStore ).getBlockType( name )?.title, - } ), - [ clientId, name ] + const { hasChildren, blockTitle, isNestedListView } = useSelect( + ( select ) => { + const { getBlockCount, getBlockParents, getBlockName } = + select( blockEditorStore ); + + // When the ListView is shown in a section, avoid showing List Views + // for both parent and child blocks that have support. In this situation + // the parent will show the child anyway in its List. + // Search parents to see if there's one that also has support, and if so + // skip rendering. + // This matches closely the logic in the `BlockCard` component. + let _isNestedListView = false; + if ( isSelectionWithinCurrentSection ) { + const parents = getBlockParents( clientId, true ); + _isNestedListView = parents.find( ( parentId ) => { + const parentName = getBlockName( parentId ); + return ( + parentName === 'core/navigation' || + hasBlockSupport( parentName, 'listView' ) + ); + } ); + } + + return { + hasChildren: !! getBlockCount( clientId ), + blockTitle: select( blocksStore ).getBlockType( name )?.title, + isNestedListView: _isNestedListView, + }; + }, + [ clientId, name, isSelectionWithinCurrentSection ] ); - if ( ! isEnabled ) { + if ( ! isEnabled || isNestedListView ) { return null; } + const showBlockTitle = isSelectionWithinCurrentSection; + return ( - + { ! hasChildren && (

{ __( 'No items yet.' ) } @@ -74,4 +103,5 @@ export default { edit: ListViewPanel, hasSupport: hasListViewSupport, attributeKeys: [], + supportsPatternEditing: true, }; diff --git a/packages/block-editor/src/hooks/utils.js b/packages/block-editor/src/hooks/utils.js index 464642b5bf7554..794b65f0523869 100644 --- a/packages/block-editor/src/hooks/utils.js +++ b/packages/block-editor/src/hooks/utils.js @@ -19,6 +19,7 @@ import { useBlockEditContext, mayDisplayControlsKey, mayDisplayParentControlsKey, + mayDisplayPatternEditingControlsKey, } from '../components/block-edit/context'; import { useSettings } from '../components'; import { useSettingsForBlockElement } from '../components/global-styles/hooks'; @@ -534,8 +535,11 @@ export function createBlockEditFilter( features ) { hasSupport, attributeKeys = [], shareWithChildBlocks, + supportsPatternEditing, } = feature; const shouldDisplayControls = + ( supportsPatternEditing && + context[ mayDisplayPatternEditingControlsKey ] ) || context[ mayDisplayControlsKey ] || ( context[ mayDisplayParentControlsKey ] && shareWithChildBlocks );