/** * External dependencies */ import { useCallback, useEffect, useMemo, useRef, useState, } from '@wordpress/element'; import { useAsyncList } from '@wordpress/compose'; import { useSelect, useDispatch, select } from '@wordpress/data'; import { BlockInstance, cloneBlock } from '@wordpress/blocks'; import { close } from '@wordpress/icons'; import { __ } from '@wordpress/i18n'; import { getNewPath, navigateTo } from '@woocommerce/navigation'; import { capitalize } from 'lodash'; import { Button, Spinner } from '@wordpress/components'; import { unlock, // @ts-expect-error No types for this exist yet. } from '@wordpress/edit-site/build-module/lock-unlock'; // @ts-expect-error No types for this exist yet. // eslint-disable-next-line @woocommerce/dependency-group import { useIsSiteEditorLoading } from '@wordpress/edit-site/build-module/components/layout/hooks'; // eslint-disable-next-line @woocommerce/dependency-group import { store as coreStore, // @ts-expect-error No types for this exist yet. } from '@wordpress/core-data'; // eslint-disable-next-line @woocommerce/dependency-group import { __experimentalBlockPatternsList as BlockPatternList, store as blockEditorStore, // @ts-expect-error No types for this exist yet. } from '@wordpress/block-editor'; /** * Internal dependencies */ import { usePatternsByCategory } from '../../hooks/use-patterns'; import './style.scss'; import { useEditorBlocks } from '../../hooks/use-editor-blocks'; import { PATTERN_CATEGORIES } from './categories'; import { THEME_SLUG } from '~/customize-store/data/constants'; import { findButtonBlockInsideCoverBlockWithBlackBackgroundPatternAndUpdate, PRODUCT_HERO_PATTERN_BUTTON_STYLE, } from '../../utils/black-background-pattern-update-button'; import { useIsActiveNewNeutralVariation } from '../../hooks/use-is-active-new-neutral-variation'; import { sortPatternsByCategory, addIsAddedClassToPatternPreview, } from './utils'; import { trackEvent } from '~/customize-store/tracking'; export const SidebarPatternScreen = ( { category }: { category: string } ) => { const { patterns, isLoading } = usePatternsByCategory( category ); const isActiveNewNeutralVariation = useIsActiveNewNeutralVariation(); const sortedPatterns = useMemo( () => { const patternsWithoutThemePatterns = patterns.filter( ( pattern ) => ! pattern.name.includes( THEME_SLUG ) && pattern.source !== 'pattern-directory/theme' && pattern.source !== 'pattern-directory/core' ); const patternWithPatchedProductHeroPattern = patternsWithoutThemePatterns.map( ( pattern ) => { if ( pattern.name !== 'woocommerce-blocks/just-arrived-full-hero' && pattern.name !== 'woocommerce-blocks/featured-category-cover-image' ) { return pattern; } if ( ! isActiveNewNeutralVariation ) { const blocks = findButtonBlockInsideCoverBlockWithBlackBackgroundPatternAndUpdate( pattern.blocks, ( patternBlocks: BlockInstance[] ) => { patternBlocks.forEach( ( block: BlockInstance ) => ( block.attributes.style = {} ) ); } ); return { ...pattern, blocks }; } const blocks = findButtonBlockInsideCoverBlockWithBlackBackgroundPatternAndUpdate( pattern.blocks, ( patternBlocks: BlockInstance[] ) => { patternBlocks.forEach( ( block ) => ( block.attributes.style = PRODUCT_HERO_PATTERN_BUTTON_STYLE ) ); } ); return { ...pattern, blocks }; } ); return sortPatternsByCategory( patternWithPatchedProductHeroPattern, category as keyof typeof PATTERN_CATEGORIES ); }, [ category, isActiveNewNeutralVariation, patterns ] ); const asyncSortedPatterns = useAsyncList( sortedPatterns ); const [ patternPagination, setPatternPagination ] = useState( 10 ); const refElement = useRef< HTMLDivElement >( null ); const currentTemplate = useSelect( ( sel ) => // @ts-expect-error No types for this exist yet. sel( coreStore ).__experimentalGetTemplateForLink( '/' ), [] ); const [ blocks ] = useEditorBlocks( 'wp_template', currentTemplate?.id ?? '' ); const blockToScroll = useRef< string | null >( null ); const isEditorLoading = useIsSiteEditorLoading(); const isSpinnerVisible = isLoading || isEditorLoading; useEffect( () => { if ( isSpinnerVisible || refElement.current === null ) { return; } // We want to add the is-added class to the pattern preview when the pattern is loaded in the editor and for each mutation. addIsAddedClassToPatternPreview( refElement.current, blocks ); const observer = new MutationObserver( () => { addIsAddedClassToPatternPreview( refElement.current as HTMLElement, blocks ); } ); const previewPatternList = document.querySelector( '.woocommerce-customize-store-edit-site-layout__sidebar-extra__pattern .block-editor-block-patterns-list' ); if ( previewPatternList ) { observer.observe( previewPatternList, { childList: true, } ); } return () => { observer.disconnect(); }; }, [ isLoading, blocks, isSpinnerVisible ] ); useEffect( () => { if ( isEditorLoading ) { return; } const iframe = window.document.querySelector( '.woocommerce-customize-store-assembler > iframe[name="editor-canvas"]' ) as HTMLIFrameElement; const blockList = iframe?.contentWindow?.document.body.querySelector( '.block-editor-block-list__layout' ); const observer = new MutationObserver( () => { if ( blockToScroll.current ) { const block = blockList?.querySelector( `[id="block-${ blockToScroll.current }"]` ); if ( block ) { block.scrollIntoView(); blockToScroll.current = null; } } } ); if ( blockList ) { observer.observe( blockList, { childList: true } ); } return () => { observer.disconnect(); }; }, [ isEditorLoading ] ); // @ts-expect-error No types for this exist yet. const { insertBlocks } = useDispatch( blockEditorStore ); const insertableIndex = useMemo( () => { return blocks.findLastIndex( ( block ) => block.name === 'core/template-part' ); }, [ blocks ] ); const onClickPattern = useCallback( ( pattern ) => { const parsedPattern = unlock( select( blockEditorStore ) ).__experimentalGetParsedPattern( pattern.name ); const cloneBlocks = parsedPattern.blocks.map( ( blockInstance: BlockInstance ) => cloneBlock( blockInstance ) ); if ( ! isActiveNewNeutralVariation ) { const updatedBlocks = findButtonBlockInsideCoverBlockWithBlackBackgroundPatternAndUpdate( cloneBlocks, ( patternBlocks: BlockInstance[] ) => { patternBlocks.forEach( ( block: BlockInstance ) => ( block.attributes.style = {} ) ); } ); insertBlocks( updatedBlocks, insertableIndex, undefined, false ); blockToScroll.current = updatedBlocks[ 0 ].clientId; } else { const updatedBlocks = findButtonBlockInsideCoverBlockWithBlackBackgroundPatternAndUpdate( cloneBlocks, ( patternBlocks: BlockInstance[] ) => { patternBlocks.forEach( ( block ) => ( block.attributes.style = PRODUCT_HERO_PATTERN_BUTTON_STYLE ) ); } ); insertBlocks( updatedBlocks, insertableIndex, undefined, false ); blockToScroll.current = updatedBlocks[ 0 ].clientId; } trackEvent( 'customize_your_store_assembler_pattern_sidebar_click', { pattern: pattern.name } ); }, [ insertBlocks, insertableIndex, isActiveNewNeutralVariation ] ); return (
{ const element = event.target as HTMLElement; const scrollTop = element.scrollTop; const percentage = scrollTop / ( element.scrollHeight - element.clientHeight ); if ( percentage > 0.5 ) { setPatternPagination( ( prev ) => prev + 10 ); } } } >

{ capitalize( PATTERN_CATEGORIES[ category as keyof typeof PATTERN_CATEGORIES ].label ) }

{ PATTERN_CATEGORIES[ category as keyof typeof PATTERN_CATEGORIES ].description }
{ isSpinnerVisible && ( ) } { ! isSpinnerVisible && ( ) }
); };