-
Notifications
You must be signed in to change notification settings - Fork 107
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
Changes from all commits
042232f
47c6779
894ed57
cf8691a
ee5f028
93d3f08
f1ec64d
2735713
b84d33e
5a9ce37
2085845
de77338
ae82ccd
8189d71
2f03e55
25d95ea
9677bca
5c6f7fd
df8741f
0790b92
8ca8b27
83be032
d3a60d0
266e457
aecb3de
7fb1962
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 { | ||
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. | ||
* | ||
|
@@ -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' ) ); | ||
|
||
|
@@ -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. | ||
|
@@ -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 ) ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The return value of There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||
} |
There was a problem hiding this comment.
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.