From b377e3f822436d3ac2976de7e6726cce289b6f7d Mon Sep 17 00:00:00 2001 From: ntsekouras Date: Mon, 16 Dec 2024 16:16:30 +0200 Subject: [PATCH 1/2] BlockPatternsList with DataViews for inserter --- package-lock.json | 1 + packages/block-editor/package.json | 1 + .../block-patterns-list.js | 170 +++++++++ .../components/block-patterns-list/fields.js | 328 ++++++++++++++++++ .../components/block-patterns-list/style.scss | 36 ++ packages/block-editor/src/components/index.js | 1 + .../pattern-category-previews.js | 71 ++-- .../src/components/inserter/menu.js | 2 +- packages/block-editor/src/style.scss | 1 + .../dataviews-view-config/index.tsx | 149 ++++---- .../src/dataviews-layouts/grid/index.tsx | 1 + .../src/filter-and-sort-data-view.ts | 5 + 12 files changed, 646 insertions(+), 120 deletions(-) create mode 100644 packages/block-editor/src/components/block-patterns-list/block-patterns-list.js create mode 100644 packages/block-editor/src/components/block-patterns-list/fields.js diff --git a/package-lock.json b/package-lock.json index 6471460ae535d9..519626087a75ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -50149,6 +50149,7 @@ "@wordpress/components": "*", "@wordpress/compose": "*", "@wordpress/data": "*", + "@wordpress/dataviews": "*", "@wordpress/date": "*", "@wordpress/deprecated": "*", "@wordpress/dom": "*", diff --git a/packages/block-editor/package.json b/packages/block-editor/package.json index c5e82b59245851..e5268d3a462917 100644 --- a/packages/block-editor/package.json +++ b/packages/block-editor/package.json @@ -46,6 +46,7 @@ "@wordpress/components": "*", "@wordpress/compose": "*", "@wordpress/data": "*", + "@wordpress/dataviews": "*", "@wordpress/date": "*", "@wordpress/deprecated": "*", "@wordpress/dom": "*", diff --git a/packages/block-editor/src/components/block-patterns-list/block-patterns-list.js b/packages/block-editor/src/components/block-patterns-list/block-patterns-list.js new file mode 100644 index 00000000000000..a61380624d33f5 --- /dev/null +++ b/packages/block-editor/src/components/block-patterns-list/block-patterns-list.js @@ -0,0 +1,170 @@ +/** + * WordPress dependencies + */ +import { DataViews, filterSortAndPaginate } from '@wordpress/dataviews'; +import { useState, forwardRef, useMemo } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { useEvent } from '@wordpress/compose'; + +/** + * Internal dependencies + */ +import { + myPatternsCategory, + INSERTER_PATTERN_TYPES, +} from '../inserter/block-patterns-tab/utils'; +import { + titleField, + previewField, + sourceField, + syncStatusField, +} from './fields'; + +const LAYOUT_GRID = 'grid'; +// export const INSERTER_PATTERN_TYPES = { +// user: 'user', +// theme: 'theme', +// directory: 'directory', +// }; + +const EMPTY_ARRAY = []; +const defaultLayouts = { + [ LAYOUT_GRID ]: { + layout: { + previewSize: 1, + }, + }, +}; +const DEFAULT_VIEW = { + type: LAYOUT_GRID, + search: '', + page: 1, + perPage: 20, + titleField: 'title', + mediaField: 'preview', + fields: [], + filters: [], + ...defaultLayouts[ LAYOUT_GRID ], +}; + +function filterBySource( pattern, sourceFilter ) { + const isDirectoryPattern = + pattern.source === 'core' || + pattern.source?.startsWith( 'pattern-directory' ); + // If the directory source is selected, filter out user created patterns + // and those bundled with the theme. + if ( sourceFilter === INSERTER_PATTERN_TYPES.directory ) { + return isDirectoryPattern; + } + const isUserPattern = pattern.name.startsWith( 'core/block' ); + // If theme source selected, filter out user created patterns and those from + // the core patterns directory. + if ( sourceFilter === INSERTER_PATTERN_TYPES.theme ) { + return ! ( isUserPattern || isDirectoryPattern ); + } + // If user source selected, filter out theme patterns. + if ( sourceFilter === INSERTER_PATTERN_TYPES.user ) { + return pattern.type === INSERTER_PATTERN_TYPES.user; + } + return false; +} + +function BlockPatternsList( + { + isDraggable, + blockPatterns, + onHover, + onClickPattern, + orientation, + label = __( 'Block patterns' ), + category, + showTitlesAsTooltip, + }, + ref +) { + const [ view, setView ] = useState( DEFAULT_VIEW ); + + const fields = useMemo( () => { + const _fields = [ titleField, previewField, syncStatusField ]; + if ( category !== myPatternsCategory.name ) { + _fields.push( sourceField ); + } + return _fields; + }, [ category ] ); + // const previousCategoryId = usePrevious( categoryId ); + // const previousPostType = usePrevious( postType ); + // const [ activeCompositeId, setActiveCompositeId ] = useState( undefined ); + // const [ activePattern, setActivePattern ] = useState( null ); // State to track active pattern + + // useEffect( () => { + // // Reset the active composite item whenever the available patterns change, + // // to make sure that Composite widget can receive focus correctly when its + // // composite items change. The first composite item will receive focus. + // const firstCompositeItemId = blockPatterns[ 0 ]?.name; + // setActiveCompositeId( firstCompositeItemId ); + // }, [ blockPatterns ] ); + + const handleClickPattern = useEvent( ( pattern, blocks ) => { + // setActivePattern( pattern.name ); + onClickPattern( pattern, blocks ); + } ); + + const { data, paginationInfo } = useMemo( () => { + // `source` field has custom filtering logic. + let _patterns = [ ...blockPatterns ]; + const sourceFilterIndex = view.filters?.findIndex( + ( { field, value } ) => field === 'source' && !! value + ); + const hasSourceFilter = sourceFilterIndex !== -1; + if ( hasSourceFilter ) { + _patterns = _patterns.filter( ( _pattern ) => + filterBySource( + _pattern, + view.filters[ sourceFilterIndex ].value + ) + ); + } + const _view = { ...view, filters: [ ...view.filters ] }; + if ( hasSourceFilter ) { + _view.filters.splice( sourceFilterIndex, 1 ); + } + return filterSortAndPaginate( _patterns, _view, fields ); + }, [ blockPatterns, view, fields ] ); + const augmentedData = useMemo( () => { + return data?.map( ( item ) => ( { + ...item, + isDraggable, + onHover, + onClick: handleClickPattern, + showTitlesAsTooltip, + selectedCategory: category, + } ) ); + }, [ + data, + isDraggable, + onHover, + handleClickPattern, + showTitlesAsTooltip, + category, + ] ); + return ( + item.name } + isLoading={ false } + // isItemClickable={ () => true } + // onClickItem={ ( item ) => { + // // handleClickPattern + // } } + view={ view } + onChangeView={ setView } + defaultLayouts={ defaultLayouts } + /> + ); +} + +export default forwardRef( BlockPatternsList ); diff --git a/packages/block-editor/src/components/block-patterns-list/fields.js b/packages/block-editor/src/components/block-patterns-list/fields.js new file mode 100644 index 00000000000000..fb35d20ca95821 --- /dev/null +++ b/packages/block-editor/src/components/block-patterns-list/fields.js @@ -0,0 +1,328 @@ +/** + * External dependencies + */ +import clsx from 'clsx'; + +/** + * WordPress dependencies + */ +import { cloneBlock, parse } from '@wordpress/blocks'; +import { useMemo, useId, useState } from '@wordpress/element'; +import { __, _x } from '@wordpress/i18n'; +import { decodeEntities } from '@wordpress/html-entities'; +import { + Composite, + VisuallyHidden, + Tooltip, + __experimentalHStack as HStack, +} from '@wordpress/components'; +import { useInstanceId } from '@wordpress/compose'; +import { Icon, symbol } from '@wordpress/icons'; + +/** + * Internal dependencies + */ +import { useGlobalStyle } from '../global-styles/hooks'; +import BlockPreview from '../block-preview'; +import InserterDraggableBlocks from '../inserter-draggable-blocks'; +import { INSERTER_PATTERN_TYPES } from '../inserter/block-patterns-tab/utils'; + +const PATTERN_SYNC_TYPES = { + full: 'fully', + unsynced: 'unsynced', +}; +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.' + ), + }, +]; + +const PATTERN_SOURCE_MENU_OPTIONS = [ + { + value: INSERTER_PATTERN_TYPES.directory, + label: __( 'Pattern Directory' ), + }, + { + value: INSERTER_PATTERN_TYPES.theme, + label: __( 'Theme & Plugins' ), + }, + { + value: INSERTER_PATTERN_TYPES.user, + label: __( 'User' ), + }, +]; + +function getPatternSyncStatus( { item } ) { + return item.type !== INSERTER_PATTERN_TYPES.user + ? PATTERN_SYNC_TYPES.unsynced + : item.syncStatus || PATTERN_SYNC_TYPES.full; +} + +function getItemTitle( item ) { + if ( typeof item.title === 'string' ) { + return decodeEntities( item.title ); + } + if ( item.title && 'rendered' in item.title ) { + return decodeEntities( item.title.rendered ); + } + if ( item.title && 'raw' in item.title ) { + return decodeEntities( item.title.raw ); + } + return ''; +} +const WithToolTip = ( { showTooltip, title, children } ) => { + if ( showTooltip ) { + return { children }; + } + return <>{ children }; +}; +function BlockPatternPlaceholder() { + return ( +
+ ); +} +function FieldWrapper( { item, isSelected, children } ) { + const [ isDragging, setIsDragging ] = useState( false ); + const { blocks, isDraggable, onHover, onClick, selectedCategory } = item; + const instanceId = useInstanceId( FieldWrapper ); + const descriptionId = `block-editor-block-patterns-list__item-description-${ instanceId }`; + + // When we have a selected category and the pattern is draggable, we need to update the + // pattern's categories in metadata to only contain the selected category, and pass this to + // InserterDraggableBlocks component. We do that because we use this information for pattern + // shuffling and it makes more sense to show only the ones from the initially selected category during insertion. + const patternBlocks = useMemo( () => { + if ( ! selectedCategory || ! isDraggable ) { + return blocks; + } + return ( blocks ?? [] ).map( ( block ) => { + const clonedBlock = cloneBlock( block ); + if ( + clonedBlock.attributes.metadata?.categories?.includes( + selectedCategory + ) + ) { + clonedBlock.attributes.metadata.categories = [ + selectedCategory, + ]; + } + return clonedBlock; + } ); + }, [ blocks, isDraggable, selectedCategory ] ); + + return ( + + { ( { draggable, onDragStart, onDragEnd } ) => ( +
{ + setIsDragging( true ); + if ( onDragStart ) { + onHover?.( null ); + onDragStart( event ); + } + } } + onDragEnd={ ( event ) => { + setIsDragging( false ); + if ( onDragEnd ) { + onDragEnd( event ); + } + } } + > +
{ + onClick( item, blocks ); + onHover?.( null ); + } } + onKeyDown={ ( event ) => { + if ( [ 'Enter', 'Space' ].includes( event.key ) ) { + onClick( item, blocks ); + onHover?.( null ); + } + } } + onMouseEnter={ () => { + if ( isDragging ) { + return; + } + onHover?.( item ); + } } + onMouseLeave={ () => onHover?.( null ) } + > + { children } +
+
+ ) } +
+ ); +} + +function PreviewField( { item } ) { + const descriptionId = useId(); + // TODO: check below description.. + const description = item.description || item?.excerpt?.raw; + const [ backgroundColor ] = useGlobalStyle( 'color.background' ); + + const blocks = useMemo( () => { + return ( + item.blocks ?? + parse( item.content, { + __unstableSkipMigrationLogs: true, + } ) + ); + }, [ item?.content, item.blocks ] ); + const isEmpty = ! item.blocks?.length; + + return ( + +
+
+ { isEmpty && __( 'Empty pattern' ) } + { ! isEmpty && ( + } + > + + + ) } +
+ { !! description && ( + + ) } +
+
+ ); +} + +function TitleField( { item } ) { + const isUserPattern = item.type === INSERTER_PATTERN_TYPES.user; + if ( item.showTitlesAsTooltip && ! isUserPattern ) { + return null; + } + return ( + + { isUserPattern && ! item.syncStatus && ( +
+ +
+ ) } +
+ { getItemTitle( item ) } +
+
+ ); +} + +export const previewField = { + label: __( 'Preview' ), + id: 'preview', + render: ( { item } ) => ( + + + + ), + enableSorting: false, + enableHiding: false, +}; +export const titleField = { + type: 'text', + id: 'title', + label: __( 'Title' ), + placeholder: __( 'No title' ), + getValue: ( { item } ) => getItemTitle( item ), + render: ( { item } ) => ( + + + + ), + enableHiding: false, + enableGlobalSearch: true, + enableSorting: false, +}; +export const syncStatusField = { + label: __( 'Sync status' ), + id: 'sync-status', + getValue: getPatternSyncStatus, + render: ( { item } ) => { + const syncStatus = getPatternSyncStatus( { item } ); + return ( + + { + SYNC_FILTERS.find( ( { value } ) => value === syncStatus ) + .label + } + + ); + }, + elements: SYNC_FILTERS, + filterBy: { + operators: [ 'is' ], + }, + enableSorting: false, +}; +export const sourceField = { + type: 'text', + id: 'source', + label: __( 'Source' ), + getValue: ( { item } ) => item.source, + render: ( { item } ) => ( + // Probably more custom logic here, like the filtering logic. + { item.source } + ), + elements: PATTERN_SOURCE_MENU_OPTIONS, + filterBy: { + operators: [ 'is' ], + }, + enableSorting: false, +}; diff --git a/packages/block-editor/src/components/block-patterns-list/style.scss b/packages/block-editor/src/components/block-patterns-list/style.scss index 8b1b0b54c9b1a0..b77315503ddc78 100644 --- a/packages/block-editor/src/components/block-patterns-list/style.scss +++ b/packages/block-editor/src/components/block-patterns-list/style.scss @@ -84,3 +84,39 @@ } } } + + + +.page-patterns-preview-field { + display: flex; + flex-direction: column; + height: 100%; + border-radius: $radius-medium; + + .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; + } + } +} + +.edit-site-patterns__pattern-icon { + fill: var(--wp-block-synced-color); + flex-shrink: 0; +} diff --git a/packages/block-editor/src/components/index.js b/packages/block-editor/src/components/index.js index cf9167e4781576..ff25a0da9903d9 100644 --- a/packages/block-editor/src/components/index.js +++ b/packages/block-editor/src/components/index.js @@ -161,6 +161,7 @@ export { DeprecatedExperimentalUseHasRecursion as __experimentalUseHasRecursion, } from './recursion-provider'; export { default as __experimentalBlockPatternsList } from './block-patterns-list'; +export { default as BlockPatternsListV2 } from './block-patterns-list/block-patterns-list'; export { default as __experimentalPublishDateTimePicker } from './publish-date-time-picker'; export { default as __experimentalInspectorPopoverHeader } from './inspector-popover-header'; export { default as BlockPopover } from './block-popover'; diff --git a/packages/block-editor/src/components/inserter/block-patterns-tab/pattern-category-previews.js b/packages/block-editor/src/components/inserter/block-patterns-tab/pattern-category-previews.js index f9af2b6f8c42d2..fe71deac5e0cc2 100644 --- a/packages/block-editor/src/components/inserter/block-patterns-tab/pattern-category-previews.js +++ b/packages/block-editor/src/components/inserter/block-patterns-tab/pattern-category-previews.js @@ -1,13 +1,7 @@ /** * WordPress dependencies */ -import { - useMemo, - useState, - useCallback, - useRef, - useEffect, -} from '@wordpress/element'; +import { useMemo, useState, useCallback, useRef } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { @@ -22,7 +16,7 @@ import { * Internal dependencies */ import usePatternsState from '../hooks/use-patterns-state'; -import BlockPatternsList from '../../block-patterns-list'; +import BlockPatternsListV2 from '../../block-patterns-list/block-patterns-list'; import usePatternsPaging from '../hooks/use-patterns-paging'; import { PatternsFilter } from './patterns-filter'; import { usePatternCategories } from './use-pattern-categories'; @@ -34,12 +28,9 @@ import { INSERTER_PATTERN_TYPES, } from './utils'; -const noop = () => {}; - export function PatternCategoryPreviews( { rootClientId, onInsert, - onHover = noop, category, showTitlesAsTooltip, } ) { @@ -110,30 +101,27 @@ export function PatternCategoryPreviews( { ] ); - const pagingProps = usePatternsPaging( - currentCategoryPatterns, - category, - scrollContainerRef - ); - const { changePage } = pagingProps; + // const pagingProps = usePatternsPaging( + // currentCategoryPatterns, + // category, + // scrollContainerRef + // ); + // const { changePage } = pagingProps; - // Hide block pattern preview on unmount. - useEffect( () => () => onHover( null ), [] ); - - const onSetPatternSyncFilter = useCallback( - ( value ) => { - setPatternSyncFilter( value ); - changePage( 1 ); - }, - [ setPatternSyncFilter, changePage ] - ); - const onSetPatternSourceFilter = useCallback( - ( value ) => { - setPatternSourceFilter( value ); - changePage( 1 ); - }, - [ setPatternSourceFilter, changePage ] - ); + // const onSetPatternSyncFilter = useCallback( + // ( value ) => { + // setPatternSyncFilter( value ); + // changePage( 1 ); + // }, + // [ setPatternSyncFilter, changePage ] + // ); + // const onSetPatternSourceFilter = useCallback( + // ( value ) => { + // setPatternSourceFilter( value ); + // changePage( 1 ); + // }, + // [ setPatternSourceFilter, changePage ] + // ); return ( <> @@ -152,14 +140,6 @@ export function PatternCategoryPreviews( { { category?.label } - { ! currentCategoryPatterns.length && ( { __( 'Drag and drop patterns into the canvas.' ) } - ) } diff --git a/packages/block-editor/src/components/inserter/menu.js b/packages/block-editor/src/components/inserter/menu.js index 019a37bffdde26..477449ecb83e5f 100644 --- a/packages/block-editor/src/components/inserter/menu.js +++ b/packages/block-editor/src/components/inserter/menu.js @@ -163,7 +163,7 @@ function InserterMenu( const showMediaPanel = selectedTab === 'media' && !! selectedMediaCategory; const inserterSearch = useMemo( () => { - if ( selectedTab === 'media' ) { + if ( [ 'media', 'patterns' ].includes( selectedTab ) ) { return null; } diff --git a/packages/block-editor/src/style.scss b/packages/block-editor/src/style.scss index 6b2ebf5cd841fd..ffd4d417d0bd51 100644 --- a/packages/block-editor/src/style.scss +++ b/packages/block-editor/src/style.scss @@ -1,3 +1,4 @@ +@import "../../dataviews/src/style.scss"; @import "./autocompleters/style.scss"; @import "./components/background-image-control/style.scss"; @import "./components/block-alignment-control/style.scss"; diff --git a/packages/dataviews/src/components/dataviews-view-config/index.tsx b/packages/dataviews/src/components/dataviews-view-config/index.tsx index 0b3512714e14a4..b4f88860551d7d 100644 --- a/packages/dataviews/src/components/dataviews-view-config/index.tsx +++ b/packages/dataviews/src/components/dataviews-view-config/index.tsx @@ -501,85 +501,92 @@ function FieldControl() { ) as Array< { field: NormalizedField< any >; isVisibleFlag: string } >; return ( - - - { ( visibleLockedFields.length > 0 || - !! visibleFields?.length ) && ( - - { visibleLockedFields.map( - ( { field, isVisibleFlag } ) => { - return ( - { - onChangeView( { - ...view, - [ isVisibleFlag ]: false, - } ); - } } - canMove={ false } - /> - ); - } - ) } - - { visibleFields.map( ( field, index ) => ( - - ) ) } - - ) } - - - { ( !! hiddenFields?.length || !! hiddenLockedFields.length ) && ( - - - { __( 'Hidden' ) } - - + + + + { ( visibleLockedFields.length > 0 || + !! visibleFields?.length ) && ( - { hiddenLockedFields.length > 0 && - hiddenLockedFields.map( - ( { field, isVisibleFlag } ) => { - return ( - { - onChangeView( { - ...view, - [ isVisibleFlag ]: true, - } ); - } } - canMove={ false } - /> - ); - } - ) } - { hiddenFields.map( ( field ) => ( + { visibleLockedFields.map( + ( { field, isVisibleFlag } ) => { + return ( + { + onChangeView( { + ...view, + [ isVisibleFlag ]: false, + } ); + } } + canMove={ false } + /> + ); + } + ) } + + { visibleFields.map( ( field, index ) => ( ) ) } - + ) } - ) } - + + { ( !! hiddenFields?.length || + !! hiddenLockedFields.length ) && ( + + + { __( 'Hidden' ) } + + + + { hiddenLockedFields.length > 0 && + hiddenLockedFields.map( + ( { field, isVisibleFlag } ) => { + return ( + { + onChangeView( { + ...view, + [ isVisibleFlag ]: + true, + } ); + } } + canMove={ false } + /> + ); + } + ) } + { hiddenFields.map( ( field ) => ( + + ) ) } + + + + ) } + + ); } @@ -665,9 +672,7 @@ function DataviewsViewConfigDropdown() { ) } - - - + ) } diff --git a/packages/dataviews/src/dataviews-layouts/grid/index.tsx b/packages/dataviews/src/dataviews-layouts/grid/index.tsx index e8f8a46002ebdf..7d5f62582408e3 100644 --- a/packages/dataviews/src/dataviews-layouts/grid/index.tsx +++ b/packages/dataviews/src/dataviews-layouts/grid/index.tsx @@ -267,6 +267,7 @@ export default function ViewGrid< Item >( { { regularFields: [], badgeFields: [] } ); const hasData = !! data?.length; + const hasBulkActions = useSomeItemHasAPossibleBulkAction( actions, data ); const updatedPreviewSize = useUpdatedPreviewSizeOnViewportChange(); const hasBulkActions = useSomeItemHasAPossibleBulkAction( actions, data ); const usedPreviewSize = updatedPreviewSize || view.layout?.previewSize; diff --git a/packages/dataviews/src/filter-and-sort-data-view.ts b/packages/dataviews/src/filter-and-sort-data-view.ts index da2e9915d515ba..e326cc675de188 100644 --- a/packages/dataviews/src/filter-and-sort-data-view.ts +++ b/packages/dataviews/src/filter-and-sort-data-view.ts @@ -121,6 +121,11 @@ export function filterSortAndPaginate< Item >( } ); } else if ( filter.operator === OPERATOR_IS ) { filteredData = filteredData.filter( ( item ) => { + // TODO: check if we should do this for every operator or + // we shouldn't do it at all. Probably the former.. + if ( filter.value === undefined ) { + return true; + } return filter.value === field.getValue( { item } ); } ); } else if ( filter.operator === OPERATOR_IS_NOT ) { From c9fdc5ac57d509de63135acda3ea10831c208fdf Mon Sep 17 00:00:00 2001 From: ntsekouras Date: Thu, 19 Dec 2024 15:52:03 +0200 Subject: [PATCH 2/2] rebase --- .../block-patterns-list.js | 27 +++----- .../components/block-patterns-list/fields.js | 30 +------- .../components/block-patterns-list/style.scss | 68 +++++++++++-------- .../pattern-category-previews.js | 2 +- packages/dataviews/README.md | 4 ++ .../src/components/dataviews/index.tsx | 8 ++- .../src/dataviews-layouts/grid/index.tsx | 1 - .../src/dataviews-layouts/grid/style.scss | 8 ++- 8 files changed, 68 insertions(+), 80 deletions(-) diff --git a/packages/block-editor/src/components/block-patterns-list/block-patterns-list.js b/packages/block-editor/src/components/block-patterns-list/block-patterns-list.js index a61380624d33f5..fe8bb85ab71e0d 100644 --- a/packages/block-editor/src/components/block-patterns-list/block-patterns-list.js +++ b/packages/block-editor/src/components/block-patterns-list/block-patterns-list.js @@ -2,7 +2,7 @@ * WordPress dependencies */ import { DataViews, filterSortAndPaginate } from '@wordpress/dataviews'; -import { useState, forwardRef, useMemo } from '@wordpress/element'; +import { useState, forwardRef, useMemo, useEffect } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { useEvent } from '@wordpress/compose'; @@ -21,17 +21,12 @@ import { } from './fields'; const LAYOUT_GRID = 'grid'; -// export const INSERTER_PATTERN_TYPES = { -// user: 'user', -// theme: 'theme', -// directory: 'directory', -// }; - const EMPTY_ARRAY = []; const defaultLayouts = { [ LAYOUT_GRID ]: { layout: { previewSize: 1, + badgeFields: [ 'sync-status' ], }, }, }; @@ -78,11 +73,14 @@ function BlockPatternsList( orientation, label = __( 'Block patterns' ), category, - showTitlesAsTooltip, + showTitle, }, ref ) { - const [ view, setView ] = useState( DEFAULT_VIEW ); + const [ view, setView ] = useState( { + ...DEFAULT_VIEW, + showTitle, + } ); const fields = useMemo( () => { const _fields = [ titleField, previewField, syncStatusField ]; @@ -136,17 +134,9 @@ function BlockPatternsList( isDraggable, onHover, onClick: handleClickPattern, - showTitlesAsTooltip, selectedCategory: category, } ) ); - }, [ - data, - isDraggable, - onHover, - handleClickPattern, - showTitlesAsTooltip, - category, - ] ); + }, [ data, isDraggable, onHover, handleClickPattern, category ] ); return ( ); } diff --git a/packages/block-editor/src/components/block-patterns-list/fields.js b/packages/block-editor/src/components/block-patterns-list/fields.js index fb35d20ca95821..22900d5d80787a 100644 --- a/packages/block-editor/src/components/block-patterns-list/fields.js +++ b/packages/block-editor/src/components/block-patterns-list/fields.js @@ -239,31 +239,6 @@ function PreviewField( { item } ) { ); } -function TitleField( { item } ) { - const isUserPattern = item.type === INSERTER_PATTERN_TYPES.user; - if ( item.showTitlesAsTooltip && ! isUserPattern ) { - return null; - } - return ( - - { isUserPattern && ! item.syncStatus && ( -
- -
- ) } -
- { getItemTitle( item ) } -
-
- ); -} - export const previewField = { label: __( 'Preview' ), id: 'preview', @@ -283,10 +258,11 @@ export const titleField = { getValue: ( { item } ) => getItemTitle( item ), render: ( { item } ) => ( - +
+ { getItemTitle( item ) } +
), - enableHiding: false, enableGlobalSearch: true, enableSorting: false, }; diff --git a/packages/block-editor/src/components/block-patterns-list/style.scss b/packages/block-editor/src/components/block-patterns-list/style.scss index b77315503ddc78..819e5f997924e4 100644 --- a/packages/block-editor/src/components/block-patterns-list/style.scss +++ b/packages/block-editor/src/components/block-patterns-list/style.scss @@ -85,38 +85,48 @@ } } +/** + * Below are the added style for v2 with DataViews. + * They should be polished and/or moved to a differrent file, depending on + * how we'll export this component.. + */ + + .block-editor-block-patterns-list-v2 { + .dataviews-view-grid { + gap: calc($grid-unit-05); + } - -.page-patterns-preview-field { - display: flex; - flex-direction: column; - height: 100%; - border-radius: $radius-medium; - - .page-patterns-preview-field__button { - box-shadow: none; - border: none; - padding: 0; - background-color: unset; - box-sizing: border-box; - cursor: pointer; - overflow: hidden; + .page-patterns-preview-field { + display: flex; + flex-direction: column; 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; - } + border-radius: $radius-medium; - &[aria-disabled="true"] { - cursor: default; + .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; + } } } -} -.edit-site-patterns__pattern-icon { - fill: var(--wp-block-synced-color); - flex-shrink: 0; -} + .edit-site-patterns__pattern-icon { + fill: var(--wp-block-synced-color); + flex-shrink: 0; + } + } diff --git a/packages/block-editor/src/components/inserter/block-patterns-tab/pattern-category-previews.js b/packages/block-editor/src/components/inserter/block-patterns-tab/pattern-category-previews.js index fe71deac5e0cc2..3668f66703767a 100644 --- a/packages/block-editor/src/components/inserter/block-patterns-tab/pattern-category-previews.js +++ b/packages/block-editor/src/components/inserter/block-patterns-tab/pattern-category-previews.js @@ -167,7 +167,7 @@ export function PatternCategoryPreviews( { orientation="vertical" category={ category.name } isDraggable - showTitlesAsTooltip={ showTitlesAsTooltip } + showTitle={ ! showTitlesAsTooltip } /> ) } diff --git a/packages/dataviews/README.md b/packages/dataviews/README.md index 4cce66a6ae6b26..7fc877ef829184 100644 --- a/packages/dataviews/README.md +++ b/packages/dataviews/README.md @@ -394,6 +394,10 @@ A callback function that is triggered when a user clicks on a media field or pri React component to be rendered next to the view config button. +#### `className`: `string` + +Optional class name to add in the main DataViews wrapper container. + ## `DataForm` diff --git a/packages/dataviews/src/components/dataviews/index.tsx b/packages/dataviews/src/components/dataviews/index.tsx index a0a89488136548..c193fe2a2aaa68 100644 --- a/packages/dataviews/src/components/dataviews/index.tsx +++ b/packages/dataviews/src/components/dataviews/index.tsx @@ -2,6 +2,7 @@ * External dependencies */ import type { ReactNode } from 'react'; +import clsx from 'clsx'; /** * WordPress dependencies @@ -49,6 +50,7 @@ type DataViewsProps< Item > = { isItemClickable?: ( item: Item ) => boolean; header?: ReactNode; getItemLevel?: ( item: Item ) => number; + className?: string; } & ( Item extends ItemWithId ? { getItemId?: ( item: Item ) => string } : { getItemId: ( item: Item ) => string } ); @@ -75,6 +77,7 @@ export default function DataViews< Item >( { onClickItem, isItemClickable = defaultIsItemClickable, header, + className, }: DataViewsProps< Item > ) { const [ containerWidth, setContainerWidth ] = useState( 0 ); const containerRef = useResizeObserver( @@ -133,7 +136,10 @@ export default function DataViews< Item >( { containerWidth, } } > -
+
( { { regularFields: [], badgeFields: [] } ); const hasData = !! data?.length; - const hasBulkActions = useSomeItemHasAPossibleBulkAction( actions, data ); const updatedPreviewSize = useUpdatedPreviewSizeOnViewportChange(); const hasBulkActions = useSomeItemHasAPossibleBulkAction( actions, data ); const usedPreviewSize = updatedPreviewSize || view.layout?.previewSize; diff --git a/packages/dataviews/src/dataviews-layouts/grid/style.scss b/packages/dataviews/src/dataviews-layouts/grid/style.scss index 333e6e9a4caf9f..d067a15f028d78 100644 --- a/packages/dataviews/src/dataviews-layouts/grid/style.scss +++ b/packages/dataviews/src/dataviews-layouts/grid/style.scss @@ -17,9 +17,11 @@ } .dataviews-view-grid__title-field { - min-height: $grid-unit-30; // Preserve layout when there is no ellipsis button - display: flex; - align-items: center; + &:not(:empty) { + min-height: $grid-unit-30; // Preserve layout when there is no ellipsis button + display: flex; + align-items: center; + } &--clickable { width: fit-content;