From e522dd9d3a856ac0f0cb543f9d2d654a1d194781 Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Fri, 11 Aug 2023 08:27:44 +1000 Subject: [PATCH] List View: Add media previews to list view for gallery and image blocks (#53381) * List View: Add images * Add some styling and a max number of images * Tweak styling * Add image count * Reduce to 3 images, remove image count --- .../list-view/block-select-button.js | 19 +++++ .../src/components/list-view/style.scss | 25 ++++++ .../list-view/use-list-view-images.js | 79 +++++++++++++++++++ 3 files changed, 123 insertions(+) create mode 100644 packages/block-editor/src/components/list-view/use-list-view-images.js diff --git a/packages/block-editor/src/components/list-view/block-select-button.js b/packages/block-editor/src/components/list-view/block-select-button.js index b6517357e7b657..930720fe565825 100644 --- a/packages/block-editor/src/components/list-view/block-select-button.js +++ b/packages/block-editor/src/components/list-view/block-select-button.js @@ -28,6 +28,7 @@ import useBlockDisplayTitle from '../block-title/use-block-display-title'; import ListViewExpander from './expander'; import { useBlockLock } from '../block-lock'; import { store as blockEditorStore } from '../../store'; +import useListViewImages from './use-list-view-images'; function ListViewBlockSelectButton( { @@ -63,6 +64,7 @@ function ListViewBlockSelectButton( const { removeBlocks } = useDispatch( blockEditorStore ); const isMatch = useShortcutEventMatch(); const isSticky = blockInformation?.positionType === 'sticky'; + const images = useListViewImages( { clientId, isExpanded } ); const positionLabel = blockInformation?.positionLabel ? sprintf( @@ -184,6 +186,23 @@ function ListViewBlockSelectButton( ) } + { images.length ? ( + + { images.map( ( image, index ) => ( + + ) ) } + + ) : null } { isLocked && ( diff --git a/packages/block-editor/src/components/list-view/style.scss b/packages/block-editor/src/components/list-view/style.scss index ab041af51aa1e9..efd625a6d83ba0 100644 --- a/packages/block-editor/src/components/list-view/style.scss +++ b/packages/block-editor/src/components/list-view/style.scss @@ -339,6 +339,31 @@ .block-editor-list-view-block-select-button__sticky { line-height: 0; } + + .block-editor-list-view-block-select-button__images { + display: flex; + } + + .block-editor-list-view-block-select-button__image { + background-size: cover; + width: 20px; + height: 20px; + border-radius: $radius-block-ui; + + &:not(:only-child) { + box-shadow: 0 0 0 $radius-block-ui $white; + } + + &:not(:first-child) { + margin-left: -5px; + } + } + + &.is-selected .block-editor-list-view-block-select-button__image { + &:not(:only-child) { + box-shadow: 0 0 0 $radius-block-ui var(--wp-admin-theme-color); + } + } } .block-editor-list-view-block__contents-cell, diff --git a/packages/block-editor/src/components/list-view/use-list-view-images.js b/packages/block-editor/src/components/list-view/use-list-view-images.js new file mode 100644 index 00000000000000..85dea4292f3600 --- /dev/null +++ b/packages/block-editor/src/components/list-view/use-list-view-images.js @@ -0,0 +1,79 @@ +/** + * WordPress dependencies + */ +import { useMemo } from '@wordpress/element'; +import { useSelect } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import { store as blockEditorStore } from '../../store'; + +// Maximum number of images to display in a list view row. +const MAX_IMAGES = 3; + +function getImageUrl( block ) { + if ( block.name !== 'core/image' ) { + return; + } + + if ( block.attributes?.url ) { + return { url: block.attributes.url, alt: block.attributes.alt }; + } +} + +function getImagesFromGallery( block ) { + if ( block.name !== 'core/gallery' || ! block.innerBlocks ) { + return []; + } + + const images = []; + + for ( const innerBlock of block.innerBlocks ) { + const img = getImageUrl( innerBlock ); + if ( img ) { + images.push( img ); + } + if ( images.length >= MAX_IMAGES ) { + return images; + } + } + + return images; +} + +function getImagesFromBlock( block, isExpanded ) { + const img = getImageUrl( block ); + if ( img ) { + return [ img ]; + } + return isExpanded ? [] : getImagesFromGallery( block ); +} + +/** + * Get a block's preview images for display within a list view row. + * + * TODO: Currently only supports images from the core/image and core/gallery + * blocks. This should be expanded to support other blocks that have images, + * potentially via an API that blocks can opt into / provide their own logic. + * + * @param {Object} props Hook properties. + * @param {string} props.clientId The block's clientId. + * @param {boolean} props.isExpanded Whether or not the block is expanded in the list view. + * @return {Array} Images. + */ +export default function useListViewImages( { clientId, isExpanded } ) { + const { block } = useSelect( + ( select ) => { + const _block = select( blockEditorStore ).getBlock( clientId ); + return { block: _block }; + }, + [ clientId ] + ); + + const images = useMemo( () => { + return getImagesFromBlock( block, isExpanded ); + }, [ block, isExpanded ] ); + + return images; +}