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;
+}