Skip to content

Commit

Permalink
DataViews: Optimize the templates dataviews by extracting the fields …
Browse files Browse the repository at this point in the history
…definition (WordPress#63929)

Co-authored-by: youknowriad <[email protected]>
Co-authored-by: ellatrix <[email protected]>
Co-authored-by: jameskoster <[email protected]>
  • Loading branch information
4 people authored Jul 25, 2024
1 parent e434ec4 commit a5cb976
Show file tree
Hide file tree
Showing 3 changed files with 172 additions and 164 deletions.
157 changes: 157 additions & 0 deletions packages/edit-site/src/components/page-templates/fields.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/**
* External dependencies
*/
import clsx from 'clsx';

/**
* WordPress dependencies
*/
import { Icon, __experimentalHStack as HStack } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { useState, useMemo } from '@wordpress/element';
import { decodeEntities } from '@wordpress/html-entities';
import { parse } from '@wordpress/blocks';
import {
BlockPreview,
privateApis as blockEditorPrivateApis,
} from '@wordpress/block-editor';
import { EditorProvider } from '@wordpress/editor';

/**
* Internal dependencies
*/
import { Async } from '../async';
import { default as Link, useLink } from '../routes/link';
import { useAddedBy } from './hooks';

import usePatternSettings from '../page-patterns/use-pattern-settings';
import { unlock } from '../../lock-unlock';

const { useGlobalStyle } = unlock( blockEditorPrivateApis );

function PreviewField( { item } ) {
const settings = usePatternSettings();
const [ backgroundColor = 'white' ] = useGlobalStyle( 'color.background' );
const blocks = useMemo( () => {
return parse( item.content.raw );
}, [ item.content.raw ] );
const { onClick } = useLink( {
postId: item.id,
postType: item.type,
canvas: 'edit',
} );

const isEmpty = ! blocks?.length;
// Wrap everything in a block editor provider to ensure 'styles' that are needed
// for the previews are synced between the site editor store and the block editor store.
// Additionally we need to have the `__experimentalBlockPatterns` setting in order to
// render patterns inside the previews.
// TODO: Same approach is used in the patterns list and it becomes obvious that some of
// the block editor settings are needed in context where we don't have the block editor.
// Explore how we can solve this in a better way.
return (
<EditorProvider post={ item } settings={ settings }>
<div
className="page-templates-preview-field"
style={ { backgroundColor } }
>
<button
className="page-templates-preview-field__button"
type="button"
onClick={ onClick }
aria-label={ item.title?.rendered || item.title }
>
{ isEmpty && __( 'Empty template' ) }
{ ! isEmpty && (
<Async>
<BlockPreview blocks={ blocks } />
</Async>
) }
</button>
</div>
</EditorProvider>
);
}

export const previewField = {
label: __( 'Preview' ),
id: 'preview',
render: PreviewField,
enableSorting: false,
};

function TitleField( { item } ) {
const linkProps = {
params: {
postId: item.id,
postType: item.type,
canvas: 'edit',
},
};
return (
<Link { ...linkProps }>
{ decodeEntities( item.title?.rendered ) || __( '(no title)' ) }
</Link>
);
}

export const titleField = {
label: __( 'Template' ),
id: 'title',
getValue: ( { item } ) => item.title?.rendered,
render: TitleField,
enableHiding: false,
enableGlobalSearch: true,
};

export const descriptionField = {
label: __( 'Description' ),
id: 'description',
render: ( { item } ) => {
return (
item.description && (
<span className="page-templates-description">
{ decodeEntities( item.description ) }
</span>
)
);
},
enableSorting: false,
enableGlobalSearch: true,
};

function AuthorField( { item } ) {
const [ isImageLoaded, setIsImageLoaded ] = useState( false );
const { text, icon, imageUrl } = useAddedBy( item.type, item.id );

return (
<HStack alignment="left" spacing={ 0 }>
{ imageUrl && (
<div
className={ clsx( 'page-templates-author-field__avatar', {
'is-loaded': isImageLoaded,
} ) }
>
<img
onLoad={ () => setIsImageLoaded( true ) }
alt=""
src={ imageUrl }
/>
</div>
) }
{ ! imageUrl && (
<div className="page-templates-author-field__icon">
<Icon icon={ icon } />
</div>
) }
<span className="page-templates-author-field__name">{ text }</span>
</HStack>
);
}

export const authorField = {
label: __( 'Author' ),
id: 'author',
getValue: ( { item } ) => item.author_text,
render: AuthorField,
};
173 changes: 12 additions & 161 deletions packages/edit-site/src/components/page-templates/index.js
Original file line number Diff line number Diff line change
@@ -1,51 +1,35 @@
/**
* External dependencies
*/
import clsx from 'clsx';

/**
* WordPress dependencies
*/
import { Icon, __experimentalHStack as HStack } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { useState, useMemo, useCallback, useEffect } from '@wordpress/element';
import { useEntityRecords } from '@wordpress/core-data';
import { decodeEntities } from '@wordpress/html-entities';
import { parse } from '@wordpress/blocks';
import {
BlockPreview,
privateApis as blockEditorPrivateApis,
} from '@wordpress/block-editor';
import { DataViews, filterSortAndPaginate } from '@wordpress/dataviews';
import { privateApis as routerPrivateApis } from '@wordpress/router';
import {
privateApis as editorPrivateApis,
EditorProvider,
} from '@wordpress/editor';
import { privateApis as editorPrivateApis } from '@wordpress/editor';

/**
* Internal dependencies
*/
import { Async } from '../async';
import Page from '../page';
import { default as Link, useLink } from '../routes/link';
import AddNewTemplate from '../add-new-template';
import { useAddedBy } from './hooks';
import {
TEMPLATE_POST_TYPE,
OPERATOR_IS_ANY,
LAYOUT_GRID,
LAYOUT_TABLE,
LAYOUT_LIST,
} from '../../utils/constants';

import usePatternSettings from '../page-patterns/use-pattern-settings';
import { unlock } from '../../lock-unlock';
import { useEditPostAction } from '../dataviews-actions';
import {
authorField,
descriptionField,
previewField,
titleField,
} from './fields';

const { usePostActions } = unlock( editorPrivateApis );

const { useGlobalStyle } = unlock( blockEditorPrivateApis );
const { useHistory, useLocation } = unlock( routerPrivateApis );

const EMPTY_ARRAY = [];
Expand Down Expand Up @@ -109,104 +93,6 @@ const DEFAULT_VIEW = {
filters: [],
};

function Title( { item, viewType } ) {
if ( viewType === LAYOUT_LIST ) {
return decodeEntities( item.title?.rendered ) || __( '(no title)' );
}
const linkProps = {
params: {
postId: item.id,
postType: item.type,
canvas: 'edit',
},
};
return (
<Link { ...linkProps }>
{ decodeEntities( item.title?.rendered ) || __( '(no title)' ) }
</Link>
);
}

function AuthorField( { item } ) {
const [ isImageLoaded, setIsImageLoaded ] = useState( false );
const { text, icon, imageUrl } = useAddedBy( item.type, item.id );

return (
<HStack alignment="left" spacing={ 0 }>
{ imageUrl && (
<div
className={ clsx( 'page-templates-author-field__avatar', {
'is-loaded': isImageLoaded,
} ) }
>
<img
onLoad={ () => setIsImageLoaded( true ) }
alt=""
src={ imageUrl }
/>
</div>
) }
{ ! imageUrl && (
<div className="page-templates-author-field__icon">
<Icon icon={ icon } />
</div>
) }
<span className="page-templates-author-field__name">{ text }</span>
</HStack>
);
}

function Preview( { item, viewType } ) {
const settings = usePatternSettings();
const [ backgroundColor = 'white' ] = useGlobalStyle( 'color.background' );
const blocks = useMemo( () => {
return parse( item.content.raw );
}, [ item.content.raw ] );
const { onClick } = useLink( {
postId: item.id,
postType: item.type,
canvas: 'edit',
} );

const isEmpty = ! blocks?.length;
// Wrap everything in a block editor provider to ensure 'styles' that are needed
// for the previews are synced between the site editor store and the block editor store.
// Additionally we need to have the `__experimentalBlockPatterns` setting in order to
// render patterns inside the previews.
// TODO: Same approach is used in the patterns list and it becomes obvious that some of
// the block editor settings are needed in context where we don't have the block editor.
// Explore how we can solve this in a better way.
return (
<EditorProvider post={ item } settings={ settings }>
<div
className={ `page-templates-preview-field is-viewtype-${ viewType }` }
style={ { backgroundColor } }
>
{ viewType === LAYOUT_LIST && ! isEmpty && (
<Async>
<BlockPreview blocks={ blocks } />
</Async>
) }
{ viewType !== LAYOUT_LIST && (
<button
className="page-templates-preview-field__button"
type="button"
onClick={ onClick }
aria-label={ item.title?.rendered || item.title }
>
{ isEmpty && __( 'Empty template' ) }
{ ! isEmpty && (
<Async>
<BlockPreview blocks={ blocks } />
</Async>
) }
</button>
) }
</div>
</EditorProvider>
);
}

export default function PageTemplates() {
const { params } = useLocation();
const { activeView = 'all', layout, postId } = params;
Expand Down Expand Up @@ -285,50 +171,15 @@ export default function PageTemplates() {

const fields = useMemo(
() => [
previewField,
titleField,
descriptionField,
{
label: __( 'Preview' ),
id: 'preview',
render: ( { item } ) => {
return <Preview item={ item } viewType={ view.type } />;
},
enableSorting: false,
},
{
label: __( 'Template' ),
id: 'title',
getValue: ( { item } ) => item.title?.rendered,
render: ( { item } ) => (
<Title item={ item } viewType={ view.type } />
),
enableHiding: false,
enableGlobalSearch: true,
},
{
label: __( 'Description' ),
id: 'description',
render: ( { item } ) => {
return (
item.description && (
<span className="page-templates-description">
{ decodeEntities( item.description ) }
</span>
)
);
},
enableSorting: false,
enableGlobalSearch: true,
},
{
label: __( 'Author' ),
id: 'author',
getValue: ( { item } ) => item.author_text,
render: ( { item } ) => {
return <AuthorField viewType={ view.type } item={ item } />;
},
...authorField,
elements: authors,
},
],
[ authors, view.type ]
[ authors ]
);

const { data, paginationInfo } = useMemo( () => {
Expand Down
Loading

0 comments on commit a5cb976

Please sign in to comment.