-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Suggest Block patterns in block placeholder states (#29602)
* temp commit * renamings and css classname changes * Style tidy up - Padding on various pattern selection elements - Use `cursor: pointer` when hovering patterns - Ensure carousel/grid view toggle is centrally aligned - Clean up vertical alignment of toolbar * remove __experimentalGetScopedBlockPatterns usage * remove from Buttons * cleanup BlockPatternSetup part 1 * add to Query and remove from Columns * remove query styles from previous integration * add title to grid patterns * provide rootClientId for pattern fetching * Polish. * Add border, fix box sizing. * adress feedback * Fix z-index. * add e2e tests Co-authored-by: James Koster <[email protected]> Co-authored-by: jasmussen <[email protected]>
- Loading branch information
1 parent
0d78904
commit 6244cf5
Showing
15 changed files
with
614 additions
and
491 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
4 changes: 4 additions & 0 deletions
4
packages/block-editor/src/components/block-pattern-setup/constants.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export const VIEWMODES = { | ||
carousel: 'carousel', | ||
grid: 'grid', | ||
}; |
184 changes: 184 additions & 0 deletions
184
packages/block-editor/src/components/block-pattern-setup/index.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { useDispatch } from '@wordpress/data'; | ||
import { cloneBlock } from '@wordpress/blocks'; | ||
import { | ||
VisuallyHidden, | ||
__unstableComposite as Composite, | ||
__unstableUseCompositeState as useCompositeState, | ||
__unstableCompositeItem as CompositeItem, | ||
} from '@wordpress/components'; | ||
|
||
import { useState } from '@wordpress/element'; | ||
import { useInstanceId } from '@wordpress/compose'; | ||
import { __ } from '@wordpress/i18n'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { store as blockEditorStore } from '../../store'; | ||
import BlockPreview from '../block-preview'; | ||
import SetupToolbar from './setup-toolbar'; | ||
import usePatternsSetup from './use-patterns-setup'; | ||
import { VIEWMODES } from './constants'; | ||
|
||
const SetupContent = ( { | ||
viewMode, | ||
activeSlide, | ||
patterns, | ||
onBlockPatternSelect, | ||
} ) => { | ||
const composite = useCompositeState(); | ||
const containerClass = 'block-editor-block-pattern-setup__container'; | ||
if ( viewMode === VIEWMODES.carousel ) { | ||
const slideClass = new Map( [ | ||
[ activeSlide, 'active-slide' ], | ||
[ activeSlide - 1, 'previous-slide' ], | ||
[ activeSlide + 1, 'next-slide' ], | ||
] ); | ||
return ( | ||
<div className={ containerClass }> | ||
<ul className="carousel-container"> | ||
{ patterns.map( ( pattern, index ) => ( | ||
<BlockPatternSlide | ||
className={ slideClass.get( index ) || '' } | ||
key={ pattern.name } | ||
pattern={ pattern } | ||
/> | ||
) ) } | ||
</ul> | ||
</div> | ||
); | ||
} | ||
return ( | ||
<Composite | ||
{ ...composite } | ||
role="listbox" | ||
className={ containerClass } | ||
aria-label={ __( 'Patterns list' ) } | ||
> | ||
{ patterns.map( ( pattern ) => ( | ||
<BlockPattern | ||
key={ pattern.name } | ||
pattern={ pattern } | ||
onSelect={ onBlockPatternSelect } | ||
composite={ composite } | ||
/> | ||
) ) } | ||
</Composite> | ||
); | ||
}; | ||
|
||
function BlockPattern( { pattern, onSelect, composite } ) { | ||
const baseClassName = 'block-editor-block-pattern-setup-list'; | ||
const { blocks, title, description, viewportWidth = 700 } = pattern; | ||
const descriptionId = useInstanceId( | ||
BlockPattern, | ||
`${ baseClassName }__item-description` | ||
); | ||
return ( | ||
<div | ||
className={ `${ baseClassName }__list-item` } | ||
aria-label={ pattern.title } | ||
aria-describedby={ pattern.description ? descriptionId : undefined } | ||
> | ||
<CompositeItem | ||
role="option" | ||
as="div" | ||
{ ...composite } | ||
className={ `${ baseClassName }__item` } | ||
onClick={ () => onSelect( blocks ) } | ||
> | ||
<BlockPreview | ||
blocks={ blocks } | ||
viewportWidth={ viewportWidth } | ||
/> | ||
<div className={ `${ baseClassName }__item-title` }> | ||
{ title } | ||
</div> | ||
</CompositeItem> | ||
{ !! description && ( | ||
<VisuallyHidden id={ descriptionId }> | ||
{ description } | ||
</VisuallyHidden> | ||
) } | ||
</div> | ||
); | ||
} | ||
|
||
function BlockPatternSlide( { className, pattern } ) { | ||
const { blocks, title, description } = pattern; | ||
const descriptionId = useInstanceId( | ||
BlockPatternSlide, | ||
'block-editor-block-pattern-setup-list__item-description' | ||
); | ||
return ( | ||
<li | ||
className={ `pattern-slide ${ className }` } | ||
aria-label={ title } | ||
aria-describedby={ description ? descriptionId : undefined } | ||
> | ||
<BlockPreview blocks={ blocks } __experimentalLive /> | ||
{ !! description && ( | ||
<VisuallyHidden id={ descriptionId }> | ||
{ description } | ||
</VisuallyHidden> | ||
) } | ||
</li> | ||
); | ||
} | ||
|
||
const BlockPatternSetup = ( { | ||
clientId, | ||
blockName, | ||
filterPatternsFn, | ||
startBlankComponent, | ||
} ) => { | ||
const [ viewMode, setViewMode ] = useState( VIEWMODES.carousel ); | ||
const [ activeSlide, setActiveSlide ] = useState( 0 ); | ||
const [ showBlank, setShowBlank ] = useState( false ); | ||
const { replaceBlock } = useDispatch( blockEditorStore ); | ||
const patterns = usePatternsSetup( clientId, blockName, filterPatternsFn ); | ||
|
||
if ( ! patterns?.length || showBlank ) { | ||
return startBlankComponent; | ||
} | ||
|
||
const onBlockPatternSelect = ( blocks ) => { | ||
const clonedBlocks = blocks.map( ( block ) => cloneBlock( block ) ); | ||
replaceBlock( clientId, clonedBlocks ); | ||
}; | ||
return ( | ||
<div | ||
className={ `block-editor-block-pattern-setup view-mode-${ viewMode }` } | ||
> | ||
<SetupToolbar | ||
viewMode={ viewMode } | ||
setViewMode={ setViewMode } | ||
activeSlide={ activeSlide } | ||
totalSlides={ patterns.length } | ||
handleNext={ () => { | ||
setActiveSlide( ( active ) => active + 1 ); | ||
} } | ||
handlePrevious={ () => { | ||
setActiveSlide( ( active ) => active - 1 ); | ||
} } | ||
onBlockPatternSelect={ () => { | ||
onBlockPatternSelect( patterns[ activeSlide ].blocks ); | ||
} } | ||
onStartBlank={ () => { | ||
setShowBlank( true ); | ||
} } | ||
/> | ||
<SetupContent | ||
viewMode={ viewMode } | ||
activeSlide={ activeSlide } | ||
patterns={ patterns } | ||
onBlockPatternSelect={ onBlockPatternSelect } | ||
/> | ||
</div> | ||
); | ||
}; | ||
|
||
export default BlockPatternSetup; |
97 changes: 97 additions & 0 deletions
97
packages/block-editor/src/components/block-pattern-setup/setup-toolbar.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { __ } from '@wordpress/i18n'; | ||
import { Button } from '@wordpress/components'; | ||
import { | ||
chevronRight, | ||
chevronLeft, | ||
grid, | ||
stretchFullWidth, | ||
} from '@wordpress/icons'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { VIEWMODES } from './constants'; | ||
|
||
const Actions = ( { onStartBlank, onBlockPatternSelect } ) => ( | ||
<div className="block-editor-block-pattern-setup__actions"> | ||
<Button onClick={ onStartBlank }>{ __( 'Start blank' ) }</Button> | ||
<Button isPrimary onClick={ onBlockPatternSelect }> | ||
{ __( 'Choose' ) } | ||
</Button> | ||
</div> | ||
); | ||
|
||
const CarouselNavigation = ( { | ||
handlePrevious, | ||
handleNext, | ||
activeSlide, | ||
totalSlides, | ||
} ) => ( | ||
<div className="block-editor-block-pattern-setup__navigation"> | ||
<Button | ||
icon={ chevronLeft } | ||
label={ __( 'Previous pattern' ) } | ||
onClick={ handlePrevious } | ||
disabled={ activeSlide === 0 } | ||
/> | ||
<Button | ||
icon={ chevronRight } | ||
label={ __( 'Next pattern' ) } | ||
onClick={ handleNext } | ||
disabled={ activeSlide === totalSlides - 1 } | ||
/> | ||
</div> | ||
); | ||
|
||
const SetupToolbar = ( { | ||
viewMode, | ||
setViewMode, | ||
handlePrevious, | ||
handleNext, | ||
activeSlide, | ||
totalSlides, | ||
onBlockPatternSelect, | ||
onStartBlank, | ||
} ) => { | ||
const isCarouselView = viewMode === VIEWMODES.carousel; | ||
const displayControls = ( | ||
<div className="block-editor-block-pattern-setup__display-controls"> | ||
<Button | ||
icon={ stretchFullWidth } | ||
label={ __( 'Carousel view' ) } | ||
onClick={ () => setViewMode( VIEWMODES.carousel ) } | ||
isPressed={ isCarouselView } | ||
/> | ||
<Button | ||
icon={ grid } | ||
label={ __( 'Grid view' ) } | ||
onClick={ () => setViewMode( VIEWMODES.grid ) } | ||
isPressed={ viewMode === VIEWMODES.grid } | ||
/> | ||
</div> | ||
); | ||
return ( | ||
<div className="block-editor-block-pattern-setup__toolbar"> | ||
{ isCarouselView && ( | ||
<CarouselNavigation | ||
handlePrevious={ handlePrevious } | ||
handleNext={ handleNext } | ||
activeSlide={ activeSlide } | ||
totalSlides={ totalSlides } | ||
/> | ||
) } | ||
{ displayControls } | ||
{ isCarouselView && ( | ||
<Actions | ||
onBlockPatternSelect={ onBlockPatternSelect } | ||
onStartBlank={ onStartBlank } | ||
/> | ||
) } | ||
</div> | ||
); | ||
}; | ||
|
||
export default SetupToolbar; |
Oops, something went wrong.