2024-06-18 12:16:16 +00:00
|
|
|
/**
|
|
|
|
* External dependencies
|
|
|
|
*/
|
2024-07-01 09:21:06 +00:00
|
|
|
import {
|
|
|
|
useCallback,
|
|
|
|
useEffect,
|
|
|
|
useMemo,
|
|
|
|
useRef,
|
|
|
|
useState,
|
|
|
|
} from '@wordpress/element';
|
2024-06-18 12:16:16 +00:00
|
|
|
import { useSelect, useDispatch, select } from '@wordpress/data';
|
2024-06-21 13:22:18 +00:00
|
|
|
import { BlockInstance, cloneBlock } from '@wordpress/blocks';
|
2024-06-18 12:16:16 +00:00
|
|
|
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';
|
2024-07-01 09:21:06 +00:00
|
|
|
// @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';
|
2024-06-18 12:16:16 +00:00
|
|
|
// 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';
|
2024-07-02 14:39:49 +00:00
|
|
|
import { THEME_SLUG } from '~/customize-store/data/constants';
|
|
|
|
import { Pattern } from '~/customize-store/types/pattern';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sorts patterns by category. For 'intro' and 'about' categories
|
|
|
|
* priorizied DotCom Patterns. For intro category, it also prioritizes the "centered-content-with-image-below" pattern.
|
|
|
|
* For other categories, it simply sorts patterns to prioritize Woo Patterns.
|
|
|
|
*/
|
|
|
|
const sortPatternsByCategory = (
|
|
|
|
patterns: Pattern[],
|
|
|
|
category: keyof typeof PATTERN_CATEGORIES
|
|
|
|
) => {
|
|
|
|
const prefix = 'woocommerce-blocks';
|
|
|
|
if ( category === 'intro' || category === 'about' ) {
|
|
|
|
return patterns.sort( ( a, b ) => {
|
|
|
|
if (
|
|
|
|
a.name ===
|
|
|
|
'woocommerce-blocks/centered-content-with-image-below'
|
|
|
|
) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (
|
|
|
|
b.name ===
|
|
|
|
'woocommerce-blocks/centered-content-with-image-below'
|
|
|
|
) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( a.name.includes( prefix ) && ! b.name.includes( prefix ) ) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if ( ! a.name.includes( prefix ) && b.name.includes( prefix ) ) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
|
|
|
|
return patterns.sort( ( a, b ) => {
|
|
|
|
if ( a.name.includes( prefix ) && ! b.name.includes( prefix ) ) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if ( ! a.name.includes( prefix ) && b.name.includes( prefix ) ) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
} );
|
|
|
|
};
|
2024-06-18 12:16:16 +00:00
|
|
|
|
|
|
|
export const SidebarPatternScreen = ( { category }: { category: string } ) => {
|
|
|
|
const { patterns, isLoading } = usePatternsByCategory( category );
|
|
|
|
|
2024-07-02 14:39:49 +00:00
|
|
|
const sortedPatterns = useMemo( () => {
|
|
|
|
const patternsWithoutThemePatterns = patterns.filter(
|
|
|
|
( pattern ) =>
|
|
|
|
! pattern.name.includes( THEME_SLUG ) &&
|
|
|
|
pattern.source !== 'pattern-directory/theme' &&
|
|
|
|
pattern.source !== 'pattern-directory/core'
|
|
|
|
);
|
|
|
|
|
|
|
|
return sortPatternsByCategory(
|
|
|
|
patternsWithoutThemePatterns,
|
|
|
|
category as keyof typeof PATTERN_CATEGORIES
|
|
|
|
);
|
|
|
|
}, [ category, patterns ] );
|
|
|
|
|
2024-06-18 12:16:16 +00:00
|
|
|
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 ?? ''
|
|
|
|
);
|
|
|
|
|
2024-07-01 09:21:06 +00:00
|
|
|
const blockToScroll = useRef< string | null >( null );
|
|
|
|
|
|
|
|
const isEditorLoading = useIsSiteEditorLoading();
|
|
|
|
|
|
|
|
const isSpinnerVisible = isLoading || isEditorLoading;
|
|
|
|
|
|
|
|
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 ] );
|
|
|
|
|
2024-06-18 12:16:16 +00:00
|
|
|
// @ts-expect-error No types for this exist yet.
|
2024-07-01 09:21:06 +00:00
|
|
|
const { insertBlocks } = useDispatch( blockEditorStore );
|
2024-06-18 12:16:16 +00:00
|
|
|
|
|
|
|
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 );
|
|
|
|
|
2024-06-21 13:22:18 +00:00
|
|
|
const cloneBlocks = parsedPattern.blocks.map(
|
|
|
|
( blockInstance: BlockInstance ) => cloneBlock( blockInstance )
|
|
|
|
);
|
2024-06-18 12:16:16 +00:00
|
|
|
|
2024-07-01 09:21:06 +00:00
|
|
|
insertBlocks( cloneBlocks, insertableIndex, undefined, false );
|
2024-06-18 12:16:16 +00:00
|
|
|
|
2024-07-01 09:21:06 +00:00
|
|
|
blockToScroll.current = cloneBlocks[ 0 ].clientId;
|
2024-06-18 12:16:16 +00:00
|
|
|
},
|
2024-07-01 09:21:06 +00:00
|
|
|
[ insertBlocks, insertableIndex ]
|
2024-06-18 12:16:16 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div
|
|
|
|
className="woocommerce-customize-store-edit-site-layout__sidebar-extra__pattern"
|
|
|
|
onScroll={ ( event ) => {
|
|
|
|
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 );
|
|
|
|
}
|
|
|
|
} }
|
|
|
|
>
|
|
|
|
<div className="woocommerce-customize-store-edit-site-layout__sidebar-extra__pattern__header">
|
|
|
|
<h1>
|
|
|
|
{ capitalize(
|
|
|
|
PATTERN_CATEGORIES[
|
|
|
|
category as keyof typeof PATTERN_CATEGORIES
|
|
|
|
].label
|
|
|
|
) }
|
|
|
|
</h1>
|
|
|
|
<Button
|
|
|
|
onClick={ () => {
|
|
|
|
const homepageUrl = getNewPath(
|
|
|
|
{ customizing: true },
|
|
|
|
`/customize-store/assembler-hub/homepage`,
|
|
|
|
{}
|
|
|
|
);
|
|
|
|
|
|
|
|
navigateTo( { url: homepageUrl } );
|
|
|
|
} }
|
2024-07-02 08:03:56 +00:00
|
|
|
iconSize={ 18 }
|
2024-06-18 12:16:16 +00:00
|
|
|
icon={ close }
|
|
|
|
label={ __( 'Close', 'woocommerce' ) }
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
<div className="woocommerce-customize-store-edit-site-layout__sidebar-extra__pattern__description">
|
|
|
|
<span>
|
|
|
|
{
|
|
|
|
PATTERN_CATEGORIES[
|
|
|
|
category as keyof typeof PATTERN_CATEGORIES
|
|
|
|
].description
|
|
|
|
}
|
|
|
|
</span>
|
|
|
|
</div>
|
2024-07-01 09:21:06 +00:00
|
|
|
{ isSpinnerVisible && (
|
2024-06-18 12:16:16 +00:00
|
|
|
<span className="components-placeholder__preview">
|
|
|
|
<Spinner />
|
|
|
|
</span>
|
|
|
|
) }
|
2024-07-01 09:21:06 +00:00
|
|
|
{ ! isSpinnerVisible && (
|
2024-06-18 12:16:16 +00:00
|
|
|
<BlockPatternList
|
2024-07-02 14:39:49 +00:00
|
|
|
shownPatterns={ sortedPatterns.slice(
|
|
|
|
0,
|
|
|
|
patternPagination
|
|
|
|
) }
|
|
|
|
blockPatterns={ sortedPatterns.slice(
|
|
|
|
0,
|
|
|
|
patternPagination
|
|
|
|
) }
|
2024-06-18 12:16:16 +00:00
|
|
|
onClickPattern={ onClickPattern }
|
|
|
|
label={ 'Homepage' }
|
|
|
|
orientation="vertical"
|
|
|
|
category={ category }
|
|
|
|
isDraggable={ false }
|
|
|
|
showTitlesAsTooltip={ true }
|
|
|
|
ref={ refElement }
|
|
|
|
/>
|
|
|
|
) }
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
};
|