Skip to content

Commit

Permalink
Merge branch 'trunk' into focalpointpicker-align-items
Browse files Browse the repository at this point in the history
  • Loading branch information
t-hamano committed Dec 20, 2022
2 parents 11a6814 + 9dc48e0 commit 38b6b72
Show file tree
Hide file tree
Showing 30 changed files with 1,120 additions and 498 deletions.
Expand Up @@ -119,6 +119,12 @@ The Query Loop block determines if there is an active variation of itself and if

In order for a pattern to be “connected” with a Query Loop variation, you should add the name of your variation prefixed with the Query Loop name (e.g. `core/query/$variation_name`) to the pattern's `blockTypes` property. For more details about registering patterns [see here](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-patterns/).

If you have not provided `innerBlocks` in your variation, there is also a way to suggest “connected” variations when the user selects `Start blank` in the setup phase. This is handled in a similar fashion with “connected” patterns, by checking if there is an active variation of Query Loop and if there are any connected variations to suggest.

In order for a variation to be connected to another Query Loop variation we need to define the `scope` attribute with `['block']` as value and the `namespace` attribute defined as an array. This array should contain the names(`name` property) of any variations they want to be connected to.

For example, if we have a Query Loop variation exposed to the inserter(`scope: ['inserter']`) with the name `products`, we can connect a scoped `block` variation by setting its `namespace` attribute to `['products']`. If the user selects this variation after having clicked `Start blank`, the namespace attribute will be overridden by the main inserter variation.

### Making Gutenberg recognize your variation

There is one slight problem you might have realized after implementing this variation: while it is transparent to the user as they are inserting it, Gutenberg will still recognize the variation as a Query Loop block at its core and so, after its insertion, it will show up as a Query Loop block in the tree view of the editor, for instance.
Expand Down
Expand Up @@ -55,7 +55,7 @@ public function register_routes() {
* @return WP_REST_Response|WP_Error
*/
public function get_template_fallback( $request ) {
$hierarchy = get_template_hierarchy( $request['slug'], $request['is_custom'], $request['template_prefix'] );
$hierarchy = gutenberg_get_template_hierarchy( $request['slug'], $request['is_custom'], $request['template_prefix'] );
$fallback_template = resolve_block_template( $request['slug'], $hierarchy, '' );
$response = $this->prepare_item_for_response( $fallback_template, $request );
return rest_ensure_response( $response );
Expand Down
112 changes: 112 additions & 0 deletions lib/compat/wordpress-6.2/block-template-utils.php
@@ -0,0 +1,112 @@
<?php
/**
* Temporary compatibility shims for features present in Gutenberg.
* This file should be removed when WordPress 6.1.0 becomes the lowest
* supported version by this plugin.
*
* @package gutenberg
*/

/**
* Helper function to get the Template Hierarchy for a given slug.
* We need to Handle special cases here like `front-page`, `singular` and `archive` templates.
*
* Noting that we always add `index` as the last fallback template.
*
* @param string $slug The template slug to be created.
* @param boolean $is_custom Indicates if a template is custom or part of the template hierarchy.
* @param string $template_prefix The template prefix for the created template. This is used to extract the main template type ex. in `taxonomy-books` we extract the `taxonomy`.
*
* @return array<string> The template hierarchy.
*/
function gutenberg_get_template_hierarchy( $slug, $is_custom = false, $template_prefix = '' ) {
if ( 'index' === $slug ) {
return array( 'index' );
}
if ( $is_custom ) {
return array( 'page', 'singular', 'index' );
}
if ( 'front-page' === $slug ) {
return array( 'front-page', 'home', 'index' );
}

$template_hierarchy = array( $slug );
// Most default templates don't have `$template_prefix` assigned.
if ( ! empty( $template_prefix ) ) {
list($type) = explode( '-', $template_prefix );
// We need these checks because we always add the `$slug` above.
if ( ! in_array( $template_prefix, array( $slug, $type ), true ) ) {
$template_hierarchy[] = $template_prefix;
}
if ( $slug !== $type ) {
$template_hierarchy[] = $type;
}
} else {
$matches = array();
if ( preg_match( '/^(author|category|archive|tag|page)-(.+)$/', $slug, $matches ) ) {
$template_hierarchy[] = $matches[1];
} elseif ( preg_match( '/^(single|taxonomy)-(.+)$/', $slug, $matches ) ) {
$type = $matches[1];
$slug_remaining = $matches[2];
if ( 'single' === $type ) {
$post_types = get_post_types();
foreach ( $post_types as $post_type ) {
if ( str_starts_with( $slug_remaining, $post_type ) ) {
// If $slug_remaining is equal to $post_type we have the single-$post_type template.
if ( $slug_remaining === $post_type ) {
$template_hierarchy[] = 'single';
break;
}
// If $slug_remaining is single-$post_type-$slug template.
if ( str_starts_with( $slug_remaining, $post_type . '-' ) && strlen( $slug_remaining ) > strlen( $post_type ) + 1 ) {
$template_hierarchy[] = "single-$post_type";
$template_hierarchy[] = 'single';
break;
}
}
}
} elseif ( 'taxonomy' === $type ) {
$taxonomies = get_taxonomies();
foreach ( $taxonomies as $taxonomy ) {
if ( str_starts_with( $slug_remaining, $taxonomy ) ) {
// If $slug_remaining is equal to $taxonomy we have the taxonomy-$taxonomy template.
if ( $slug_remaining === $taxonomy ) {
$template_hierarchy[] = 'taxonomy';
break;
}
// If $slug_remaining is taxonomy-$taxonomy-$term template.
if ( str_starts_with( $slug_remaining, $taxonomy . '-' ) && strlen( $slug_remaining ) > strlen( $taxonomy ) + 1 ) {
$template_hierarchy[] = "taxonomy-$taxonomy";
$template_hierarchy[] = 'taxonomy';
break;
}
}
}
}
}
}
// Handle `archive` template.
if (
str_starts_with( $slug, 'author' ) ||
str_starts_with( $slug, 'taxonomy' ) ||
str_starts_with( $slug, 'category' ) ||
str_starts_with( $slug, 'tag' ) ||
'date' === $slug
) {
$template_hierarchy[] = 'archive';
}
// Handle `single` template.
if ( 'attachment' === $slug ) {
$template_hierarchy[] = 'single';
}
// Handle `singular` template.
if (
str_starts_with( $slug, 'single' ) ||
str_starts_with( $slug, 'page' ) ||
'attachment' === $slug
) {
$template_hierarchy[] = 'singular';
}
$template_hierarchy[] = 'index';
return $template_hierarchy;
}
1 change: 1 addition & 0 deletions lib/load.php
Expand Up @@ -77,6 +77,7 @@ function gutenberg_is_experiment_enabled( $name ) {

// WordPress 6.2 compat.
require __DIR__ . '/compat/wordpress-6.2/script-loader.php';
require __DIR__ . '/compat/wordpress-6.2/block-template-utils.php';
require __DIR__ . '/compat/wordpress-6.2/get-global-styles-and-settings.php';
require __DIR__ . '/compat/wordpress-6.2/default-filters.php';
require __DIR__ . '/compat/wordpress-6.2/class-wp-theme-json-resolver-6-2.php';
Expand Down
Expand Up @@ -10,6 +10,7 @@ import { useLayout } from '../block-list/layout';
import { store as blockEditorStore } from '../../store';
import { getLayoutType } from '../../layouts';

const EMPTY_ARRAY = [];
const DEFAULT_CONTROLS = [ 'none', 'left', 'center', 'right', 'wide', 'full' ];
const WIDE_CONTROLS = [ 'wide', 'full' ];

Expand Down Expand Up @@ -40,14 +41,14 @@ export default function useAvailableAlignments( controls = DEFAULT_CONTROLS ) {
// While we treat `none` as an alignment, we shouldn't return it if no
// other alignments exist.
if ( alignments.length === 1 && alignments[ 0 ].name === 'none' ) {
return [];
return EMPTY_ARRAY;
}
return alignments;
}

// Starting here, it's the fallback for themes not supporting the layout config.
if ( layoutType.name !== 'default' && layoutType.name !== 'constrained' ) {
return [];
return EMPTY_ARRAY;
}
const { alignments: availableAlignments = DEFAULT_CONTROLS } = layout;
const enabledControls = controls
Expand All @@ -66,7 +67,7 @@ export default function useAvailableAlignments( controls = DEFAULT_CONTROLS ) {
enabledControls.length === 1 &&
enabledControls[ 0 ].name === 'none'
) {
return [];
return EMPTY_ARRAY;
}

return enabledControls;
Expand Down
119 changes: 65 additions & 54 deletions packages/block-editor/src/components/block-list-appender/index.js
Expand Up @@ -16,67 +16,78 @@ import DefaultBlockAppender from '../default-block-appender';
import ButtonBlockAppender from '../button-block-appender';
import { store as blockEditorStore } from '../../store';

function BlockListAppender( {
rootClientId,
renderAppender: CustomAppender,
className,
tagName: TagName = 'div',
} ) {
const { hideInserter, canInsertDefaultBlock, selectedBlockClientId } =
useSelect(
( select ) => {
const {
canInsertBlockType,
getTemplateLock,
getSelectedBlockClientId,
__unstableGetEditorMode,
} = select( blockEditorStore );

return {
hideInserter:
!! getTemplateLock( rootClientId ) ||
__unstableGetEditorMode() === 'zoom-out',
canInsertDefaultBlock: canInsertBlockType(
getDefaultBlockName(),
rootClientId
),
selectedBlockClientId: getSelectedBlockClientId(),
};
},
[ rootClientId ]
);
function DefaultAppender( { rootClientId } ) {
const canInsertDefaultBlock = useSelect( ( select ) =>
select( blockEditorStore ).canInsertBlockType(
getDefaultBlockName(),
rootClientId
)
);

if ( canInsertDefaultBlock ) {
// Render the default block appender if the context supports use
// of the default appender.
return <DefaultBlockAppender rootClientId={ rootClientId } />;
}

// Fallback in case the default block can't be inserted.
return (
<ButtonBlockAppender
rootClientId={ rootClientId }
className="block-list-appender__toggle"
/>
);
}

function useAppender( rootClientId, CustomAppender ) {
const { hideInserter, isParentSelected } = useSelect(
( select ) => {
const {
getTemplateLock,
getSelectedBlockClientId,
__unstableGetEditorMode,
} = select( blockEditorStore );

const selectedBlockClientId = getSelectedBlockClientId();

return {
hideInserter:
!! getTemplateLock( rootClientId ) ||
__unstableGetEditorMode() === 'zoom-out',
isParentSelected:
rootClientId === selectedBlockClientId ||
( ! rootClientId && ! selectedBlockClientId ),
};
},
[ rootClientId ]
);

if ( hideInserter || CustomAppender === false ) {
return null;
}

let appender;
if ( CustomAppender ) {
// Prefer custom render prop if provided.
appender = <CustomAppender />;
} else {
const isParentSelected =
selectedBlockClientId === rootClientId ||
( ! rootClientId && ! selectedBlockClientId );

if ( ! isParentSelected ) {
return null;
}

if ( canInsertDefaultBlock ) {
// Render the default block appender when renderAppender has not been
// provided and the context supports use of the default appender.
appender = <DefaultBlockAppender rootClientId={ rootClientId } />;
} else {
// Fallback in the case no renderAppender has been provided and the
// default block can't be inserted.
appender = (
<ButtonBlockAppender
rootClientId={ rootClientId }
className="block-list-appender__toggle"
/>
);
}
return <CustomAppender />;
}

if ( ! isParentSelected ) {
return null;
}

return <DefaultAppender rootClientId={ rootClientId } />;
}

function BlockListAppender( {
rootClientId,
renderAppender,
className,
tagName: TagName = 'div',
} ) {
const appender = useAppender( rootClientId, renderAppender );

if ( ! appender ) {
return null;
}

return (
Expand Down

1 comment on commit 38b6b72

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flaky tests detected.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/3740282262
📝 Reported issues:

Please sign in to comment.