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

[WIP] Patterns: Explore partial syncing #53887

Closed
wants to merge 15 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
2 changes: 1 addition & 1 deletion docs/reference-guides/core-blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Create and save content to reuse across your site. Update the pattern, and the c
- **Name:** core/block
- **Category:** reusable
- **Supports:** ~~customClassName~~, ~~html~~, ~~inserter~~
- **Attributes:** ref
- **Attributes:** dynamicContent, patternId, ref

## Button

Expand Down
180 changes: 180 additions & 0 deletions lib/block-supports/pattern.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
<?php
/**
* Pattern block support flag.
*
* @package gutenberg
*/

$gutenberg_experiments = get_option( 'gutenberg-experiments' );
if ( $gutenberg_experiments && array_key_exists( 'gutenberg-patterns', $gutenberg_experiments ) ) {
/**
* Adds `patternId` and `dynamicContent` items to the block's `usesContext`
* configuration.
*
* @param WP_Block_Type $block_type Block type.
*/
function gutenberg_register_pattern_support( $block_type ) {
$pattern_support = property_exists( $block_type, 'supports' ) ? _wp_array_get( $block_type->supports, array( '__experimentalPattern' ), false ) : false;

if ( $pattern_support ) {
if ( ! $block_type->uses_context ) {
$block_type->uses_context = array();
}

if ( ! in_array( 'patternId', $block_type->uses_context, true ) ) {
$block_type->uses_context[] = 'patternId';
}

if ( ! in_array( 'dynamicContent', $block_type->uses_context, true ) ) {
$block_type->uses_context[] = 'dynamicContent';
}
}
}

/**
* Creates an HTML tag processor based off the block's content and selector
* for the pattern sourced attribute.
*
* @param string $block_content Block markup.
* @param string $selector Attribute's CSS selector.
*
* @return WP_HTML_Tag_Processor HTML tag processor having matched on attribute's selector.
*/
function gutenberg_create_pattern_content_processor( $block_content, $selector ) {
if ( ! $selector ) {
return false;
}

$tags = new WP_HTML_Tag_Processor( $block_content );
$is_selector_list = strpos( $selector, ',' ) !== false;

if ( ! $is_selector_list ) {
// TODO: The retrieval via selector could do with some work.
$found = $tags->next_tag( array( 'tag_name' => $selector ) );

return $found ? $tags : null;
}

$found = false;
$selectors = explode( ',', $selector );

foreach ( $selectors as $tag_selector ) {
// TODO: The retrieval via selector could do with some work.
$found = $tags->next_tag( array( 'tag_name' => $tag_selector ) );

if ( $found ) {
break;
}

// TODO: Revisit whether a bookmark can be used here. The need for
// the bookmark to be on a found tag meant that you already needed
// to have searched and found a tag which made the rest of this
// search awkward. Perhaps we could wrap the block in a div and
// create the processor from that content, bookmarking that outer
// div if the current approach isn't performant.
$tags = new WP_HTML_Tag_Processor( $block_content );
}

return $found ? $tags : null;
}

/**
* Renders pattern data into the final block markup for block's within
* partially synced patterns.
*
* @param string $block_content Block Content.
* @param array $block Block attributes.
* @param WP_Block $block_instance The block instance.
*
* @return string
*/
function gutenberg_render_block_pattern_data( $block_content, $block, $block_instance ) {
$block_type = $block_instance->block_type;

// If for some reason, the block type is not found, skip it.
if ( null === $block_type ) {
return $block_content;
}

// If the block does not have pattern support, skip it.
if ( ! block_has_support( $block_type, array( '__experimentalPattern' ), false ) ) {
return $block_content;
}

// If the block doesn't have an ID to retrieve pattern instance data from, skip it.
$pattern_block_id = _wp_array_get( $block, array( 'attrs', 'metadata', 'id' ), false );
if ( ! $pattern_block_id ) {
return $block_content;
}

// If there is no dynamic content matching this block's ID, skip it.
$dynamic_content = _wp_array_get( $block_instance->context, array( 'dynamicContent', $pattern_block_id ), false );
if ( ! $dynamic_content ) {
return $block_content;
}

$pattern_attributes = _wp_array_get( $block_type->supports, array( '__experimentalPattern' ), false );

foreach ( $pattern_attributes as $pattern_attribute => $pattern_attribute_type ) {
// Some attributes might not need to be used in the markup but are
// linked to other attributes e.g. Image block's url and id attributes.
// TODO: Update the markup to alter the classname etc if required for image's and IDs etc.
if ( 'ignore' === $pattern_attribute_type ) {
continue;
}

$pattern_attribute_value = _wp_array_get( $dynamic_content, array( $pattern_attribute ), false );

if ( ! $pattern_attribute_value ) {
continue;
}

$selector = _wp_array_get( $block_type->attributes, array( $pattern_attribute, 'selector' ), null );

if ( ! $selector ) {
continue;
}

$tags = gutenberg_create_pattern_content_processor( $block_content, $selector );

if ( ! $tags ) {
continue;
}

// Only inner html content and DOM attributes are currently processed.

// Process content.
if ( 'content' === $pattern_attribute_type ) {
$tag_name = $tags->get_tag();
$markup = "<$tag_name>$pattern_attribute_value</$tag_name>";
$updated_tags = new WP_HTML_Tag_Processor( $markup );
$updated_tags->next_tag();

// Get all the attributes from the original block and add them to the new markup.
$names = $tags->get_attribute_names_with_prefix( '' );
foreach ( $names as $name ) {
$updated_tags->set_attribute( $name, $tags->get_attribute( $name ) );
}

$block_content = $updated_tags->get_updated_html();
}

if ( 'attr' === $pattern_attribute_type ) {
$tags->set_attribute( $block_type->attributes[ $pattern_attribute ]['attribute'], $pattern_attribute_value );
$block_content = $tags->get_updated_html();
}
}

return $block_content;
}

// Register the block support.
WP_Block_Supports::get_instance()->register(
'pattern',
array(
'register_attribute' => 'gutenberg_register_pattern_support',
)
);

add_filter( 'render_block', 'gutenberg_render_block_pattern_data', 10, 3 );
}
1 change: 1 addition & 0 deletions lib/experimental/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -223,5 +223,6 @@ function gutenberg_render_block_connections( $block_content, $block, $block_inst

return $block_content;
}

add_filter( 'render_block', 'gutenberg_render_block_connections', 10, 3 );
}
4 changes: 4 additions & 0 deletions lib/experimental/editor-settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ function gutenberg_enable_experiments() {
wp_add_inline_script( 'wp-block-editor', 'window.__experimentalConnections = true', 'before' );
}

if ( $gutenberg_experiments && array_key_exists( 'gutenberg-patterns', $gutenberg_experiments ) ) {
wp_add_inline_script( 'wp-block-editor', 'window.__experimentalPatterns = true', 'before' );
}

if ( gutenberg_is_experiment_enabled( 'gutenberg-no-tinymce' ) ) {
wp_add_inline_script( 'wp-block-library', 'window.__experimentalDisableTinymce = true', 'before' );
}
Expand Down
12 changes: 12 additions & 0 deletions lib/experiments-page.php
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,18 @@ function gutenberg_initialize_experiments_settings() {
)
);

add_settings_field(
'gutenberg-patterns',
__( 'Partially synced patterns', 'gutenberg' ),
'gutenberg_display_experiment_field',
'gutenberg-experiments',
'gutenberg_experiments_section',
array(
'label' => __( 'Test partially synced patterns', 'gutenberg' ),
'id' => 'gutenberg-patterns',
)
);

register_setting(
'gutenberg-experiments',
'gutenberg-experiments'
Expand Down
1 change: 1 addition & 0 deletions lib/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -251,3 +251,4 @@ function gutenberg_is_experiment_enabled( $name ) {
require __DIR__ . '/block-supports/duotone.php';
require __DIR__ . '/block-supports/shadow.php';
require __DIR__ . '/block-supports/behaviors.php';
require __DIR__ . '/block-supports/pattern.php';
1 change: 1 addition & 0 deletions packages/block-editor/src/hooks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import './duotone';
import './font-family';
import './font-size';
import './border';
import './pattern';
import './position';
import './layout';
import './content-lock-ui';
Expand Down
Loading