diff --git a/packages/edit-site/src/components/page-patterns/fields.js b/packages/edit-site/src/components/page-patterns/fields.js new file mode 100644 index 0000000000000..eab6dbca32833 --- /dev/null +++ b/packages/edit-site/src/components/page-patterns/fields.js @@ -0,0 +1,251 @@ +/** + * External dependencies + */ +import clsx from 'clsx'; + +/** + * WordPress dependencies + */ +import { + __experimentalHStack as HStack, + Button, + Tooltip, + Flex, +} from '@wordpress/components'; +import { __, _x } from '@wordpress/i18n'; +import { useState, useMemo, useId } from '@wordpress/element'; +import { + BlockPreview, + privateApis as blockEditorPrivateApis, +} from '@wordpress/block-editor'; +import { Icon, lockSmall } from '@wordpress/icons'; +import { parse } from '@wordpress/blocks'; +import { decodeEntities } from '@wordpress/html-entities'; + +/** + * Internal dependencies + */ +import { Async } from '../async'; +import { + PATTERN_TYPES, + TEMPLATE_PART_POST_TYPE, + PATTERN_SYNC_TYPES, + OPERATOR_IS, +} from '../../utils/constants'; +import { unlock } from '../../lock-unlock'; +import { useLink } from '../routes/link'; +import { useAddedBy } from '../page-templates/hooks'; +import { defaultGetTitle } from './search-items'; + +const { useGlobalStyle } = unlock( blockEditorPrivateApis ); + +function PreviewWrapper( { item, onClick, ariaDescribedBy, children } ) { + return ( + + ); +} + +function PreviewField( { item } ) { + const descriptionId = useId(); + const description = item.description || item?.excerpt?.raw; + const isUserPattern = item.type === PATTERN_TYPES.user; + const isTemplatePart = item.type === TEMPLATE_PART_POST_TYPE; + const [ backgroundColor ] = useGlobalStyle( 'color.background' ); + const { onClick } = useLink( { + postType: item.type, + postId: isUserPattern || isTemplatePart ? item.id : item.name, + canvas: 'edit', + } ); + const blocks = useMemo( () => { + return ( + item.blocks ?? + parse( item.content.raw, { + __unstableSkipMigrationLogs: true, + } ) + ); + }, [ item?.content?.raw, item.blocks ] ); + const isEmpty = ! blocks?.length; + + return ( +
+ + { isEmpty && isTemplatePart && __( 'Empty template part' ) } + { isEmpty && ! isTemplatePart && __( 'Empty pattern' ) } + { ! isEmpty && ( + + + + ) } + + { !! description && ( + + ) } +
+ ); +} + +export const previewField = { + label: __( 'Preview' ), + id: 'preview', + render: PreviewField, + enableSorting: false, +}; + +function TitleField( { item } ) { + const isUserPattern = item.type === PATTERN_TYPES.user; + const isTemplatePart = item.type === TEMPLATE_PART_POST_TYPE; + const { onClick } = useLink( { + postType: item.type, + postId: isUserPattern || isTemplatePart ? item.id : item.name, + canvas: 'edit', + } ); + const title = decodeEntities( defaultGetTitle( item ) ); + return ( + + + { item.type === PATTERN_TYPES.theme ? ( + title + ) : ( + + ) } + + { item.type === PATTERN_TYPES.theme && ( + + + + ) } + + ); +} + +export const titleField = { + label: __( 'Title' ), + id: 'title', + getValue: ( { item } ) => item.title?.raw || item.title, + render: TitleField, + enableHiding: false, +}; + +const SYNC_FILTERS = [ + { + value: PATTERN_SYNC_TYPES.full, + label: _x( 'Synced', 'pattern (singular)' ), + description: __( 'Patterns that are kept in sync across the site.' ), + }, + { + value: PATTERN_SYNC_TYPES.unsynced, + label: _x( 'Not synced', 'pattern (singular)' ), + description: __( + 'Patterns that can be changed freely without affecting the site.' + ), + }, +]; + +export const patternStatusField = { + label: __( 'Sync status' ), + id: 'sync-status', + render: ( { item } ) => { + const syncStatus = + 'wp_pattern_sync_status' in item + ? item.wp_pattern_sync_status || PATTERN_SYNC_TYPES.full + : PATTERN_SYNC_TYPES.unsynced; + // User patterns can have their sync statuses checked directly. + // Non-user patterns are all unsynced for the time being. + return ( + + { + SYNC_FILTERS.find( ( { value } ) => value === syncStatus ) + .label + } + + ); + }, + elements: SYNC_FILTERS, + filterBy: { + operators: [ OPERATOR_IS ], + isPrimary: true, + }, + enableSorting: false, +}; + +function AuthorField( { item } ) { + const [ isImageLoaded, setIsImageLoaded ] = useState( false ); + const { text, icon, imageUrl } = useAddedBy( item.type, item.id ); + + return ( + + { imageUrl && ( +
+ setIsImageLoaded( true ) } + alt="" + src={ imageUrl } + /> +
+ ) } + { ! imageUrl && ( +
+ +
+ ) } + { text } +
+ ); +} + +export const templatePartAuthorField = { + label: __( 'Author' ), + id: 'author', + getValue: ( { item } ) => item.author_text, + render: AuthorField, + filterBy: { + isPrimary: true, + }, +}; diff --git a/packages/edit-site/src/components/page-patterns/index.js b/packages/edit-site/src/components/page-patterns/index.js index a0f107e91601b..a8db73272c0ce 100644 --- a/packages/edit-site/src/components/page-patterns/index.js +++ b/packages/edit-site/src/components/page-patterns/index.js @@ -1,59 +1,39 @@ -/** - * External dependencies - */ -import clsx from 'clsx'; - /** * WordPress dependencies */ -import { - __experimentalHStack as HStack, - Button, - Tooltip, - Flex, -} from '@wordpress/components'; -import { __, _x } from '@wordpress/i18n'; +import { __ } from '@wordpress/i18n'; import { useState, useMemo, useId, useEffect } from '@wordpress/element'; -import { - BlockPreview, - privateApis as blockEditorPrivateApis, -} from '@wordpress/block-editor'; +import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; import { DataViews, filterSortAndPaginate } from '@wordpress/dataviews'; -import { Icon, lockSmall } from '@wordpress/icons'; import { usePrevious } from '@wordpress/compose'; import { useEntityRecords } from '@wordpress/core-data'; import { privateApis as editorPrivateApis } from '@wordpress/editor'; import { privateApis as routerPrivateApis } from '@wordpress/router'; -import { parse } from '@wordpress/blocks'; -import { decodeEntities } from '@wordpress/html-entities'; /** * Internal dependencies */ -import { Async } from '../async'; import Page from '../page'; import { LAYOUT_GRID, LAYOUT_TABLE, - LAYOUT_LIST, PATTERN_TYPES, TEMPLATE_PART_POST_TYPE, - PATTERN_SYNC_TYPES, PATTERN_DEFAULT_CATEGORY, - OPERATOR_IS, } from '../../utils/constants'; import usePatternSettings from './use-pattern-settings'; import { unlock } from '../../lock-unlock'; import usePatterns from './use-patterns'; import PatternsHeader from './header'; -import { useLink } from '../routes/link'; -import { useAddedBy } from '../page-templates/hooks'; import { useEditPostAction } from '../dataviews-actions'; -import { defaultGetTitle } from './search-items'; +import { + patternStatusField, + previewField, + titleField, + templatePartAuthorField, +} from './fields'; -const { ExperimentalBlockEditorProvider, useGlobalStyle } = unlock( - blockEditorPrivateApis -); +const { ExperimentalBlockEditorProvider } = unlock( blockEditorPrivateApis ); const { usePostActions } = unlock( editorPrivateApis ); const { useLocation } = unlock( routerPrivateApis ); @@ -90,164 +70,6 @@ const DEFAULT_VIEW = { filters: [], }; -const SYNC_FILTERS = [ - { - value: PATTERN_SYNC_TYPES.full, - label: _x( 'Synced', 'pattern (singular)' ), - description: __( 'Patterns that are kept in sync across the site.' ), - }, - { - value: PATTERN_SYNC_TYPES.unsynced, - label: _x( 'Not synced', 'pattern (singular)' ), - description: __( - 'Patterns that can be changed freely without affecting the site.' - ), - }, -]; - -function PreviewWrapper( { item, onClick, ariaDescribedBy, children } ) { - return ( - - ); -} - -function Preview( { item, viewType } ) { - const descriptionId = useId(); - const description = item.description || item?.excerpt?.raw; - const isUserPattern = item.type === PATTERN_TYPES.user; - const isTemplatePart = item.type === TEMPLATE_PART_POST_TYPE; - const [ backgroundColor ] = useGlobalStyle( 'color.background' ); - const { onClick } = useLink( { - postType: item.type, - postId: isUserPattern || isTemplatePart ? item.id : item.name, - canvas: 'edit', - } ); - const blocks = useMemo( () => { - return ( - item.blocks ?? - parse( item.content.raw, { - __unstableSkipMigrationLogs: true, - } ) - ); - }, [ item?.content?.raw, item.blocks ] ); - const isEmpty = ! blocks?.length; - - return ( -
- - { isEmpty && isTemplatePart && __( 'Empty template part' ) } - { isEmpty && ! isTemplatePart && __( 'Empty pattern' ) } - { ! isEmpty && ( - - - - ) } - - { !! description && ( - - ) } -
- ); -} - -function Author( { item, viewType } ) { - const [ isImageLoaded, setIsImageLoaded ] = useState( false ); - const { text, icon, imageUrl } = useAddedBy( item.type, item.id ); - const withIcon = viewType !== LAYOUT_LIST; - - return ( - - { withIcon && imageUrl && ( -
- setIsImageLoaded( true ) } - alt="" - src={ imageUrl } - /> -
- ) } - { withIcon && ! imageUrl && ( -
- -
- ) } - { text } -
- ); -} - -function Title( { item } ) { - const isUserPattern = item.type === PATTERN_TYPES.user; - const isTemplatePart = item.type === TEMPLATE_PART_POST_TYPE; - const { onClick } = useLink( { - postType: item.type, - postId: isUserPattern || isTemplatePart ? item.id : item.name, - canvas: 'edit', - } ); - const title = decodeEntities( defaultGetTitle( item ) ); - return ( - - - { item.type === PATTERN_TYPES.theme ? ( - title - ) : ( - - ) } - - { item.type === PATTERN_TYPES.theme && ( - - - - ) } - - ); -} - export default function DataviewsPatterns() { const { params: { postType, categoryId: categoryIdFromURL }, @@ -267,6 +89,7 @@ export default function DataviewsPatterns() { const { records } = useEntityRecords( 'postType', TEMPLATE_PART_POST_TYPE, { per_page: -1, } ); + const authors = useMemo( () => { if ( ! records ) { return EMPTY_ARRAY; @@ -282,72 +105,19 @@ export default function DataviewsPatterns() { }, [ records ] ); const fields = useMemo( () => { - const _fields = [ - { - label: __( 'Preview' ), - id: 'preview', - render: ( { item } ) => ( - - ), - enableSorting: false, - }, - { - label: __( 'Title' ), - id: 'title', - getValue: ( { item } ) => item.title?.raw || item.title, - render: ( { item } ) => , - enableHiding: false, - }, - ]; + const _fields = [ previewField, titleField ]; if ( type === PATTERN_TYPES.user ) { - _fields.push( { - label: __( 'Sync status' ), - id: 'sync-status', - render: ( { item } ) => { - const syncStatus = - 'wp_pattern_sync_status' in item - ? item.wp_pattern_sync_status || - PATTERN_SYNC_TYPES.full - : PATTERN_SYNC_TYPES.unsynced; - // User patterns can have their sync statuses checked directly. - // Non-user patterns are all unsynced for the time being. - return ( - <span - className={ `edit-site-patterns__field-sync-status-${ syncStatus }` } - > - { - SYNC_FILTERS.find( - ( { value } ) => value === syncStatus - ).label - } - </span> - ); - }, - elements: SYNC_FILTERS, - filterBy: { - operators: [ OPERATOR_IS ], - isPrimary: true, - }, - enableSorting: false, - } ); + _fields.push( patternStatusField ); } else if ( type === TEMPLATE_PART_POST_TYPE ) { _fields.push( { - label: __( 'Author' ), - id: 'author', - getValue: ( { item } ) => item.author_text, - render: ( { item } ) => { - return <Author viewType={ view.type } item={ item } />; - }, + ...templatePartAuthorField, elements: authors, - filterBy: { - isPrimary: true, - }, } ); } return _fields; - }, [ view.type, type, authors ] ); + }, [ type, authors ] ); // Reset the page number when the category changes. useEffect( () => { diff --git a/packages/edit-site/src/components/page-patterns/style.scss b/packages/edit-site/src/components/page-patterns/style.scss index 2014d3b94fdae..f457624d100c1 100644 --- a/packages/edit-site/src/components/page-patterns/style.scss +++ b/packages/edit-site/src/components/page-patterns/style.scss @@ -1,118 +1,114 @@ -.edit-site-patterns__section-header { - .screen-reader-shortcut:focus { - top: 0; - } +.edit-site-patterns__delete-modal { + width: $modal-width-small; } -.edit-site-patterns__pattern-title { - .is-link { - text-decoration: none; - color: $gray-200; +.page-patterns-preview-field { + display: flex; + flex-direction: column; + height: 100%; - &:hover, - &:focus { - color: $white; + .dataviews-view-table & { + width: 96px; + flex-grow: 0; + border-radius: 2px; + + .page-patterns-preview-field__button { + border-radius: 2px; } } - .edit-site-patterns__pattern-icon { + .page-patterns-preview-field__button { + box-shadow: none; + border: none; + padding: 0; + background-color: unset; + box-sizing: border-box; + cursor: pointer; + overflow: hidden; + height: 100%; border-radius: $grid-unit-05; - background: var(--wp-block-synced-color); - fill: $white; - } - .edit-site-patterns__pattern-lock-icon { - fill: currentcolor; + &:focus-visible { + box-shadow: 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color); + // Windows High Contrast mode will show this outline, but not the box-shadow. + outline: 2px solid transparent; + } + + &[aria-disabled="true"] { + cursor: default; + } } } -.edit-site-patterns__delete-modal { - width: $modal-width-small; +.edit-site-patterns__pattern-icon { + fill: var(--wp-block-synced-color); + flex-shrink: 0; } -.edit-site-page-patterns-dataviews { - .page-patterns-preview-field { - display: flex; - flex-direction: column; - height: 100%; - - &.is-viewtype-table { - width: 96px; - flex-grow: 0; - border-radius: 2px; +.edit-site-patterns__pattern-lock-icon { + min-width: min-content; +} - .page-patterns-preview-field__button { - border-radius: 2px; - } - } +.edit-site-patterns__section-header { + border-bottom: 1px solid #f0f0f0; + padding: $grid-unit-20 $grid-unit-60; + position: sticky; + top: 0; + z-index: 2; + flex-shrink: 0; + transition: padding ease-out 0.1s; + @include reduce-motion("transition"); + min-height: $grid-unit-50; + + .edit-site-patterns__title { + min-height: $grid-unit-50; - .page-patterns-preview-field__button { - box-shadow: none; - border: none; - padding: 0; - background-color: unset; - box-sizing: border-box; - cursor: pointer; - overflow: hidden; - height: 100%; - border-radius: $grid-unit-05; - - &:focus-visible { - box-shadow: 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color); - // Windows High Contrast mode will show this outline, but not the box-shadow. - outline: 2px solid transparent; - } - - &[aria-disabled="true"] { - cursor: default; - } + .components-heading { + flex-grow: 1; + flex-basis: 0; + white-space: nowrap; } } - .edit-site-patterns__pattern-icon { - fill: var(--wp-block-synced-color); - flex-shrink: 0; + .edit-site-patterns__sub-title { + margin-bottom: $grid-unit-10; } - .edit-site-patterns__pattern-lock-icon { - min-width: min-content; + .screen-reader-shortcut:focus { + top: 0; } +} - .edit-site-patterns__section-header { - border-bottom: 1px solid #f0f0f0; - padding: $grid-unit-20 $grid-unit-60; - position: sticky; - top: 0; - z-index: 2; - flex-shrink: 0; - transition: padding ease-out 0.1s; - @include reduce-motion("transition"); - min-height: $grid-unit-50; +.edit-site-patterns__pattern-title { + display: block; + width: 100%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + color: inherit; - .edit-site-patterns__title { - min-height: $grid-unit-50; + .is-link { + text-decoration: none; + color: $gray-200; - .components-heading { - flex-grow: 1; - flex-basis: 0; - white-space: nowrap; - } + &:hover, + &:focus { + color: $white; } + } - .edit-site-patterns__sub-title { - margin-bottom: $grid-unit-10; - } + .edit-site-patterns__pattern-icon { + border-radius: $grid-unit-05; + background: var(--wp-block-synced-color); + fill: $white; } - .edit-site-patterns__pattern-title { - display: block; - width: 100%; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - color: inherit; + .edit-site-patterns__pattern-lock-icon { + fill: currentcolor; } +} +.edit-site-page-patterns-dataviews { .dataviews-pagination { z-index: z-index(".edit-site-patterns__dataviews-list-pagination"); }