Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Patterns explorer experiment #35006

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ export const getPatternTransformedBlocks = (
* @param {WPBlock[]} selectedBlocks The currently selected blocks.
* @return {TransformedBlockPattern[]} Returns the eligible matched patterns with all the selected blocks.
*/
// TODO tests
const useTransformedPatterns = ( patterns, selectedBlocks ) => {
return useMemo(
() =>
Expand Down
1 change: 1 addition & 0 deletions packages/block-editor/src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ export { useCanvasClickRedirect as __unstableUseCanvasClickRedirect } from './us
export { default as useBlockDisplayInformation } from './use-block-display-information';
export { default as __unstableIframe } from './iframe';
export { default as __experimentalUseNoRecursiveRenders } from './use-no-recursive-renders';
export { default as __experimentalPatternExplorer } from './pattern-explorer';

/*
* State Related Components
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,16 @@ import BlockPatternList from '../block-patterns-list';
function BlockPatternsCategory( {
rootClientId,
onInsert,
onSelectPattern,
selectedCategory,
onClickCategory,
isDraggable = true, // TODO: check docs (onSelectPattern too)
} ) {
// TODO: check other usages of `usePatternState`
const [ allPatterns, allCategories, onClick ] = usePatternsState(
onInsert,
rootClientId
rootClientId,
onSelectPattern
);

// Remove any empty categories
Expand Down Expand Up @@ -115,7 +119,7 @@ function BlockPatternsCategory( {
onClickPattern={ onClick }
label={ patternCategory.label }
orientation="vertical"
isDraggable
isDraggable={ isDraggable }
/>
</PatternInserterPanel>
) }
Expand All @@ -127,14 +131,18 @@ function BlockPatternsTabs( {
rootClientId,
onInsert,
onClickCategory,
onSelectPattern,
selectedCategory,
isDraggable = true,
} ) {
return (
<BlockPatternsCategory
rootClientId={ rootClientId }
selectedCategory={ selectedCategory }
onInsert={ onInsert }
onSelectPattern={ onSelectPattern }
onClickCategory={ onClickCategory }
isDraggable={ isDraggable }
/>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,12 @@ function useInsertionPoint( {
]
);

return [ destinationRootClientId, onInsertBlocks, onToggleInsertionPoint ];
return [
destinationRootClientId,
onInsertBlocks,
onToggleInsertionPoint,
destinationIndex,
];
}

export default useInsertionPoint;
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@ import { store as blockEditorStore } from '../../../store';
/**
* Retrieves the block patterns inserter state.
*
* @param {Function} onInsert function called when inserter a list of blocks.
* @param {string=} rootClientId Insertion's root client ID.
* @param {Function} onInsert function called when inserter a list of blocks.
* @param {string=} rootClientId Insertion's root client ID.
* @param {Function} onSelectPattern Callback function on pattern select.
*
* @return {Array} Returns the patterns state. (patterns, categories, onSelect handler)
*/
const usePatternsState = ( onInsert, rootClientId ) => {
const usePatternsState = ( onInsert, rootClientId, onSelectPattern ) => {
const { patternCategories, patterns } = useSelect(
( select ) => {
const { __experimentalGetAllowedPatterns, getSettings } = select(
Expand All @@ -41,20 +42,25 @@ const usePatternsState = ( onInsert, rootClientId ) => {
);
const { createSuccessNotice } = useDispatch( noticesStore );
const onClickPattern = useCallback( ( pattern, blocks ) => {
onInsert(
map( blocks, ( block ) => cloneBlock( block ) ),
pattern.name
);
createSuccessNotice(
sprintf(
/* translators: %s: block pattern title. */
__( 'Block pattern "%s" inserted.' ),
pattern.title
),
{
type: 'snackbar',
}
);
if ( onSelectPattern ) {
onSelectPattern( pattern );
}
if ( onInsert ) {
onInsert(
map( blocks, ( block ) => cloneBlock( block ) ),
pattern.name
);
createSuccessNotice(
sprintf(
/* translators: %s: block pattern title. */
__( 'Block pattern "%s" inserted.' ),
pattern.title
),
{
type: 'snackbar',
}
);
}
}, [] );

return [ patterns, patternCategories, onClickPattern ];
Expand Down
42 changes: 42 additions & 0 deletions packages/block-editor/src/components/pattern-explorer/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* WordPress dependencies
*/
import { useState } from '@wordpress/element';

/**
* Internal dependencies
*/
import PatternExplorerSidebar from './sidebar';
import PatternExplorerPreview from './preview';
import useInsertionPoint from '../inserter/hooks/use-insertion-point';

function PatternExplorer( { rootClientId, __experimentalInsertionIndex } ) {
const [ selectedPattern, setSelectedPattern ] = useState();
const [
destinationRootClientId,
onInsertBlocks,
,
destinationIndex,
] = useInsertionPoint( {
rootClientId,
insertionIndex: __experimentalInsertionIndex,
} );
return (
<div className="block-editor-pattern-explorer">
<PatternExplorerSidebar
rootClientId={ destinationRootClientId }
setSelectedPattern={ setSelectedPattern }
/>
{ selectedPattern && (
<PatternExplorerPreview
rootClientId={ destinationRootClientId }
destinationIndex={ destinationIndex }
selectedPattern={ selectedPattern }
onInsertBlocks={ onInsertBlocks }
/>
) }
</div>
);
}

export default PatternExplorer;
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* Internal dependencies
*/
import PreviewHeader from './preview-header';
import PreviewPattern from './preview-pattern';

function PatternExplorerPreview( {
rootClientId,
destinationIndex,
selectedPattern = {},
onInsertBlocks,
} ) {
const baseCssClass = 'block-editor-pattern-explorer__preview';
return (
<div className={ baseCssClass }>
<PreviewHeader
pattern={ selectedPattern }
onInsertBlocks={ onInsertBlocks }
/>
<PreviewPattern
pattern={ selectedPattern }
destinationRootClientId={ rootClientId }
destinationIndex={ destinationIndex }
/>
</div>
);
}

export default PatternExplorerPreview;
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* WordPress dependencies
*/
import { cloneBlock } from '@wordpress/blocks';
import { __ } from '@wordpress/i18n';
import { Button } from '@wordpress/components';

function PreviewHeader( { pattern, onInsertBlocks } ) {
const { title, categories = [], blocks } = pattern;
const baseCssClass = 'block-editor-pattern-explorer__preview__header';

return (
<div className={ baseCssClass }>
<div className={ `${ baseCssClass }__title` }>{ title }</div>
{ !! categories?.length && (
// Todo print categories properly
<div className={ `${ baseCssClass }__category` }>
{ categories.map( ( category ) => (
<span key={ category }>{ category }</span>
) ) }
</div>
) }
<div className={ `${ baseCssClass }__choose` }>
<Button
onClick={ () =>
onInsertBlocks(
blocks.map( ( block ) => cloneBlock( block ) )
)
}
isPrimary
>
{ __( 'Choose' ) }
</Button>
</div>
</div>
);
}

export default PreviewHeader;
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/**
* External dependencies
*/
import { cloneDeep } from 'lodash';

/**
* WordPress dependencies
*/
import { ResizableBox } from '@wordpress/components';
import { useSelect } from '@wordpress/data';
import { useState, useMemo } from '@wordpress/element';
import { cloneBlock } from '@wordpress/blocks';

/**
* Internal dependencies
*/
import PreviewSettings from './preview-settings';
import BlockPreview from '../../block-preview';
import { store as blockEditorStore } from '../../../store';

const INITIAL_WIDTH = 660;
const MIN_PREVIEW_WIDTH = 280;
const enableHandlers = {
left: true,
right: true,
top: false,
bottom: false,
};

// TODO: refactor, doc, tests (mutation)
const getPreviewBlocksInRootBlock = (
block,
patternBlocks,
destinationRootClientId,
destinationIndex
) => {
const { clientId } = block;
if ( clientId === destinationRootClientId ) {
block.innerBlocks = [
...block.innerBlocks.slice( 0, destinationIndex ),
...patternBlocks,
...block.innerBlocks.slice( destinationIndex ),
];
return block;
}
return {
...block,
innerBlocks: block.innerBlocks.map( ( innerBlock ) =>
getPreviewBlocksInRootBlock(
innerBlock,
patternBlocks,
destinationRootClientId,
destinationIndex
)
),
};
};

function PreviewPattern( {
pattern,
destinationRootClientId,
destinationIndex,
} ) {
// const [ width, setWidth ] = useState(
// window.innerWidth < INITIAL_WIDTH ? window.innerWidth : INITIAL_WIDTH
// );
const [ width, setWidth ] = useState( INITIAL_WIDTH );
const [ showPreviewWithContent, setShowPreviewWithContent ] = useState(
false
);
const { blocks } = pattern;
const baseCssClass = 'block-editor-pattern-explorer__preview__pattern';
const rootBlock = useSelect(
( select ) => {
const { getBlock, getBlockHierarchyRootClientId } = select(
blockEditorStore
);
return (
destinationRootClientId &&
getBlock(
getBlockHierarchyRootClientId( destinationRootClientId )
)
);
},
[ destinationRootClientId ]
);

const previewBlocks = useMemo( () => {
if ( ! showPreviewWithContent || ! rootBlock ) {
return blocks;
}

const _rootBlock = cloneDeep( rootBlock );
return [
cloneBlock(
getPreviewBlocksInRootBlock(
_rootBlock,
blocks,
destinationRootClientId,
destinationIndex
)
),
];
}, [
blocks,
rootBlock,
destinationRootClientId,
destinationIndex,
showPreviewWithContent,
] );

return (
<div className={ baseCssClass }>
<PreviewSettings
width={ width }
setWidth={ setWidth }
showPreviewWithContentControl={ !! destinationRootClientId }
showPreviewWithContent={ showPreviewWithContent }
setShowPreviewWithContent={ setShowPreviewWithContent }
/>
<ResizableBox
size={ {
width: width ?? 'auto',
height: 'auto',
} }
minWidth={ MIN_PREVIEW_WIDTH }
// maxWidth={ maxWidthBuffer }
// minHeight={ minHeight }
// maxHeight="50vh"
enable={ enableHandlers }
onResize={ ( _event, _direction, elt ) => {
setWidth( parseInt( elt.offsetWidth, 10 ) );
} }
// onResizeStop={ ( _event, _direction, elt ) => {
// setWidth( parseInt( elt.offsetWidth, 10 ) );
// } }
>
<BlockPreview
blocks={ previewBlocks }
viewportWidth={ width }
/>
</ResizableBox>
</div>
);
}

export default PreviewPattern;
Loading