Skip to content
Merged
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
133 changes: 95 additions & 38 deletions packages/block-editor/src/components/block-card/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,20 @@ import clsx from 'clsx';
*/
import {
Button,
Icon,
__experimentalText as Text,
__experimentalVStack as VStack,
privateApis as componentsPrivateApis,
} from '@wordpress/components';
import { useDispatch, useSelect } from '@wordpress/data';
import deprecated from '@wordpress/deprecated';
import { __, isRTL } from '@wordpress/i18n';
import { chevronLeft, chevronRight } from '@wordpress/icons';
import {
chevronLeft,
chevronRight,
arrowRight,
arrowLeft,
} from '@wordpress/icons';

/**
* Internal dependencies
Expand All @@ -26,6 +32,22 @@ import BlockIcon from '../block-icon';

const { Badge } = unlock( componentsPrivateApis );

function OptionalParentSelectButton( { children, onClick } ) {
if ( ! onClick ) {
return children;
}

return (
<Button
__next40pxDefaultSize
className="block-editor-block-card__parent-select-button"
onClick={ onClick }
>
{ children }
</Button>
);
}

/**
* A card component that displays block information including title, icon, and description.
* Can be used to show block metadata and navigation controls for parent blocks.
Expand Down Expand Up @@ -54,6 +76,9 @@ const { Badge } = unlock( componentsPrivateApis );
* @param {string} [props.className] Additional classes to apply to the card.
* @param {string} [props.name] Custom block name to display before the title.
* @param {string} [props.allowParentNavigation] Show a back arrow to the parent block in some situations.
* @param {string} [props.parentClientId] The parent clientId, if this card is for a parent block.
* @param {string} [props.isChild] Whether the block card is for a child block, in which case, indent the block using an arrow.
* @param {string} [props.clientId] Whether the block card is for a child block, in which case, indent the block using an arrow.
* @param {Element} [props.children] Children.
* @return {Element} Block card component.
*/
Expand All @@ -65,7 +90,10 @@ function BlockCard( {
className,
name,
allowParentNavigation,
parentClientId,
isChild,
children,
clientId,
} ) {
if ( blockType ) {
deprecated( '`blockType` property in `BlockCard component`', {
Expand All @@ -77,56 +105,85 @@ function BlockCard( {

const parentNavBlockClientId = useSelect(
( select ) => {
if ( ! allowParentNavigation ) {
if ( parentClientId || isChild || ! allowParentNavigation ) {
return;
}
const { getSelectedBlockClientId, getBlockParentsByBlockName } =
select( blockEditorStore );

const _selectedBlockClientId = getSelectedBlockClientId();
const { getBlockParentsByBlockName } = select( blockEditorStore );

return getBlockParentsByBlockName(
_selectedBlockClientId,
clientId,
'core/navigation',
true
)[ 0 ];
},
[ allowParentNavigation ]
[ clientId, allowParentNavigation, isChild, parentClientId ]
);

const { selectBlock } = useDispatch( blockEditorStore );

const TitleElement = parentClientId ? 'div' : 'h2';

return (
<div className={ clsx( 'block-editor-block-card', className ) }>
{ allowParentNavigation &&
parentNavBlockClientId && ( // 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' ) }
style={
// TODO: This style override is also used in ToolsPanelHeader.
// It should be supported out-of-the-box by Button.
{ minWidth: 24, padding: 0 }
}
icon={ isRTL() ? chevronRight : chevronLeft }
size="small"
/>
) }
<BlockIcon icon={ icon } showColors />
<VStack spacing={ 1 }>
<h2 className="block-editor-block-card__title">
<span className="block-editor-block-card__name">
{ !! name?.length ? name : title }
</span>
{ !! name?.length && <Badge>{ title }</Badge> }
</h2>
{ description && (
<Text className="block-editor-block-card__description">
{ description }
</Text>
) }
{ children }
</VStack>
<div
className={ clsx(
'block-editor-block-card',
{
'is-parent': parentClientId,
'is-child': isChild,
},
className
) }
>
{ parentNavBlockClientId && ( // 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={
parentNavBlockClientId
? __( 'Go to parent Navigation block' )
: // TODO - improve copy, not sure that we should use the term 'section'
__( 'Go to parent section' )
Copy link
Contributor

Choose a reason for hiding this comment

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

Since we're flattening the structure, could "go to parent" be enough? (Not a blocker for now, of course)

Suggested change
__( 'Go to parent section' )
__( 'Go to parent' )

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ended up removing this, it's from an earlier iteration!

}
style={
// TODO: This style override is also used in ToolsPanelHeader.
// It should be supported out-of-the-box by Button.
{ minWidth: 24, padding: 0 }
}
icon={ isRTL() ? chevronRight : chevronLeft }
size="small"
/>
) }
{ isChild && (
<span className="block-editor-block-card__child-indicator-icon">
<Icon icon={ isRTL() ? arrowLeft : arrowRight } />
</span>
) }
<OptionalParentSelectButton
onClick={
parentClientId
? () => {
selectBlock( parentClientId );
}
: undefined
}
>
<BlockIcon icon={ icon } showColors />
<VStack spacing={ 1 }>
<TitleElement className="block-editor-block-card__title">
<span className="block-editor-block-card__name">
{ !! name?.length ? name : title }
</span>
{ ! parentClientId && ! isChild && !! name?.length && (
<Badge>{ title }</Badge>
) }
</TitleElement>
{ ! parentClientId && ! isChild && description && (
<Text className="block-editor-block-card__description">
{ description }
</Text>
) }
{ children }
</VStack>
</OptionalParentSelectButton>
</div>
);
}
Expand Down
18 changes: 17 additions & 1 deletion packages/block-editor/src/components/block-card/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,21 @@
color: $gray-900;
display: flex;
padding: $grid-unit-20;

&.is-parent {
padding-bottom: $grid-unit-05;
}

&.is-child {
padding-top: $grid-unit-05;
}
}

.block-editor-block-card__parent-select-button {
padding: 0;
align-items: start;
text-align: start;
height: auto !important;
}

.block-editor-block-card__title {
Expand All @@ -26,7 +41,8 @@
padding: 3px 0; // This makes the title as high as the icon.
}

.block-editor-block-card .block-editor-block-icon {
.block-editor-block-card .block-editor-block-icon,
.block-editor-block-card__child-indicator-icon {
flex: 0 0 $button-size-small;
margin-left: 0;
margin-right: $grid-unit-15;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* WordPress dependencies
*/
import { Button, __experimentalVStack as VStack } from '@wordpress/components';
import { useDispatch, useSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import { store as blockEditorStore } from '../../store';
import { unlock } from '../../lock-unlock';

export default function EditContents( { clientId } ) {
const { editContentOnlySection, stopEditingContentOnlySection } = unlock(
useDispatch( blockEditorStore )
);
const { isWithinSection, isWithinEditedSection, editedContentOnlySection } =
useSelect(
( select ) => {
const {
isSectionBlock,
getParentSectionBlock,
getEditedContentOnlySection,
isWithinEditedContentOnlySection,
} = unlock( select( blockEditorStore ) );

return {
isWithinSection:
isSectionBlock( clientId ) ||
!! getParentSectionBlock( clientId ),
isWithinEditedSection:
isWithinEditedContentOnlySection( clientId ),
editedContentOnlySection: getEditedContentOnlySection(),
};
},
[ clientId ]
);

Copy link
Member

Choose a reason for hiding this comment

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

Wondering if we should stop editing blocks when this component unmounts too? E.g.,

  useEffect( () => {
    return () => {
      stopEditingAsBlocks();
    };
  }, [ stopEditingAsBlocks ] );

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess a problem with doing that would be that if we close the sidebar, then we'd stop editing blocks, which mightn't be desired?

if ( ! isWithinSection && ! isWithinEditedSection ) {
return null;
}

return (
<VStack className="block-editor-block-inspector-edit-contents" expanded>
<Button
className="block-editor-block-inspector-edit-contents__button"
__next40pxDefaultSize
variant="secondary"
onClick={ () => {
if ( ! editedContentOnlySection ) {
editContentOnlySection( clientId );
} else {
stopEditingContentOnlySection();
}
} }
>
{ editedContentOnlySection
? __( 'Lock design' )
: __( 'Unlock design' ) }
</Button>
</VStack>
);
}
Loading
Loading