Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Try surfacing content inspector controls for contentOnly inner blocks #60412

Draft
wants to merge 1 commit into
base: trunk
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 40 additions & 12 deletions packages/block-editor/src/components/block-card/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
Button,
__experimentalText as Text,
__experimentalVStack as VStack,
__experimentalUseSlotFills as useSlotFills,
} from '@wordpress/components';
import { chevronLeft, chevronRight } from '@wordpress/icons';
import { __, _x, isRTL, sprintf } from '@wordpress/i18n';
Expand All @@ -21,6 +22,8 @@ import { useSelect, useDispatch } from '@wordpress/data';
*/
import BlockIcon from '../block-icon';
import { store as blockEditorStore } from '../../store';
import useBlockDisplayInformation from '../use-block-display-information';
import { unlock } from '../../lock-unlock';

function BlockCard( { title, icon, description, blockType, className, name } ) {
if ( blockType ) {
Expand All @@ -31,29 +34,54 @@ function BlockCard( { title, icon, description, blockType, className, name } ) {
( { title, icon, description } = blockType );
}

const { parentNavBlockClientId } = useSelect( ( select ) => {
const { getSelectedBlockClientId, getBlockParentsByBlockName } =
select( blockEditorStore );
const { isParentNavigationBlock, parentClientId } = useSelect(
( select ) => {
const {
getSelectedBlockClientId,
getBlockParentsByBlockName,
getContentLockingParent,
} = unlock( select( blockEditorStore ) );

const _selectedBlockClientId = getSelectedBlockClientId();
const _selectedBlockClientId = getSelectedBlockClientId();

return {
parentNavBlockClientId: getBlockParentsByBlockName(
let _parentClientId = getBlockParentsByBlockName(
_selectedBlockClientId,
'core/navigation',
true
)[ 0 ],
};
}, [] );
)[ 0 ];
const _isParentNavigationBlock = !! _parentClientId;

if ( ! _parentClientId ) {
_parentClientId = getContentLockingParent(
_selectedBlockClientId
);
}

return {
isParentNavigationBlock: _isParentNavigationBlock,
parentClientId: _parentClientId,
};
},
[]
);

const parentDisplayInfo = useBlockDisplayInformation( parentClientId );
const contentOnlyFills = useSlotFills( 'InspectorControlsContentOnly' );
const { selectBlock } = useDispatch( blockEditorStore );
const hasParentBackArrow =
isParentNavigationBlock ||
( parentClientId && !! contentOnlyFills?.length );

return (
<div className={ clsx( 'block-editor-block-card', className ) }>
{ parentNavBlockClientId && ( // This is only used by the Navigation block for now. It's not ideal having Navigation block specific code here.
{ hasParentBackArrow && ( // This is only used by the Navigation block for now. It's not ideal having Navigation block specific code here.
<Button
onClick={ () => selectBlock( parentNavBlockClientId ) }
label={ __( 'Go to parent Navigation block' ) }
onClick={ () => selectBlock( parentClientId ) }
label={ sprintf(
// translators: %s: the block title of the parent.
__( 'Go to parent: %s' ),
parentDisplayInfo?.title
) }
style={
// TODO: This style override is also used in ToolsPanelHeader.
// It should be supported out-of-the-box by Button.
Expand Down
118 changes: 80 additions & 38 deletions packages/block-editor/src/components/block-inspector/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,22 @@ import {
getUnregisteredTypeHandlerName,
store as blocksStore,
} from '@wordpress/blocks';
import { PanelBody, __unstableMotion as motion } from '@wordpress/components';
import {
PanelBody,
__unstableMotion as motion,
__experimentalUseSlotFills as useSlotFills,
} from '@wordpress/components';
import { useSelect } from '@wordpress/data';

/**
* Internal dependencies
*/
import { unlock } from '../../lock-unlock';
import SkipToSelectedBlock from '../skip-to-selected-block';
import BlockCard from '../block-card';
import MultiSelectionInspector from '../multi-selection-inspector';
import MultiSelectionInspector, {
MultiSelectionControls,
} from '../multi-selection-inspector';
import BlockVariationTransforms from '../block-variation-transforms';
import useBlockDisplayInformation from '../use-block-display-information';
import { store as blockEditorStore } from '../../store';
Expand All @@ -30,7 +37,41 @@ import BlockInfo from '../block-info-slot-fill';
import BlockQuickNavigation from '../block-quick-navigation';
import { useBorderPanelLabel } from '../../hooks/border';

import { unlock } from '../../lock-unlock';
function BlockInspectorContentLockedUI( {
clientId,
contentLockingParent,
isContentLockedChild,
isContentLockedParent,
} ) {
const contentOnlyFills = useSlotFills( 'InspectorControlsContentOnly' );
const isChildWithoutContentControls =
isContentLockedChild && ! contentOnlyFills?.length;

if ( isChildWithoutContentControls || isContentLockedParent ) {
const parentClientId = isContentLockedParent
? clientId
: contentLockingParent;

return <BlockInspectorContentLockedChild clientId={ parentClientId } />;
}

return <BlockInspectorContentLockedChild clientId={ clientId } />;
}

function BlockInspectorContentLockedChild( { clientId } ) {
const blockInformation = useBlockDisplayInformation( clientId );
return (
<div className="block-editor-block-inspector">
<BlockCard
{ ...blockInformation }
className={ blockInformation.isSynced && 'is-synced' }
/>
<BlockVariationTransforms blockClientId={ clientId } />
<BlockInfo.Slot />
<InspectorControls.Slot group="contentOnly" />
</div>
);
}

function BlockStylesPanel( { clientId } ) {
return (
Expand All @@ -47,20 +88,28 @@ const BlockInspector = ( { showNoBlockSelectedMessage = true } ) => {
selectedBlockClientId,
blockType,
isSectionBlock,
isContentLockedParent,
isContentLockedChild,
contentLockingParent,
} = useSelect( ( select ) => {
const {
getSelectedBlockClientId,
getSelectedBlockCount,
getBlockName,
getParentSectionBlock,
isSectionBlock: _isSectionBlock,
getContentLockingParent,
getTemplateLock,
} = unlock( select( blockEditorStore ) );
const _selectedBlockClientId = getSelectedBlockClientId();
const renderedBlockClientId =
getParentSectionBlock( _selectedBlockClientId ) ||
getSelectedBlockClientId();
const _selectedBlockName =
renderedBlockClientId && getBlockName( renderedBlockClientId );
const _contentLockingParent = getContentLockingParent(
_selectedBlockClientId
);
const _blockType =
_selectedBlockName && getBlockType( _selectedBlockName );

Expand All @@ -70,12 +119,14 @@ const BlockInspector = ( { showNoBlockSelectedMessage = true } ) => {
selectedBlockName: _selectedBlockName,
blockType: _blockType,
isSectionBlock: _isSectionBlock( renderedBlockClientId ),
isContentLockedParent:
getTemplateLock( _selectedBlockClientId ) === 'contentOnly' ||
_selectedBlockName === 'core/block',
isContentLockedChild: !! _contentLockingParent,
contentLockingParent: _contentLockingParent,
};
}, [] );

const availableTabs = useInspectorControlsTabs( blockType?.name );
const showTabs = availableTabs?.length > 1;

// The block inspector animation settings will be completely
// removed in the future to create an API which allows the block
// inspector to transition between what it
Expand All @@ -85,42 +136,15 @@ const BlockInspector = ( { showNoBlockSelectedMessage = true } ) => {
const blockInspectorAnimationSettings =
useBlockInspectorAnimationSettings( blockType );

const borderPanelLabel = useBorderPanelLabel( {
blockName: selectedBlockName,
} );

if ( count > 1 && ! isSectionBlock ) {
return (
<div className="block-editor-block-inspector">
<MultiSelectionInspector />
{ showTabs ? (
<InspectorControlsTabs tabs={ availableTabs } />
) : (
<>
<InspectorControls.Slot />
<InspectorControls.Slot
group="color"
label={ __( 'Color' ) }
className="color-block-support-panel__inner-wrapper"
/>
<InspectorControls.Slot
group="background"
label={ __( 'Background image' ) }
/>
<InspectorControls.Slot
group="typography"
label={ __( 'Typography' ) }
/>
<InspectorControls.Slot
group="dimensions"
label={ __( 'Dimensions' ) }
/>
<InspectorControls.Slot
group="border"
label={ borderPanelLabel }
/>
<InspectorControls.Slot group="styles" />
</>
{ ! isContentLockedChild && (
<MultiSelectionControls
blockType={ blockType }
blockName={ selectedBlockName }
/>
) }
</div>
);
Expand Down Expand Up @@ -148,6 +172,15 @@ const BlockInspector = ( { showNoBlockSelectedMessage = true } ) => {
return null;
}

if ( isContentLockedChild || isContentLockedParent ) {
<BlockInspectorContentLockedUI
clientId={ selectedBlockClientId }
contentLockingParent={ contentLockingParent }
isContentLockedParent={ isContentLockedParent }
isContentLockedChild={ isContentLockedChild }
/>;
}

return (
<BlockInspectorSingleBlockWrapper
animate={ blockInspectorAnimationSettings }
Expand Down Expand Up @@ -246,6 +279,12 @@ const BlockInspectorSingleBlock = ( {
[ isSectionBlock, clientId ]
);

const contentClientIdsWithControls = useSelect(
( select ) =>
unlock( select( blockEditorStore ) ).getContentOnlyControlsBlocks(),
[]
);

return (
<div className="block-editor-block-inspector">
<BlockCard
Expand All @@ -272,6 +311,9 @@ const BlockInspectorSingleBlock = ( {
<PanelBody title={ __( 'Content' ) }>
<BlockQuickNavigation
clientIds={ contentClientIds }
clientIdsWithControls={
contentClientIdsWithControls
}
/>
</PanelBody>
) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import {
Flex,
FlexBlock,
FlexItem,
Icon,
} from '@wordpress/components';
import { chevronRight } from '@wordpress/icons';

/**
* Internal dependencies
Expand All @@ -19,7 +21,11 @@ import BlockIcon from '../block-icon';
import useBlockDisplayInformation from '../use-block-display-information';
import useBlockDisplayTitle from '../block-title/use-block-display-title';

export default function BlockQuickNavigation( { clientIds, onSelect } ) {
export default function BlockQuickNavigation( {
clientIds,
clientIdsWithControls,
onSelect,
} ) {
if ( ! clientIds.length ) {
return null;
}
Expand All @@ -30,13 +36,14 @@ export default function BlockQuickNavigation( { clientIds, onSelect } ) {
onSelect={ onSelect }
key={ clientId }
clientId={ clientId }
hasControls={ clientIdsWithControls?.includes( clientId ) }
/>
) ) }
</VStack>
);
}

function BlockQuickNavigationItem( { clientId, onSelect } ) {
function BlockQuickNavigationItem( { clientId, hasControls, onSelect } ) {
const blockInformation = useBlockDisplayInformation( clientId );
const blockTitle = useBlockDisplayTitle( {
clientId,
Expand Down Expand Up @@ -75,6 +82,11 @@ function BlockQuickNavigationItem( { clientId, onSelect } ) {
<FlexBlock style={ { textAlign: 'left' } }>
<Truncate>{ blockTitle }</Truncate>
</FlexBlock>
{ hasControls && (
<FlexItem>
<Icon icon={ chevronRight } />
</FlexItem>
) }
</Flex>
</Button>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ import {
__experimentalStyleProvider as StyleProvider,
__experimentalToolsPanelContext as ToolsPanelContext,
} from '@wordpress/components';
import { useDispatch } from '@wordpress/data';
import warning from '@wordpress/warning';
import deprecated from '@wordpress/deprecated';
import { useEffect, useContext } from '@wordpress/element';

/**
* Internal dependencies
*/
import { store as blockEditorStore } from '../../store';
import { unlock } from '../../lock-unlock';
import {
useBlockEditContext,
mayDisplayControlsKey,
Expand All @@ -36,7 +39,29 @@ export default function InspectorControlsFill( {
group = __experimentalGroup;
}

const context = useBlockEditContext();
const { clientId, ...context } = useBlockEditContext();

// Register any clientIds with `contentOnly` controls before the
// `mayDisplayControls` early return so that parent blocks can know whether
// child blocks have those controls.
const { addContentOnlyControlsBlock, removeContentOnlyControlsBlock } =
unlock( useDispatch( blockEditorStore ) );
useEffect( () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels a bit weird to me (two-way syncing). Could we instead calculate the contentOnly blocks using a memoed selector? I think this would mean keeping a static list of supported blocks though 🤔.

if ( group === 'contentOnly' ) {
addContentOnlyControlsBlock( clientId );
}
return () => {
if ( group === 'contentOnly' ) {
removeContentOnlyControlsBlock( clientId );
}
};
}, [
clientId,
group,
addContentOnlyControlsBlock,
removeContentOnlyControlsBlock,
] );

const Fill = groups[ group ]?.Fill;
if ( ! Fill ) {
warning( `Unknown InspectorControls group "${ group }" provided.` );
Expand Down
Loading
Loading