Skip to content

Commit

Permalink
Allow drill down to contentOnly blocks with back arrow to navigate ba…
Browse files Browse the repository at this point in the history
…ck to parent
  • Loading branch information
draganescu committed Oct 24, 2024
1 parent 27b1d9d commit f920007
Show file tree
Hide file tree
Showing 10 changed files with 288 additions and 141 deletions.
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 { __, 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
27 changes: 26 additions & 1 deletion packages/block-editor/src/components/inspector-controls/fill.js
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( () => {
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

0 comments on commit f920007

Please sign in to comment.