From 58a768402bf1d92573e8b464f5de0991c7be7484 Mon Sep 17 00:00:00 2001 From: ntsekouras Date: Fri, 22 Dec 2023 10:24:56 +0200 Subject: [PATCH] DataViews: Use in patterns page --- .../src/components/page-main/index.js | 7 +- .../page-patterns/dataviews-patterns.js | 341 ++++++++++++++++++ .../src/components/page-patterns/style.scss | 27 ++ 3 files changed, 374 insertions(+), 1 deletion(-) create mode 100644 packages/edit-site/src/components/page-patterns/dataviews-patterns.js diff --git a/packages/edit-site/src/components/page-main/index.js b/packages/edit-site/src/components/page-main/index.js index 10b5b99dc2fbf..4a4a235c1922b 100644 --- a/packages/edit-site/src/components/page-main/index.js +++ b/packages/edit-site/src/components/page-main/index.js @@ -7,6 +7,7 @@ import { privateApis as routerPrivateApis } from '@wordpress/router'; * Internal dependencies */ import PagePatterns from '../page-patterns'; +import DataviewsPatterns from '../page-patterns/dataviews-patterns'; import PageTemplateParts from '../page-template-parts'; import PageTemplates from '../page-templates'; import PagePages from '../page-pages'; @@ -24,7 +25,11 @@ export default function PageMain() { } else if ( path === '/wp_template_part/all' ) { return ; } else if ( path === '/patterns' ) { - return ; + return window?.__experimentalAdminViews ? ( + + ) : ( + + ); } else if ( window?.__experimentalAdminViews && path === '/pages' ) { return ; } diff --git a/packages/edit-site/src/components/page-patterns/dataviews-patterns.js b/packages/edit-site/src/components/page-patterns/dataviews-patterns.js new file mode 100644 index 0000000000000..88494dab9734c --- /dev/null +++ b/packages/edit-site/src/components/page-patterns/dataviews-patterns.js @@ -0,0 +1,341 @@ +/** + * WordPress dependencies + */ +import { + __experimentalHStack as HStack, + Button, + __experimentalHeading as Heading, + Tooltip, + Flex, +} from '@wordpress/components'; +import { getQueryArgs } from '@wordpress/url'; +import { __ } from '@wordpress/i18n'; +import { useState, useMemo, useCallback, useId } from '@wordpress/element'; +import { + BlockPreview, + privateApis as blockEditorPrivateApis, +} from '@wordpress/block-editor'; +import { DataViews } from '@wordpress/dataviews'; +import { + Icon, + header, + footer, + symbolFilled as uncategorized, + symbol, + lockSmall, +} from '@wordpress/icons'; + +/** + * Internal dependencies + */ +import Page from '../page'; +import { + LAYOUT_GRID, + PATTERN_TYPES, + TEMPLATE_PART_POST_TYPE, + PATTERN_SYNC_TYPES, + PATTERN_DEFAULT_CATEGORY, +} from '../../utils/constants'; +// import { duplicatePatternAction } from './dataviews-pattern-actions'; +import usePatternSettings from './use-pattern-settings'; +import { unlock } from '../../lock-unlock'; +import usePatterns from './use-patterns'; +import PatternsHeader from './header'; + +const { ExperimentalBlockEditorProvider, useGlobalStyle } = unlock( + blockEditorPrivateApis +); + +const templatePartIcons = { header, footer, uncategorized }; +const EMPTY_ARRAY = []; +const defaultConfigPerViewType = { + [ LAYOUT_GRID ]: { + mediaField: 'preview', + primaryField: 'title', + }, +}; +const DEFAULT_VIEW = { + type: LAYOUT_GRID, + search: '', + page: 1, + perPage: 20, + hiddenFields: [], + layout: { + ...defaultConfigPerViewType[ LAYOUT_GRID ], + }, + filters: [], +}; + +function Preview( { item, viewType } ) { + const descriptionId = useId(); + const isUserPattern = item.type === PATTERN_TYPES.user; + const isNonUserPattern = item.type === PATTERN_TYPES.theme; + const isTemplatePart = item.type === TEMPLATE_PART_POST_TYPE; + const isEmpty = ! item.blocks?.length; + // Only custom patterns or custom template parts can be renamed or deleted. + const isCustomPattern = + isUserPattern || ( isTemplatePart && item.isCustom ); + const ariaDescriptions = []; + if ( isCustomPattern ) { + // User patterns don't have descriptions, but can be edited and deleted, so include some help text. + ariaDescriptions.push( + __( 'Press Enter to edit, or Delete to delete the pattern.' ) + ); + } else if ( item.description ) { + ariaDescriptions.push( item.description ); + } + + if ( isNonUserPattern ) { + ariaDescriptions.push( + __( 'Theme & plugin patterns cannot be edited.' ) + ); + } + const [ backgroundColor ] = useGlobalStyle( 'color.background' ); + return ( + <> +
+ { isEmpty && isTemplatePart && __( 'Empty template part' ) } + { isEmpty && ! isTemplatePart && __( 'Empty pattern' ) } + { ! isEmpty && } +
+ { ariaDescriptions.map( ( ariaDescription, index ) => ( + + ) ) } + + ); +} + +function Title( { item, onClick, categoryId } ) { + const isUserPattern = item.type === PATTERN_TYPES.user; + const isNonUserPattern = item.type === PATTERN_TYPES.theme; + let itemIcon; + if ( ! isUserPattern && templatePartIcons[ categoryId ] ) { + itemIcon = templatePartIcons[ categoryId ]; + } else { + itemIcon = + item.syncStatus === PATTERN_SYNC_TYPES.full ? symbol : undefined; + } + return ( + + { itemIcon && ! isNonUserPattern && ( + + + + ) } + + { item.type === PATTERN_TYPES.theme ? ( + item.title + ) : ( + + + + ) } + { item.type === PATTERN_TYPES.theme && ( + + + + ) } + + + ); +} + +export default function DataviewsPatterns() { + const { categoryType, categoryId = PATTERN_DEFAULT_CATEGORY } = + getQueryArgs( window.location.href ); + const type = categoryType || PATTERN_TYPES.theme; + const [ view, setView ] = useState( DEFAULT_VIEW ); + const isUncategorizedThemePatterns = + type === PATTERN_TYPES.theme && categoryId === 'uncategorized'; + const { patterns, isResolving } = usePatterns( + type, + isUncategorizedThemePatterns ? '' : categoryId, + { + search: view.search, + // syncStatus: + // deferredSyncedFilter === 'all' + // ? undefined + // : deferredSyncedFilter, + } + ); + const fields = useMemo( + () => [ + { + header: __( 'Preview' ), + id: 'preview', + render: ( { item } ) => ( + + ), + minWidth: 120, + maxWidth: 120, + enableSorting: false, + enableHiding: false, + }, + { + header: __( 'Title' ), + id: 'title', + getValue: ( { item } ) => item.title, + render: ( { item } ) => ( + {} } + categoryId={ categoryId } + /> + ), + maxWidth: 400, + enableHiding: false, + }, + ], + [ view.type, categoryId ] + ); + + const { data, paginationInfo } = useMemo( () => { + if ( ! patterns ) { + return { + data: EMPTY_ARRAY, + paginationInfo: { totalItems: 0, totalPages: 0 }, + }; + } + let filteredData = [ ...patterns ]; + // Handle filters. + if ( view.filters.length > 0 ) { + // view.filters.forEach( ( filter ) => { + // if ( + // filter.field === 'author' && + // filter.operator === OPERATOR_IN && + // !! filter.value + // ) { + // filteredData = filteredData.filter( ( item ) => { + // return item.author_text === filter.value; + // } ); + // } else if ( + // filter.field === 'author' && + // filter.operator === OPERATOR_NOT_IN && + // !! filter.value + // ) { + // filteredData = filteredData.filter( ( item ) => { + // return item.author_text !== filter.value; + // } ); + // } + // } ); + } + + // Handle sorting. + if ( view.sort ) { + const stringSortingFields = [ 'title' ]; + const fieldId = view.sort.field; + if ( stringSortingFields.includes( fieldId ) ) { + const fieldToSort = fields.find( ( field ) => { + return field.id === fieldId; + } ); + filteredData.sort( ( a, b ) => { + const valueA = fieldToSort.getValue( { item: a } ) ?? ''; + const valueB = fieldToSort.getValue( { item: b } ) ?? ''; + return view.sort.direction === 'asc' + ? valueA.localeCompare( valueB ) + : valueB.localeCompare( valueA ); + } ); + } + } + + // Handle pagination. + const start = ( view.page - 1 ) * view.perPage; + const totalItems = filteredData?.length || 0; + filteredData = filteredData?.slice( start, start + view.perPage ); + return { + data: filteredData, + paginationInfo: { + totalItems, + totalPages: Math.ceil( totalItems / view.perPage ), + }, + }; + }, [ patterns, view, fields ] ); + + // const actions = useMemo( () => [ duplicatePatternAction ], [] ); + 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, setView ] + ); + const id = useId(); + const titleId = `${ id }-title`; + const descriptionId = `${ id }-description`; + const settings = usePatternSettings(); + // Wrap everything in a block editor provider. + // This ensures 'styles' that are needed for the previews are synced + // from the site editor store to the block editor store. + // TODO: check if I add the provider in every preview like in templates... + return ( + <ExperimentalBlockEditorProvider settings={ settings }> + <Page + title={ __( 'Patterns content' ) } + className="edit-site-page-patterns-dataviews" + hideTitleFromUI + > + <PatternsHeader + categoryId={ categoryId } + type={ type } + titleId={ titleId } + descriptionId={ descriptionId } + /> + <DataViews + paginationInfo={ paginationInfo } + fields={ fields } + // actions={ actions } + data={ data || EMPTY_ARRAY } + getItemId={ ( item ) => item.name } + isLoading={ isResolving } + view={ view } + onChangeView={ onChangeView } + deferredRendering={ true } + supportedLayouts={ [ LAYOUT_GRID ] } + /> + </Page> + </ExperimentalBlockEditorProvider> + ); +} diff --git a/packages/edit-site/src/components/page-patterns/style.scss b/packages/edit-site/src/components/page-patterns/style.scss index 8995a0d25c96c..dc055b94c4b49 100644 --- a/packages/edit-site/src/components/page-patterns/style.scss +++ b/packages/edit-site/src/components/page-patterns/style.scss @@ -223,3 +223,30 @@ } } } + +/** + * DataViews patterns styles + * TODO: when this becomes stable, consolidate styles with the above. + */ +.edit-site-page-patterns-dataviews { + .page-patterns-preview-field { + &.is-viewtype-grid { + .block-editor-block-preview__container { + height: auto; + } + } + } + + .edit-site-patterns__pattern-lock-icon { + min-width: min-content; + } + + .edit-site-patterns__section-header { + border-bottom: 1px solid #f0f0f0; + min-height: 72px; + padding: $grid-unit-20 $grid-unit-40; + position: sticky; + top: 0; + z-index: 2; + } +}