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

Try partially synced patterns via a template block #51042

Closed
wants to merge 8 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
30 changes: 30 additions & 0 deletions docs/reference-guides/core-blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,36 @@ Show a block pattern. ([Source](https://github.com/WordPress/gutenberg/tree/trun
- **Supports:** ~~html~~, ~~inserter~~
- **Attributes:** slug, syncStatus

## Pattern Template

Create arrangements of blocks where the design is fixed, but content can be altered. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/pattern-template))

- **Name:** core/pattern-template
- **Experimental:** true
- **Category:** design
- **Supports:** ~~html~~, ~~reusable~~
- **Attributes:**

## Pattern Template Content

The content of a pattern template. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/pattern-template-content))

- **Name:** core/pattern-template-content
- **Experimental:** true
- **Category:** design
- **Supports:** ~~html~~, ~~inserter~~, ~~reusable~~
- **Attributes:**

## Pattern Template Token

A token that can be inserted into a pattern template. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/pattern-template-token))

- **Name:** core/pattern-template-token
- **Experimental:** true
- **Category:** design
- **Supports:** ~~html~~, ~~inserter~~, ~~reusable~~
- **Attributes:**

## Post Author

Display post author details such as name, avatar, and bio. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/post-author))
Expand Down
1 change: 1 addition & 0 deletions lib/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ function gutenberg_reregister_core_block_types() {
'page-list.php' => 'core/page-list',
'page-list-item.php' => 'core/page-list-item',
'pattern.php' => 'core/pattern',
'pattern-template.php' => 'core/pattern-template',
'post-author.php' => 'core/post-author',
'post-author-name.php' => 'core/post-author-name',
'post-author-biography.php' => 'core/post-author-biography',
Expand Down
8 changes: 8 additions & 0 deletions packages/block-library/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ import * as navigationLink from './navigation-link';
import * as navigationSubmenu from './navigation-submenu';
import * as nextpage from './nextpage';
import * as pattern from './pattern';
import * as patternTemplate from './pattern-template';
import * as patternTemplateContent from './pattern-template-content';
import * as patternTemplateToken from './pattern-template-token';
import * as pageList from './page-list';
import * as pageListItem from './page-list-item';
import * as paragraph from './paragraph';
Expand Down Expand Up @@ -227,6 +230,11 @@ const getAllBlocks = () => {
queryTitle,
postAuthorBiography,
];
if ( window?.__experimentalEnablePatternEnhancements ) {
blocks.push( patternTemplate );
blocks.push( patternTemplateContent );
blocks.push( patternTemplateToken );
}
return blocks.filter( Boolean );
};

Expand Down
17 changes: 17 additions & 0 deletions packages/block-library/src/pattern-template-content/block.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"__experimental": true,
"name": "core/pattern-template-content",
"title": "Pattern Template Content",
"category": "design",
"description": "The content of a pattern template.",
"textdomain": "default",
"attributes": {},
"supports": {
"__experimentalExposeControlsToChildren": true,
"inserter": false,
"html": false,
"reusable": false
}
}
194 changes: 194 additions & 0 deletions packages/block-library/src/pattern-template-content/edit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
/**
* WordPress dependencies
*/
import {
BlockControls,
useBlockProps,
useInnerBlocksProps,
privateApis as blockEditorPrivateApis,
store as blockEditorStore,
} from '@wordpress/block-editor';
import { createBlock } from '@wordpress/blocks';
import { ToolbarButton } from '@wordpress/components';
import { createHigherOrderComponent } from '@wordpress/compose';
import { useDispatch, useRegistry, useSelect } from '@wordpress/data';
import { addFilter } from '@wordpress/hooks';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import { unlock } from '../private-apis';

const { useBlockEditingMode } = unlock( blockEditorPrivateApis );

const NON_CONVERTABLE_BLOCKS = [
'core/pattern-template-content',
'core/pattern-template-token',
];

export default function PatternTemplateContentEdit( { clientId } ) {
// useBlockEditingMode( 'disabled' );
const registry = useRegistry();

const {
parentClientId,
placeholderClientIds,
selectedBlockIndex,
selectedBlockName,
selectedClientId,
selectedParentClientId,
tokenIndex,
} = useSelect(
( select ) => {
const {
getBlocksByClientId,
getBlockIndex,
getBlockName,
getBlockOrder,
getClientIdsOfDescendants,
getBlockRootClientId,
getSelectedBlockClientId,
} = select( blockEditorStore );

const selectedBlockClientId = getSelectedBlockClientId();

// Find the index of this block compared to other token blocks.
const flattenedClientIds = getClientIdsOfDescendants( [
clientId,
] );
const tokenBlocks = getBlocksByClientId(
flattenedClientIds
).filter(
( { name, clientId: childClientId } ) =>
name === 'core/pattern-template-token' ||
childClientId === selectedBlockClientId
);
const tokenPosition = tokenBlocks.findIndex(
( { clientId: tokenClientId } ) =>
tokenClientId === selectedBlockClientId
);

const rootClientId = getBlockRootClientId( clientId );

return {
parentClientId: rootClientId,
placeholderClientIds: getBlockOrder( rootClientId ).filter(
( siblingClientId ) => siblingClientId !== clientId
),
selectedBlockIndex: getBlockIndex( selectedBlockClientId ),
selectedClientId: selectedBlockClientId,
selectedBlockName: getBlockName( selectedBlockClientId ),
selectedParentClientId: getBlockRootClientId(
selectedBlockClientId
),
tokenIndex: Math.max( 0, tokenPosition ),
};
},
[ clientId ]
);

const { insertBlock, removeBlock, moveBlockToPosition } =
useDispatch( blockEditorStore );

const canConvertToToken =
! NON_CONVERTABLE_BLOCKS.includes( selectedBlockName );

const canRevertFromToken =
selectedBlockName === 'core/pattern-template-token';

// Show a 'convert' button on children, but not on this block.
const controls = ( canConvertToToken || canRevertFromToken ) && (
<BlockControls group="block" __experimentalShareWithChildBlocks>
{ canConvertToToken && (
<ToolbarButton
onClick={ () => {
const token = createBlock(
'core/pattern-template-token'
);
const updateSelection = false;
registry.batch( () => {
// Move the selected block to the correct placeholder position.
moveBlockToPosition(
selectedClientId,
selectedParentClientId,
parentClientId,
tokenIndex + 1
);
// Insert a token in its place.
// TODO - determine if the move was successful before inserting the token.
insertBlock(
token,
selectedBlockIndex,
selectedParentClientId,
updateSelection
);
} );
} }
>
{ __( 'Convert to placeholder' ) }
</ToolbarButton>
) }
{ canRevertFromToken && (
<ToolbarButton
onClick={ () => {
const selectPrevious = true;
registry.batch( () => {
// Move the placeholder back to where the token is.
moveBlockToPosition(
placeholderClientIds[ tokenIndex ],
parentClientId,
selectedParentClientId,
selectedBlockIndex
);
// Remove the token.
removeBlock( selectedClientId, selectPrevious );
} );
} }
>
{ __( 'Remove token' ) }
</ToolbarButton>
) }
</BlockControls>
);

const blockProps = useBlockProps();
const innerBlocksProps = useInnerBlocksProps( blockProps );

// TODO - render the compiled template.
return (
<>
{ controls }
<div { ...innerBlocksProps } />
</>
);
}

const withEnableTemplateContent = createHigherOrderComponent(
( BlockEdit ) => ( props ) => {
const mode = useSelect(
( select ) => {
// While the pattern template content block itself has blockEditingMode=disabled,
// the children should still be enabled. Set 'default' for any children.
if (
select( blockEditorStore ).getBlockParentsByBlockName(
props.clientId,
'core/pattern-template-content'
).length
) {
return 'default';
}
},
[ props.clientId ]
);
useBlockEditingMode( mode );
return <BlockEdit { ...props } />;
},
'withBlockEditingMode'
);

addFilter(
'editor.BlockEdit',
'core/block-library/pattern-template-content/enable-content-blocks',
withEnableTemplateContent
);
17 changes: 17 additions & 0 deletions packages/block-library/src/pattern-template-content/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* Internal dependencies
*/
import initBlock from '../utils/init-block';
import metadata from './block.json';
import edit from './edit';
import save from './save';

const { name } = metadata;
export { metadata, name };

export const settings = {
edit,
save,
};

export const init = () => initBlock( { name, metadata, settings } );
8 changes: 8 additions & 0 deletions packages/block-library/src/pattern-template-content/save.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* WordPress dependencies
*/
import { useInnerBlocksProps, useBlockProps } from '@wordpress/block-editor';

export default function save() {
return <div { ...useInnerBlocksProps.save( useBlockProps.save() ) } />;
}
16 changes: 16 additions & 0 deletions packages/block-library/src/pattern-template-token/block.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"__experimental": true,
"name": "core/pattern-template-token",
"title": "Pattern Template Token",
"category": "design",
"description": "A token that can be inserted into a pattern template.",
"textdomain": "default",
"attributes": {},
"supports": {
"inserter": false,
"html": false,
"reusable": false
}
}
69 changes: 69 additions & 0 deletions packages/block-library/src/pattern-template-token/edit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/**
* WordPress dependencies
*/
import {
useBlockProps,
privateApis as blockEditorPrivateApis,
store as blockEditorStore,
} from '@wordpress/block-editor';
import { useSelect } from '@wordpress/data';

/**
* Internal dependencies
*/
import { unlock } from '../private-apis';

const { useBlockEditingMode } = unlock( blockEditorPrivateApis );

export default function PatternTemplateTokenEdit( { clientId } ) {
useBlockEditingMode( 'disabled' );

const placeholderBlock = useSelect(
( select ) => {
const {
getBlocksByClientId,
getBlockParentsByBlockName,
getClientIdsOfDescendants,
getBlockOrder,
getBlock,
} = select( blockEditorStore );
const [ contentClientId ] = getBlockParentsByBlockName(
clientId,
'core/pattern-template-content',
true
);

// Find the index of this block compared to other token blocks.
const flattenedClientIds = getClientIdsOfDescendants( [
contentClientId,
] );
const tokenBlocks = getBlocksByClientId(
flattenedClientIds
).filter( ( { name } ) => name === 'core/pattern-template-token' );
const myPosition = tokenBlocks.findIndex(
( { clientId: tokenClientId } ) => tokenClientId === clientId
);

// Use the index of the token to get the placeholder block.
const [ templateClientId ] = getBlockParentsByBlockName(
clientId,
'core/pattern-template',
true
);
const [ , ...placeholderClientIds ] =
getBlockOrder( templateClientId );
const placeholderClientId = placeholderClientIds[ myPosition ];

return getBlock( placeholderClientId );
},
[ clientId ]
);

const blockProps = useBlockProps();

return (
<div { ...blockProps }>
{ `Token for ${ placeholderBlock?.name } (clientId: ${ placeholderBlock?.clientId })` }
</div>
);
}
Loading