diff --git a/package-lock.json b/package-lock.json index 80a3f384e808e..ec0fbd96b0cb1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -55075,6 +55075,7 @@ "@wordpress/element": "file:../element", "@wordpress/i18n": "file:../i18n", "@wordpress/icons": "file:../icons", + "@wordpress/keycodes": "file:../keycodes", "@wordpress/private-apis": "file:../private-apis", "classnames": "^2.3.1", "remove-accents": "^0.5.0" @@ -70411,6 +70412,7 @@ "@wordpress/element": "file:../element", "@wordpress/i18n": "file:../i18n", "@wordpress/icons": "file:../icons", + "@wordpress/keycodes": "file:../keycodes", "@wordpress/private-apis": "file:../private-apis", "classnames": "^2.3.1", "remove-accents": "^0.5.0" diff --git a/packages/dataviews/README.md b/packages/dataviews/README.md index 2ee1d7f42eff5..c0d0a01cbc3e2 100644 --- a/packages/dataviews/README.md +++ b/packages/dataviews/README.md @@ -22,6 +22,7 @@ npm install @wordpress/dataviews --save fields={ fields } actions={ [ trashPostAction ] } paginationInfo={ { totalItems, totalPages } } + onSelectionChange={ ( items ) => { /* ... */ } } /> ``` @@ -75,8 +76,8 @@ Example: - `value`: the actual value selected by the user. - `hiddenFields`: the `id` of the fields that are hidden in the UI. - `layout`: config that is specific to a particular layout type. - - `mediaField`: used by the `grid` layout. The `id` of the field to be used for rendering each card's media. - - `primaryField`: used by the `grid` layout. The `id` of the field to be used for rendering each card's title. + - `mediaField`: used by the `grid` and `list` layouts. The `id` of the field to be used for rendering each card's media. + - `primaryField`: used by the `grid` and `list` layouts. The `id` of the field to be highlighted in each card/list item. ### View <=> data diff --git a/packages/dataviews/package.json b/packages/dataviews/package.json index 40a09050b9432..3d15ea435ab4f 100644 --- a/packages/dataviews/package.json +++ b/packages/dataviews/package.json @@ -35,6 +35,7 @@ "@wordpress/element": "file:../element", "@wordpress/i18n": "file:../i18n", "@wordpress/icons": "file:../icons", + "@wordpress/keycodes": "file:../keycodes", "@wordpress/private-apis": "file:../private-apis", "classnames": "^2.3.1", "remove-accents": "^0.5.0" diff --git a/packages/dataviews/src/constants.js b/packages/dataviews/src/constants.js index c4d1d9242f08a..387050a1dca5b 100644 --- a/packages/dataviews/src/constants.js +++ b/packages/dataviews/src/constants.js @@ -1,8 +1,13 @@ /** * WordPress dependencies */ -import { __ } from '@wordpress/i18n'; -import { blockTable, category, drawerLeft } from '@wordpress/icons'; +import { __, isRTL } from '@wordpress/i18n'; +import { + blockTable, + category, + formatListBullets, + formatListBulletsRTL, +} from '@wordpress/icons'; /** * Internal dependencies @@ -29,26 +34,17 @@ export const VIEW_LAYOUTS = [ label: __( 'Table' ), component: ViewTable, icon: blockTable, - supports: { - preview: false, - }, }, { type: LAYOUT_GRID, label: __( 'Grid' ), component: ViewGrid, icon: category, - supports: { - preview: false, - }, }, { type: LAYOUT_LIST, label: __( 'List' ), component: ViewList, - icon: drawerLeft, - supports: { - preview: true, - }, + icon: isRTL() ? formatListBulletsRTL : formatListBullets, }, ]; diff --git a/packages/dataviews/src/dataviews.js b/packages/dataviews/src/dataviews.js index b75155e8fddf0..21d5b24aec345 100644 --- a/packages/dataviews/src/dataviews.js +++ b/packages/dataviews/src/dataviews.js @@ -5,7 +5,7 @@ import { __experimentalVStack as VStack, __experimentalHStack as HStack, } from '@wordpress/components'; -import { useMemo } from '@wordpress/element'; +import { useMemo, useState } from '@wordpress/element'; /** * Internal dependencies @@ -28,7 +28,15 @@ export default function DataViews( { isLoading = false, paginationInfo, supportedLayouts, + onSelectionChange, } ) { + const [ selection, setSelection ] = useState( [] ); + + const onSetSelection = ( items ) => { + setSelection( items.map( ( item ) => item.id ) ); + onSelectionChange( items ); + }; + const ViewComponent = VIEW_LAYOUTS.find( ( v ) => v.type === view.type ).component; @@ -72,6 +80,8 @@ export default function DataViews( { data={ data } getItemId={ getItemId } isLoading={ isLoading } + onSelectionChange={ onSetSelection } + selection={ selection } />
- { mediaField?.render( { item, view } ) } + { mediaField?.render( { item } ) }
- { primaryField?.render( { item, view } ) } + { primaryField?.render( { item } ) } { const renderedValue = field.render( { item, - view, } ); if ( ! renderedValue ) { return null; @@ -80,7 +79,7 @@ export default function ViewGrid( { data, fields, view, actions, getItemId } ) { { field.header }
- { field.render( { item, view } ) } + { field.render( { item } ) }
); diff --git a/packages/dataviews/src/view-list.js b/packages/dataviews/src/view-list.js index 7d781b0fb2ed6..cd9b651cabca5 100644 --- a/packages/dataviews/src/view-list.js +++ b/packages/dataviews/src/view-list.js @@ -1,9 +1,97 @@ /** - * Internal dependencies + * External dependencies */ -import ViewTable from './view-table'; +import classNames from 'classnames'; -export default function ViewList( props ) { - // To do: change to email-like preview list. - return ; +/** + * WordPress dependencies + */ +import { useAsyncList } from '@wordpress/compose'; +import { + __experimentalHStack as HStack, + __experimentalVStack as VStack, +} from '@wordpress/components'; +import { ENTER, SPACE } from '@wordpress/keycodes'; + +export default function ViewList( { + view, + fields, + data, + getItemId, + onSelectionChange, + selection, +} ) { + const shownData = useAsyncList( data, { step: 3 } ); + const mediaField = fields.find( + ( field ) => field.id === view.layout.mediaField + ); + const primaryField = fields.find( + ( field ) => field.id === view.layout.primaryField + ); + const visibleFields = fields.filter( + ( field ) => + ! view.hiddenFields.includes( field.id ) && + ! [ view.layout.primaryField, view.layout.mediaField ].includes( + field.id + ) + ); + + const onEnter = ( item ) => ( event ) => { + const { keyCode } = event; + if ( [ ENTER, SPACE ].includes( keyCode ) ) { + onSelectionChange( [ item ] ); + } + }; + + return ( +
    + { shownData.map( ( item, index ) => { + return ( +
  • +
    onSelectionChange( [ item ] ) } + > + +
    + { mediaField?.render( { item } ) || ( +
    + ) } +
    + + + { primaryField?.render( { item } ) } +
    + { visibleFields.map( ( field ) => { + return ( + + { field.render( { + item, + } ) } + + ); + } ) } +
    +
    +
    +
    +
    +
  • + ); + } ) } +
+ ); } diff --git a/packages/dataviews/src/view-table.js b/packages/dataviews/src/view-table.js index 8b6422b4be11a..5ce9456344f69 100644 --- a/packages/dataviews/src/view-table.js +++ b/packages/dataviews/src/view-table.js @@ -344,8 +344,7 @@ function ViewTable( { const columns = useMemo( () => { const _columns = fields.map( ( field ) => { const { render, getValue, ...column } = field; - column.cell = ( props ) => - render( { item: props.row.original, view } ); + column.cell = ( props ) => render( { item: props.row.original } ); if ( getValue ) { column.accessorFn = ( item ) => getValue( { item } ); } diff --git a/packages/edit-site/src/components/page-pages/index.js b/packages/edit-site/src/components/page-pages/index.js index bac881f2ceb21..fe6f50a4a9c07 100644 --- a/packages/edit-site/src/components/page-pages/index.js +++ b/packages/edit-site/src/components/page-pages/index.js @@ -12,7 +12,7 @@ import { useState, useMemo, useCallback, useEffect } from '@wordpress/element'; import { dateI18n, getDate, getSettings } from '@wordpress/date'; import { privateApis as routerPrivateApis } from '@wordpress/router'; import { useSelect, useDispatch } from '@wordpress/data'; -import { DataViews, VIEW_LAYOUTS } from '@wordpress/dataviews'; +import { DataViews } from '@wordpress/dataviews'; /** * Internal dependencies @@ -24,6 +24,7 @@ import { ENUMERATION_TYPE, LAYOUT_GRID, LAYOUT_TABLE, + LAYOUT_LIST, OPERATOR_IN, OPERATOR_NOT_IN, } from '../../utils/constants'; @@ -48,6 +49,10 @@ const defaultConfigPerViewType = { mediaField: 'featured-image', primaryField: 'title', }, + [ LAYOUT_LIST ]: { + primaryField: 'title', + mediaField: 'featured-image', + }, }; function useView( type ) { @@ -124,7 +129,10 @@ const DEFAULT_STATUSES = 'draft,future,pending,private,publish'; // All but 'tra export default function PagePages() { const postType = 'page'; const [ view, setView ] = useView( postType ); - const [ selection, setSelection ] = useState( [] ); + const [ pageId, setPageId ] = useState( null ); + + const onSelectionChange = ( items ) => + setPageId( items?.length === 1 ? items[ 0 ].id : null ); const queryArgs = useMemo( () => { const filters = {}; @@ -187,15 +195,15 @@ export default function PagePages() { id: 'featured-image', header: __( 'Featured Image' ), getValue: ( { item } ) => item.featured_media, - render: ( { item, view: currentView } ) => + render: ( { item } ) => !! item.featured_media ? ( ) : null, @@ -205,31 +213,29 @@ export default function PagePages() { header: __( 'Title' ), id: 'title', getValue: ( { item } ) => item.title?.rendered || item.slug, - render: ( { item, view: { type } } ) => { + render: ( { item } ) => { return ( - - { - if ( - VIEW_LAYOUTS.find( - ( v ) => v.type === type - )?.supports?.preview - ) { - event.preventDefault(); - setSelection( [ item.id ] ); - } - } } - > - { decodeEntities( + + { [ LAYOUT_TABLE, LAYOUT_GRID ].includes( + view.type + ) ? ( + + { decodeEntities( + item.title?.rendered || item.slug + ) || __( '(no title)' ) } + + ) : ( + decodeEntities( item.title?.rendered || item.slug - ) || __( '(no title)' ) } - + ) || __( '(no title)' ) + ) } ); @@ -274,7 +280,7 @@ export default function PagePages() { }, }, ], - [ authors ] + [ authors, view ] ); const permanentlyDeletePostAction = usePermanentlyDeletePostAction(); @@ -324,19 +330,18 @@ export default function PagePages() { isLoading={ isLoadingPages || isLoadingAuthors } view={ view } onChangeView={ onChangeView } + onSelectionChange={ onSelectionChange } /> - { VIEW_LAYOUTS.find( ( v ) => v.type === view.type )?.supports - ?.preview && ( + { view.type === LAYOUT_LIST && (
- { selection.length === 1 && ( + { pageId !== null ? ( - ) } - { selection.length !== 1 && ( + ) : (
{ + render: ( { item } ) => { return ( ); }, @@ -229,7 +229,7 @@ export default function DataviewsTemplates() { elements: authors, }, ], - [ authors ] + [ authors, view ] ); const { shownTemplates, paginationInfo } = useMemo( () => {