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

Suggest block pattern transformations that are contextual to the currently selected 'simple' blocks (no InnerBlocks) #30469

Merged
merged 13 commits into from
Apr 13, 2021
Merged
26 changes: 26 additions & 0 deletions lib/block-patterns.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,29 @@
<!-- /wp:group -->',
)
);

// Initial block patterns to be used in block transformations with patterns.
register_block_pattern(
'paragraph/large-with-background-color',
array(
'title' => __( 'Large Paragraph with background color', 'gutenberg' ),
'blockTypes' => array( 'core/paragraph' ),
'viewportWidth' => 500,
'content' => '<!-- wp:paragraph {"style":{"color":{"link":"#FFFFFF","text":"#FFFFFF","background":"#000000"},"typography":{"lineHeight":"1.3","fontSize":"26px"}}} -->
<p class="has-text-color has-background has-link-color" style="--wp--style--color--link:#FFFFFF;background-color:#000000;color:#FFFFFF;font-size:26px;line-height:1.3">The whole series of my life appeared to me as a dream; I sometimes doubted if indeed it were all true, for it never presented itself to my mind with the force of reality.</p>
<!-- /wp:paragraph -->',
)
);
register_block_pattern(
'social-links/shared-background-color',
array(
'title' => __( 'Social links with a shared background color', 'gutenberg' ),
'blockTypes' => array( 'core/social-links' ),
'viewportWidth' => 500,
'content' => '<!-- wp:social-links {"customIconColor":"#ffffff","iconColorValue":"#ffffff","customIconBackgroundColor":"#3962e3","iconBackgroundColorValue":"#3962e3","className":"has-icon-color"} -->
<ul class="wp-block-social-links has-icon-color has-icon-background-color"><!-- wp:social-link {"url":"https://wordpress.org","service":"wordpress"} /-->
<!-- wp:social-link {"url":"#","service":"chain"} /-->
<!-- wp:social-link {"url":"#","service":"mail"} /--></ul>
<!-- /wp:social-links -->',
)
);
46 changes: 37 additions & 9 deletions packages/block-editor/src/components/block-switcher/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import BlockIcon from '../block-icon';
import BlockTitle from '../block-title';
import BlockTransformationsMenu from './block-transformations-menu';
import BlockStylesMenu from './block-styles-menu';
import PatternTransformationsMenu from './pattern-transformations-menu';

export const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => {
const { replaceBlocks } = useDispatch( blockEditorStore );
Expand All @@ -40,12 +41,14 @@ export const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => {
hasBlockStyles,
icon,
blockTitle,
patterns,
} = useSelect(
( select ) => {
const { getBlockRootClientId, getBlockTransformItems } = select(
blockEditorStore
);

const {
getBlockRootClientId,
getBlockTransformItems,
__experimentalGetPatternTransformItems,
} = select( blockEditorStore );
const { getBlockStyles, getBlockType } = select( blocksStore );
const rootClientId = getBlockRootClientId(
castArray( clientIds )[ 0 ]
Expand All @@ -66,7 +69,6 @@ export const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => {
? getBlockType( firstBlockName )?.icon
: stack;
}

return {
possibleBlockTransformations: getBlockTransformItems(
blocks,
Expand All @@ -75,6 +77,10 @@ export const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => {
hasBlockStyles: !! styles?.length,
icon: _icon,
blockTitle: getBlockType( firstBlockName ).title,
patterns: __experimentalGetPatternTransformItems(
blocks,
rootClientId
),
};
},
[ clientIds, blocks, blockInformation?.icon ]
Expand All @@ -83,9 +89,14 @@ export const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => {
const isReusable = blocks.length === 1 && isReusableBlock( blocks[ 0 ] );
const isTemplate = blocks.length === 1 && isTemplatePart( blocks[ 0 ] );

const onTransform = ( name ) =>
// Simple block tranformation based on the `Block Transforms` API.
const onBlockTransform = ( name ) =>
replaceBlocks( clientIds, switchToBlockType( blocks, name ) );
// Pattern transformation through the `Patterns` API.
const onPatternTransform = ( transformedBlocks ) =>
replaceBlocks( clientIds, transformedBlocks );
const hasPossibleBlockTransformations = !! possibleBlockTransformations.length;
const hasPatternTransformation = !! patterns?.length;
if ( ! hasBlockStyles && ! hasPossibleBlockTransformations ) {
return (
<ToolbarGroup>
Expand Down Expand Up @@ -114,6 +125,10 @@ export const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => {
blocks.length
);

const showDropDown =
hasBlockStyles ||
hasPossibleBlockTransformations ||
hasPatternTransformation;
return (
<ToolbarGroup>
<ToolbarItem>
Expand Down Expand Up @@ -147,9 +162,22 @@ export const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => {
menuProps={ { orientation: 'both' } }
>
{ ( { onClose } ) =>
( hasBlockStyles ||
hasPossibleBlockTransformations ) && (
showDropDown && (
<div className="block-editor-block-switcher__container">
{ hasPatternTransformation && (
<PatternTransformationsMenu
blocks={ blocks }
patterns={ patterns }
onSelect={ (
transformedBlocks
) => {
onPatternTransform(
transformedBlocks
);
onClose();
} }
/>
) }
{ hasPossibleBlockTransformations && (
<BlockTransformationsMenu
className="block-editor-block-switcher__transforms__menugroup"
Expand All @@ -158,7 +186,7 @@ export const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => {
}
blocks={ blocks }
onSelect={ ( name ) => {
onTransform( name );
onBlockTransform( name );
onClose();
} }
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { useState } from '@wordpress/element';
import { useInstanceId } from '@wordpress/compose';
import { chevronRight } from '@wordpress/icons';

import {
MenuGroup,
MenuItem,
Popover,
VisuallyHidden,
__unstableComposite as Composite,
__unstableUseCompositeState as useCompositeState,
__unstableCompositeItem as CompositeItem,
} from '@wordpress/components';

/**
* Internal dependencies
*/
import BlockPreview from '../block-preview';
import useTransformedPatterns from './use-transformed-patterns';

function PatternTransformationsMenu( {
blocks,
patterns: statePatterns,
onSelect,
} ) {
const [ showTransforms, setShowTransforms ] = useState( false );
const patterns = useTransformedPatterns( statePatterns, blocks );
if ( ! patterns.length ) return null;

return (
<MenuGroup className="block-editor-block-switcher__pattern__transforms__menugroup">
{ showTransforms && (
<PreviewPatternsPopover
patterns={ patterns }
onSelect={ onSelect }
/>
) }
<MenuItem
onClick={ ( event ) => {
event.preventDefault();
setShowTransforms( ! showTransforms );
} }
icon={ chevronRight }
>
{ __( 'Patterns' ) }
</MenuItem>
</MenuGroup>
);
}

function PreviewPatternsPopover( { patterns, onSelect } ) {
return (
<div className="block-editor-block-switcher__popover__preview__parent">
<div className="block-editor-block-switcher__popover__preview__container">
<Popover
className="block-editor-block-switcher__preview__popover"
position="bottom right"
>
<div className="block-editor-block-switcher__preview">
<div className="block-editor-block-switcher__preview-title">
{ __( 'Preview' ) }
</div>
<BlockPatternsList
patterns={ patterns }
onSelect={ onSelect }
/>
</div>
</Popover>
</div>
</div>
);
}

function BlockPatternsList( { patterns, onSelect } ) {
const composite = useCompositeState();
return (
<Composite
{ ...composite }
role="listbox"
className="block-editor-block-switcher__preview-patterns-container"
aria-label={ __( 'Patterns list' ) }
>
{ patterns.map( ( pattern ) => (
<BlockPattern
key={ pattern.name }
pattern={ pattern }
onSelect={ onSelect }
composite={ composite }
/>
) ) }
</Composite>
);
}

function BlockPattern( { pattern, onSelect, composite } ) {
// TODO check pattern/preview width...
const baseClassName =
'block-editor-block-switcher__preview-patterns-container';
const descriptionId = useInstanceId(
BlockPattern,
`${ baseClassName }-list__item-description`
);
return (
<div
className={ `${ baseClassName }-list__list-item` }
aria-label={ pattern.title }
aria-describedby={ pattern.description ? descriptionId : undefined }
>
<CompositeItem
role="option"
as="div"
{ ...composite }
className={ `${ baseClassName }-list__item` }
onClick={ () => onSelect( pattern.transformedBlocks ) }
>
<BlockPreview
blocks={ pattern.transformedBlocks }
viewportWidth={ pattern.viewportWidth || 500 }
/>
<div className={ `${ baseClassName }-list__item-title` }>
{ pattern.title }
</div>
</CompositeItem>
{ !! pattern.description && (
<VisuallyHidden id={ descriptionId }>
{ pattern.description }
</VisuallyHidden>
) }
</div>
);
}

export default PatternTransformationsMenu;
41 changes: 40 additions & 1 deletion packages/block-editor/src/components/block-switcher/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@
padding: 0;

.components-menu-group {
padding: $grid-unit-20;
margin: 0;
}
}
Expand Down Expand Up @@ -149,6 +148,7 @@
.block-editor-block-switcher__preview {
width: 300px;
height: auto;
max-height: 500px;
padding: $grid-unit-20;
}
}
Expand Down Expand Up @@ -182,3 +182,42 @@
}
}
}

.block-editor-block-switcher__preview-patterns-container {
padding-bottom: $grid-unit-20;

.block-editor-block-switcher__preview-patterns-container-list__list-item {
margin-top: $grid-unit-20;

.block-editor-block-preview__container {
cursor: pointer;
}

.block-editor-block-switcher__preview-patterns-container-list__item {
height: 100%;
border-radius: $radius-block-ui;
transition: all 0.05s ease-in-out;
position: relative;
border: $border-width solid transparent;

&:hover,
&:focus {
box-shadow: inset 0 0 0 1px $white, 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color);

// Windows High Contrast mode will show this outline, but not the box-shadow.
outline: 2px solid transparent;
}

&:hover {
box-shadow: inset 0 0 0 1px $white, 0 0 0 var(--wp-admin-border-width-focus) $gray-900;
}

.block-editor-block-switcher__preview-patterns-container-list__item-title {
padding: $grid-unit-05;
font-size: 12px;
text-align: center;
cursor: pointer;
}
}
}
}
Loading