From 72ccb247fe1d63bcad7da2395592e7b69ee9e26d Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 17 Oct 2023 11:37:18 +0100 Subject: [PATCH] DataViews: Add Grid Layout (#55353) --- .../src/components/dataviews/field-actions.js | 28 ++++++++ .../src/components/dataviews/style.scss | 10 +++ .../src/components/dataviews/text-filter.js | 10 ++- .../src/components/dataviews/view-grid.js | 64 ++++++++++++++++++- .../src/components/dataviews/view-list.js | 45 ++++--------- .../edit-site/src/components/media/index.js | 10 ++- .../src/components/page-pages/index.js | 58 +++++++++++++---- .../src/components/page-pages/style.scss | 1 - 8 files changed, 169 insertions(+), 57 deletions(-) create mode 100644 packages/edit-site/src/components/dataviews/field-actions.js diff --git a/packages/edit-site/src/components/dataviews/field-actions.js b/packages/edit-site/src/components/dataviews/field-actions.js new file mode 100644 index 00000000000000..490faa1f6a126c --- /dev/null +++ b/packages/edit-site/src/components/dataviews/field-actions.js @@ -0,0 +1,28 @@ +/** + * WordPress dependencies + */ +import { DropdownMenu, MenuGroup, MenuItem } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; +import { moreVertical } from '@wordpress/icons'; + +function FieldActions( { item, actions } ) { + return ( + + { () => ( + + { actions.map( ( action ) => ( + action.perform( item ) } + isDestructive={ action.isDesctructive } + > + { action.label } + + ) ) } + + ) } + + ); +} + +export default FieldActions; diff --git a/packages/edit-site/src/components/dataviews/style.scss b/packages/edit-site/src/components/dataviews/style.scss index 47e80782255a4a..1b27c0833c836c 100644 --- a/packages/edit-site/src/components/dataviews/style.scss +++ b/packages/edit-site/src/components/dataviews/style.scss @@ -34,3 +34,13 @@ color: $gray-700; text-wrap: nowrap; } + +.dataviews-view-grid__media { + width: 100%; + min-height: 200px; + + > * { + max-width: 100%; + object-fit: cover; + } +} diff --git a/packages/edit-site/src/components/dataviews/text-filter.js b/packages/edit-site/src/components/dataviews/text-filter.js index 5a0b6820e6716d..31eb5b25e91aa2 100644 --- a/packages/edit-site/src/components/dataviews/text-filter.js +++ b/packages/edit-site/src/components/dataviews/text-filter.js @@ -2,7 +2,7 @@ * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { useEffect } from '@wordpress/element'; +import { useEffect, useRef } from '@wordpress/element'; import { SearchControl } from '@wordpress/components'; /** @@ -14,13 +14,17 @@ export default function TextFilter( { view, onChangeView } ) { const [ search, setSearch, debouncedSearch ] = useDebouncedInput( view.search ); + const onChangeViewRef = useRef( onChangeView ); useEffect( () => { - onChangeView( ( currentView ) => ( { + onChangeViewRef.current = onChangeView; + }, [ onChangeView ] ); + useEffect( () => { + onChangeViewRef.current( ( currentView ) => ( { ...currentView, search: debouncedSearch, page: 1, } ) ); - }, [ debouncedSearch, onChangeView ] ); + }, [ debouncedSearch ] ); const searchLabel = __( 'Filter list' ); return ( field.id === view.layout.mediaField + ); + const visibleFields = fields.filter( + ( field ) => + ! view.hiddenFields.includes( field.id ) && + field.id !== view.layout.mediaField + ); + return ( + + { data.map( ( item, index ) => { + return ( + +
+ { ( mediaField && + mediaField.render( { item, view } ) ) || ( + + ) } +
+ + + + + { visibleFields.map( ( field ) => ( +
+ { field.render + ? field.render( { item, view } ) + : field.accessorFn( item ) } +
+ ) ) } +
+
+ +
+
+ ); + } ) } +
+ ); } diff --git a/packages/edit-site/src/components/dataviews/view-list.js b/packages/edit-site/src/components/dataviews/view-list.js index c2080863622a45..c8d4ee1efd063d 100644 --- a/packages/edit-site/src/components/dataviews/view-list.js +++ b/packages/edit-site/src/components/dataviews/view-list.js @@ -21,16 +21,12 @@ import { check, arrowUp, arrowDown, - moreVertical, } from '@wordpress/icons'; import { Button, Icon, privateApis as componentsPrivateApis, VisuallyHidden, - DropdownMenu, - MenuGroup, - MenuItem, } from '@wordpress/components'; import { useMemo } from '@wordpress/element'; @@ -38,6 +34,7 @@ import { useMemo } from '@wordpress/element'; * Internal dependencies */ import { unlock } from '../../lock-unlock'; +import FieldActions from './field-actions'; const { DropdownMenuV2, @@ -135,38 +132,24 @@ function ViewList( { paginationInfo, } ) { const columns = useMemo( () => { - const _columns = [ ...fields ]; + const _columns = [ + ...fields.map( ( field ) => { + const column = { ...field }; + delete column.render; + column.cell = ( props ) => { + return field.render + ? field.render( { item: props.row.original, view } ) + : field.accessorFn( props.row.original ); + }; + return column; + } ), + ]; if ( actions?.length ) { _columns.push( { header: { __( 'Actions' ) }, id: 'actions', cell: ( props ) => { - return ( - - { () => ( - - { actions.map( ( action ) => ( - - action.perform( - props.row.original - ) - } - isDestructive={ - action.isDesctructive - } - > - { action.label } - - ) ) } - - ) } - - ); + return ; }, enableHiding: false, } ); diff --git a/packages/edit-site/src/components/media/index.js b/packages/edit-site/src/components/media/index.js index 7120d7c7f56ce7..7df6c4f882842e 100644 --- a/packages/edit-site/src/components/media/index.js +++ b/packages/edit-site/src/components/media/index.js @@ -3,14 +3,12 @@ */ import { useEntityRecord } from '@wordpress/core-data'; -function Media( { id, size, ...props } ) { +function Media( { id, size = [ 'large', 'medium', 'thumbnail' ], ...props } ) { const { record: media } = useEntityRecord( 'root', 'media', id ); - const sizesPerPriority = [ 'large', 'thumbnail' ]; - const currentSize = - size ?? - sizesPerPriority.find( ( s ) => !! media?.media_details?.sizes[ s ] ); + const currentSize = size.find( + ( s ) => !! media?.media_details?.sizes[ s ] + ); const mediaDetails = media?.media_details?.sizes[ currentSize ]; - if ( ! mediaDetails ) { return null; } diff --git a/packages/edit-site/src/components/page-pages/index.js b/packages/edit-site/src/components/page-pages/index.js index 33a5cc9f81a12c..602d14b8ad2e3b 100644 --- a/packages/edit-site/src/components/page-pages/index.js +++ b/packages/edit-site/src/components/page-pages/index.js @@ -8,7 +8,7 @@ import { import { __ } from '@wordpress/i18n'; import { useEntityRecords } from '@wordpress/core-data'; import { decodeEntities } from '@wordpress/html-entities'; -import { useState, useMemo } from '@wordpress/element'; +import { useState, useMemo, useCallback } from '@wordpress/element'; import { dateI18n, getDate, getSettings } from '@wordpress/date'; /** @@ -22,6 +22,12 @@ import Media from '../media'; const EMPTY_ARRAY = []; const EMPTY_OBJECT = {}; +const defaultConfigPerViewType = { + list: {}, + grid: { + mediaField: 'featured-image', + }, +}; export default function PagePages() { const [ view, setView ] = useState( { @@ -36,6 +42,7 @@ export default function PagePages() { // All fields are visible by default, so it's // better to keep track of the hidden ones. hiddenFields: [ 'date', 'featured-image' ], + layout: {}, } ); // Request post statuses to get the proper labels. const { records: statuses } = useEntityRecords( 'root', 'status' ); @@ -82,12 +89,16 @@ export default function PagePages() { id: 'featured-image', header: __( 'Featured Image' ), accessorFn: ( page ) => page.featured_media, - cell: ( props ) => - !! props.row.original.featured_media ? ( + render: ( { item, view: currentView } ) => + !! item.featured_media ? ( ) : null, enableSorting: false, @@ -96,8 +107,7 @@ export default function PagePages() { header: __( 'Title' ), id: 'title', accessorFn: ( page ) => page.title?.rendered || page.slug, - cell: ( props ) => { - const page = props.row.original; + render: ( { item: page } ) => { return ( @@ -108,8 +118,9 @@ export default function PagePages() { canvas: 'edit', } } > - { decodeEntities( props.getValue() ) || - __( '(no title)' ) } + { decodeEntities( + page.title?.rendered || page.slug + ) || __( '(no title)' ) } @@ -123,8 +134,8 @@ export default function PagePages() { header: __( 'Author' ), id: 'author', accessorFn: ( page ) => page._embedded?.author[ 0 ]?.name, - cell: ( props ) => { - const author = props.row.original._embedded?.author[ 0 ]; + render: ( { item } ) => { + const author = item._embedded?.author[ 0 ]; return ( { author.name } @@ -142,10 +153,10 @@ export default function PagePages() { { header: 'Date', id: 'date', - cell: ( props ) => { + render: ( { item } ) => { const formattedDate = dateI18n( getSettings().formats.datetimeAbbreviated, - getDate( props.row.original.date ) + getDate( item.date ) ); return ; }, @@ -157,6 +168,25 @@ export default function PagePages() { const trashPostAction = useTrashPostAction(); const actions = useMemo( () => [ trashPostAction ], [ trashPostAction ] ); + const onChangeView = useCallback( + ( viewUpdater ) => { + let updatedView = + typeof viewUpdater === 'function' + ? viewUpdater( view ) + : viewUpdater; + if ( updatedView.type !== view.type ) { + updatedView = { + ...updatedView, + layout: { + ...defaultConfigPerViewType[ updatedView.type ], + }, + }; + } + + setView( updatedView ); + }, + [ view ] + ); // TODO: we need to handle properly `data={ data || EMPTY_ARRAY }` for when `isLoading`. return ( @@ -168,7 +198,7 @@ export default function PagePages() { data={ pages || EMPTY_ARRAY } isLoading={ isLoadingPages } view={ view } - onChangeView={ setView } + onChangeView={ onChangeView } /> ); diff --git a/packages/edit-site/src/components/page-pages/style.scss b/packages/edit-site/src/components/page-pages/style.scss index 82e124b3ac4fe9..fde960ca1a72ca 100644 --- a/packages/edit-site/src/components/page-pages/style.scss +++ b/packages/edit-site/src/components/page-pages/style.scss @@ -1,4 +1,3 @@ .edit-site-page-pages__featured-image { border-radius: $radius-block-ui; - max-height: 60px; }