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

Accurate sizes: Pass parent alignment context to images #1701

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
042232f
Add tests for passing parent alignment context to images
joemcgill Nov 25, 2024
47c6779
POC - Pass group block alignment context to image block
mukeshpanchal27 Nov 26, 2024
894ed57
Fix spell
mukeshpanchal27 Nov 26, 2024
cf8691a
Clean up
mukeshpanchal27 Nov 26, 2024
ee5f028
Merge branch 'feature/1511-incorporate-layout-constraints-from-ancest…
joemcgill Nov 26, 2024
93d3f08
Remove test group from unit test
mukeshpanchal27 Nov 28, 2024
f1ec64d
Move add_filter() calls in hooks.php
mukeshpanchal27 Nov 28, 2024
2735713
Remove unused wp_parse_args from unit test
mukeshpanchal27 Nov 28, 2024
b84d33e
Remove parent block context instead use existing key for check
mukeshpanchal27 Nov 28, 2024
5a9ce37
Bump unit test WP version to 6.6
mukeshpanchal27 Nov 28, 2024
2085845
Merge pull request #1704 from WordPress/update/poc-pass-alignment-by-…
mukeshpanchal27 Nov 28, 2024
de77338
implement static cache
mukeshpanchal27 Dec 2, 2024
ae82ccd
Separate function for width calculation and format sizes attribute
mukeshpanchal27 Dec 3, 2024
8189d71
Remove unused old function
mukeshpanchal27 Dec 3, 2024
2f03e55
Merge branch 'feature/1511-incorporate-layout-constraints-from-ancest…
joemcgill Dec 3, 2024
25d95ea
Rename filter callbacks for consistency
joemcgill Dec 3, 2024
9677bca
Simplify passing of context for sizes calculation
joemcgill Dec 5, 2024
5c6f7fd
Simplify context logic further
joemcgill Dec 5, 2024
df8741f
Remove unused auto_sizes_get_width function
joemcgill Dec 5, 2024
0790b92
Minor docbock update
mukeshpanchal27 Dec 6, 2024
8ca8b27
WIP: fix center alignments and cover blocks
joemcgill Dec 10, 2024
83be032
Return accurate sizes for center alignment
mukeshpanchal27 Dec 11, 2024
d3a60d0
Improve cover block left/right alignment
joemcgill Dec 12, 2024
266e457
Initial review feedback improvements
joemcgill Dec 12, 2024
aecb3de
Force resize width to be an int
joemcgill Dec 12, 2024
7fb1962
Additional type casting fixes
joemcgill Dec 12, 2024
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: 2 additions & 0 deletions plugins/auto-sizes/hooks.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,5 @@ function auto_sizes_render_generator(): void {
add_filter( 'the_content', 'auto_sizes_prime_attachment_caches', 9 ); // This must run before 'do_blocks', which runs at priority 9.
add_filter( 'render_block_core/image', 'auto_sizes_filter_image_tag', 10, 3 );
add_filter( 'render_block_core/cover', 'auto_sizes_filter_image_tag', 10, 3 );
add_filter( 'get_block_type_uses_context', 'auto_sizes_filter_uses_context', 10, 2 );
add_filter( 'render_block_context', 'auto_sizes_filter_render_block_context', 10, 2 );
216 changes: 168 additions & 48 deletions plugins/auto-sizes/includes/improve-calculate-sizes.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,6 @@
* @since n.e.x.t
*/

/**
* Gets the smaller image size if the layout width is bigger.
*
* It will return the smaller image size and return "px" if the layout width
* is something else, e.g. min(640px, 90vw) or 90vw.
*
* @since 1.1.0
*
* @param string $layout_width The layout width.
* @param int $image_width The image width.
* @return string The proper width after some calculations.
*/
function auto_sizes_get_width( string $layout_width, int $image_width ): string {
Copy link
Member Author

Choose a reason for hiding this comment

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

Technically we should probably deprecate this since it is in a current release, but I can't imagine it's being referenced by third party code.

if ( str_ends_with( $layout_width, 'px' ) ) {
return $image_width > (int) $layout_width ? $layout_width : $image_width . 'px';
}
return $image_width . 'px';
}

/**
* Primes attachment into the cache with a single database query.
*
Expand Down Expand Up @@ -84,6 +65,7 @@ function auto_sizes_filter_image_tag( $content, array $parsed_block, WP_Block $b
if ( ! is_string( $content ) ) {
return '';
}

$processor = new WP_HTML_Tag_Processor( $content );
$has_image = $processor->next_tag( array( 'tag_name' => 'IMG' ) );

Expand All @@ -99,11 +81,24 @@ function auto_sizes_filter_image_tag( $content, array $parsed_block, WP_Block $b
* @param string $size The image size data.
*/
$filter = static function ( $sizes, $size ) use ( $block ) {
$id = $block->attributes['id'] ?? 0;
$alignment = $block->attributes['align'] ?? '';
$width = $block->attributes['width'] ?? '';

return auto_sizes_calculate_better_sizes( (int) $id, (string) $size, (string) $alignment, (string) $width );
$id = isset( $block->attributes['id'] ) ? (int) $block->attributes['id'] : 0;
$alignment = $block->attributes['align'] ?? '';
$width = isset( $block->attributes['width'] ) ? (int) $block->attributes['width'] : 0;
$max_alignment = $block->context['max_alignment'] ?? '';

/*
* Update width for cover block.
* See https://github.com/WordPress/gutenberg/blob/938720602082dc50a1746bd2e33faa3d3a6096d4/packages/block-library/src/cover/style.scss#L82-L87.
*/
if ( 'core/cover' === $block->name && in_array( $alignment, array( 'left', 'right' ), true ) ) {
$size = array( 420, 420 );
}

$better_sizes = auto_sizes_calculate_better_sizes( $id, $size, $alignment, $width, $max_alignment );

// If better sizes can't be calculated, use the default sizes.
return false !== $better_sizes ? $better_sizes : $sizes;
};

// Hook this filter early, before default filters are run.
Expand Down Expand Up @@ -135,50 +130,175 @@ function auto_sizes_filter_image_tag( $content, array $parsed_block, WP_Block $b
/**
* Modifies the sizes attribute of an image based on layout context.
*
* @param int $id The image id.
* @param string $size The image size data.
* @param string $align The image alignment.
* @param string $resize_width Resize image width.
* @return string The sizes attribute value.
* @since n.e.x.t
*
* @param int $id The image attachment post ID.
* @param string|array{int, int} $size Image size name or array of width and height.
* @param string $align The image alignment.
* @param int $resize_width Resize image width.
* @param string $max_alignment The maximum usable layout alignment.
* @return string|false An improved sizes attribute or false if a better size cannot be calculated.
*/
function auto_sizes_calculate_better_sizes( int $id, string $size, string $align, string $resize_width ): string {
$sizes = '';
$image = wp_get_attachment_image_src( $id, $size );
function auto_sizes_calculate_better_sizes( int $id, $size, string $align, int $resize_width, string $max_alignment ) {
// Without an image ID or a resize width, we cannot calculate a better size.
if ( 0 === $id && 0 === $resize_width ) {
return false;
}

if ( false === $image ) {
return $sizes;
$image_data = wp_get_attachment_image_src( $id, $size );

$image_width = false !== $image_data ? $image_data[1] : 0;

// If we don't have an image width or a resize width, we cannot calculate a better size.
if ( 0 === $image_width && 0 === $resize_width ) {
return false;
}

// Retrieve width from the image tag itself.
$image_width = '' !== $resize_width ? (int) $resize_width : $image[1];
/*
* If we don't have an image width, use the resize width.
* If we have both an image width and a resize width, use the smaller of the two.
*/
if ( 0 === $image_width ) {
$image_width = $resize_width;
} elseif ( 0 !== $resize_width ) {
$image_width = min( $image_width, $resize_width );
}

// Normalize default alignment values.
$align = '' !== $align ? $align : 'default';

/*
* Map alignment values to a weighting value so they can be compared.
* Note that 'left' and 'right' alignments are only constrained by max alignment.
*/
$constraints = array(
'full' => 0,
'wide' => 1,
'left' => 2,
'right' => 2,
'default' => 3,
'center' => 3,
);

$layout = wp_get_global_settings( array( 'layout' ) );
$alignment = $constraints[ $align ] > $constraints[ $max_alignment ] ? $align : $max_alignment;

// Handle different alignment use cases.
switch ( $align ) {
switch ( $alignment ) {
case 'full':
$sizes = '100vw';
$layout_width = auto_sizes_get_layout_width( 'full' );
break;

case 'wide':
if ( array_key_exists( 'wideSize', $layout ) ) {
$sizes = sprintf( '(max-width: %1$s) 100vw, %1$s', $layout['wideSize'] );
}
$layout_width = auto_sizes_get_layout_width( 'wide' );
break;

case 'left':
case 'right':
case 'center':
$sizes = sprintf( '(max-width: %1$dpx) 100vw, %1$dpx', $image_width );
$layout_width = sprintf( '%1$spx', $image_width );
break;

case 'center':
default:
if ( array_key_exists( 'contentSize', $layout ) ) {
$width = auto_sizes_get_width( $layout['contentSize'], $image_width );
$sizes = sprintf( '(max-width: %1$s) 100vw, %1$s', $width );
}
$alignment = auto_sizes_get_layout_width( 'default' );
$layout_width = sprintf( '%1$spx', min( (int) $alignment, $image_width ) );
Copy link
Member

Choose a reason for hiding this comment

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

The return value of auto_sizes_get_layout_width() seems like it could be a non-pixel value, like 100vw. Is that a problem?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes. This is a concern that I've been thinking about and plan to address in a follow-up PR.

Copy link
Member Author

Choose a reason for hiding this comment

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

Documented in #1511 (comment)

break;
}

return $sizes;
// Format layout width when not 'full'.
if ( 'full' !== $alignment ) {
$layout_width = sprintf( '(max-width: %1$s) 100vw, %1$s', $layout_width );
}

return $layout_width;
}

/**
* Retrieves the layout width for an alignment defined in theme.json.
*
* @since n.e.x.t
*
* @param string $alignment The alignment value.
* @return string The alignment width based.
*/
function auto_sizes_get_layout_width( string $alignment ): string {
$layout = auto_sizes_get_layout_settings();

$layout_widths = array(
'full' => '100vw', // Todo: incorporate useRootPaddingAwareAlignments.
'wide' => array_key_exists( 'wideSize', $layout ) ? $layout['wideSize'] : '',
'default' => array_key_exists( 'contentSize', $layout ) ? $layout['contentSize'] : '',
);

return $layout_widths[ $alignment ] ?? '';
}

/**
* Filters the context keys that a block type uses.
*
* @since n.e.x.t
*
* @param string[] $uses_context Array of registered uses context for a block type.
* @param WP_Block_Type $block_type The full block type object.
* @return string[] The filtered context keys used by the block type.
*/
function auto_sizes_filter_uses_context( array $uses_context, WP_Block_Type $block_type ): array {
// The list of blocks that can consume outer layout context.
$consumer_blocks = array(
'core/cover',
'core/image',
);

if ( in_array( $block_type->name, $consumer_blocks, true ) ) {
// Use array_values to reset the array keys after merging.
return array_values( array_unique( array_merge( $uses_context, array( 'max_alignment' ) ) ) );
}
return $uses_context;
}

/**
* Modifies the block context during rendering to blocks.
*
* @since n.e.x.t
*
* @param array<string, mixed> $context Current block context.
* @param array<string, mixed> $block The block being rendered.
* @return array<string, mixed> Modified block context.
*/
function auto_sizes_filter_render_block_context( array $context, array $block ): array {
// When no max alignment is set, the maximum is assumed to be 'full'.
$context['max_alignment'] = $context['max_alignment'] ?? 'full';

// The list of blocks that can modify outer layout context.
$provider_blocks = array(
'core/columns',
'core/group',
);

if ( in_array( $block['blockName'], $provider_blocks, true ) ) {
$alignment = $block['attrs']['align'] ?? '';

// If the container block doesn't have alignment, it's assumed to be 'default'.
if ( '' === $alignment ) {
$context['max_alignment'] = 'default';
} elseif ( 'wide' === $alignment ) {
$context['max_alignment'] = 'wide';
}
}

return $context;
}

/**
* Retrieves the layout settings defined in theme.json.
*
* @since n.e.x.t
*
* @return array<string, mixed> Associative array of layout settings.
*/
function auto_sizes_get_layout_settings(): array {
static $layout = array();
if ( count( $layout ) === 0 ) {
$layout = wp_get_global_settings( array( 'layout' ) );
}
return $layout;
}
Loading
Loading