Skip to content

Commit 59e86f8

Browse files
joemcgillmukeshpanchal27felixarntzwestonruter
authored
Merge pull request #1701 from WordPress/update/pass-alignment-by-context
Accurate sizes: Pass parent alignment context to images Co-authored-by: joemcgill <[email protected]> Co-authored-by: mukeshpanchal27 <[email protected]> Co-authored-by: felixarntz <[email protected]> Co-authored-by: westonruter <[email protected]>
2 parents d209703 + 7fb1962 commit 59e86f8

File tree

3 files changed

+357
-60
lines changed

3 files changed

+357
-60
lines changed

plugins/auto-sizes/hooks.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,5 @@ function auto_sizes_render_generator(): void {
3838
add_filter( 'the_content', 'auto_sizes_prime_attachment_caches', 9 ); // This must run before 'do_blocks', which runs at priority 9.
3939
add_filter( 'render_block_core/image', 'auto_sizes_filter_image_tag', 10, 3 );
4040
add_filter( 'render_block_core/cover', 'auto_sizes_filter_image_tag', 10, 3 );
41+
add_filter( 'get_block_type_uses_context', 'auto_sizes_filter_uses_context', 10, 2 );
42+
add_filter( 'render_block_context', 'auto_sizes_filter_render_block_context', 10, 2 );

plugins/auto-sizes/includes/improve-calculate-sizes.php

Lines changed: 168 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,6 @@
66
* @since n.e.x.t
77
*/
88

9-
/**
10-
* Gets the smaller image size if the layout width is bigger.
11-
*
12-
* It will return the smaller image size and return "px" if the layout width
13-
* is something else, e.g. min(640px, 90vw) or 90vw.
14-
*
15-
* @since 1.1.0
16-
*
17-
* @param string $layout_width The layout width.
18-
* @param int $image_width The image width.
19-
* @return string The proper width after some calculations.
20-
*/
21-
function auto_sizes_get_width( string $layout_width, int $image_width ): string {
22-
if ( str_ends_with( $layout_width, 'px' ) ) {
23-
return $image_width > (int) $layout_width ? $layout_width : $image_width . 'px';
24-
}
25-
return $image_width . 'px';
26-
}
27-
289
/**
2910
* Primes attachment into the cache with a single database query.
3011
*
@@ -84,6 +65,7 @@ function auto_sizes_filter_image_tag( $content, array $parsed_block, WP_Block $b
8465
if ( ! is_string( $content ) ) {
8566
return '';
8667
}
68+
8769
$processor = new WP_HTML_Tag_Processor( $content );
8870
$has_image = $processor->next_tag( array( 'tag_name' => 'IMG' ) );
8971

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

106-
return auto_sizes_calculate_better_sizes( (int) $id, (string) $size, (string) $alignment, (string) $width );
85+
$id = isset( $block->attributes['id'] ) ? (int) $block->attributes['id'] : 0;
86+
$alignment = $block->attributes['align'] ?? '';
87+
$width = isset( $block->attributes['width'] ) ? (int) $block->attributes['width'] : 0;
88+
$max_alignment = $block->context['max_alignment'] ?? '';
89+
90+
/*
91+
* Update width for cover block.
92+
* See https://github.com/WordPress/gutenberg/blob/938720602082dc50a1746bd2e33faa3d3a6096d4/packages/block-library/src/cover/style.scss#L82-L87.
93+
*/
94+
if ( 'core/cover' === $block->name && in_array( $alignment, array( 'left', 'right' ), true ) ) {
95+
$size = array( 420, 420 );
96+
}
97+
98+
$better_sizes = auto_sizes_calculate_better_sizes( $id, $size, $alignment, $width, $max_alignment );
99+
100+
// If better sizes can't be calculated, use the default sizes.
101+
return false !== $better_sizes ? $better_sizes : $sizes;
107102
};
108103

109104
// 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
135130
/**
136131
* Modifies the sizes attribute of an image based on layout context.
137132
*
138-
* @param int $id The image id.
139-
* @param string $size The image size data.
140-
* @param string $align The image alignment.
141-
* @param string $resize_width Resize image width.
142-
* @return string The sizes attribute value.
133+
* @since n.e.x.t
134+
*
135+
* @param int $id The image attachment post ID.
136+
* @param string|array{int, int} $size Image size name or array of width and height.
137+
* @param string $align The image alignment.
138+
* @param int $resize_width Resize image width.
139+
* @param string $max_alignment The maximum usable layout alignment.
140+
* @return string|false An improved sizes attribute or false if a better size cannot be calculated.
143141
*/
144-
function auto_sizes_calculate_better_sizes( int $id, string $size, string $align, string $resize_width ): string {
145-
$sizes = '';
146-
$image = wp_get_attachment_image_src( $id, $size );
142+
function auto_sizes_calculate_better_sizes( int $id, $size, string $align, int $resize_width, string $max_alignment ) {
143+
// Without an image ID or a resize width, we cannot calculate a better size.
144+
if ( 0 === $id && 0 === $resize_width ) {
145+
return false;
146+
}
147147

148-
if ( false === $image ) {
149-
return $sizes;
148+
$image_data = wp_get_attachment_image_src( $id, $size );
149+
150+
$image_width = false !== $image_data ? $image_data[1] : 0;
151+
152+
// If we don't have an image width or a resize width, we cannot calculate a better size.
153+
if ( 0 === $image_width && 0 === $resize_width ) {
154+
return false;
150155
}
151156

152-
// Retrieve width from the image tag itself.
153-
$image_width = '' !== $resize_width ? (int) $resize_width : $image[1];
157+
/*
158+
* If we don't have an image width, use the resize width.
159+
* If we have both an image width and a resize width, use the smaller of the two.
160+
*/
161+
if ( 0 === $image_width ) {
162+
$image_width = $resize_width;
163+
} elseif ( 0 !== $resize_width ) {
164+
$image_width = min( $image_width, $resize_width );
165+
}
166+
167+
// Normalize default alignment values.
168+
$align = '' !== $align ? $align : 'default';
169+
170+
/*
171+
* Map alignment values to a weighting value so they can be compared.
172+
* Note that 'left' and 'right' alignments are only constrained by max alignment.
173+
*/
174+
$constraints = array(
175+
'full' => 0,
176+
'wide' => 1,
177+
'left' => 2,
178+
'right' => 2,
179+
'default' => 3,
180+
'center' => 3,
181+
);
154182

155-
$layout = wp_get_global_settings( array( 'layout' ) );
183+
$alignment = $constraints[ $align ] > $constraints[ $max_alignment ] ? $align : $max_alignment;
156184

157185
// Handle different alignment use cases.
158-
switch ( $align ) {
186+
switch ( $alignment ) {
159187
case 'full':
160-
$sizes = '100vw';
188+
$layout_width = auto_sizes_get_layout_width( 'full' );
161189
break;
162190

163191
case 'wide':
164-
if ( array_key_exists( 'wideSize', $layout ) ) {
165-
$sizes = sprintf( '(max-width: %1$s) 100vw, %1$s', $layout['wideSize'] );
166-
}
192+
$layout_width = auto_sizes_get_layout_width( 'wide' );
167193
break;
168194

169195
case 'left':
170196
case 'right':
171-
case 'center':
172-
$sizes = sprintf( '(max-width: %1$dpx) 100vw, %1$dpx', $image_width );
197+
$layout_width = sprintf( '%1$spx', $image_width );
173198
break;
174199

200+
case 'center':
175201
default:
176-
if ( array_key_exists( 'contentSize', $layout ) ) {
177-
$width = auto_sizes_get_width( $layout['contentSize'], $image_width );
178-
$sizes = sprintf( '(max-width: %1$s) 100vw, %1$s', $width );
179-
}
202+
$alignment = auto_sizes_get_layout_width( 'default' );
203+
$layout_width = sprintf( '%1$spx', min( (int) $alignment, $image_width ) );
180204
break;
181205
}
182206

183-
return $sizes;
207+
// Format layout width when not 'full'.
208+
if ( 'full' !== $alignment ) {
209+
$layout_width = sprintf( '(max-width: %1$s) 100vw, %1$s', $layout_width );
210+
}
211+
212+
return $layout_width;
213+
}
214+
215+
/**
216+
* Retrieves the layout width for an alignment defined in theme.json.
217+
*
218+
* @since n.e.x.t
219+
*
220+
* @param string $alignment The alignment value.
221+
* @return string The alignment width based.
222+
*/
223+
function auto_sizes_get_layout_width( string $alignment ): string {
224+
$layout = auto_sizes_get_layout_settings();
225+
226+
$layout_widths = array(
227+
'full' => '100vw', // Todo: incorporate useRootPaddingAwareAlignments.
228+
'wide' => array_key_exists( 'wideSize', $layout ) ? $layout['wideSize'] : '',
229+
'default' => array_key_exists( 'contentSize', $layout ) ? $layout['contentSize'] : '',
230+
);
231+
232+
return $layout_widths[ $alignment ] ?? '';
233+
}
234+
235+
/**
236+
* Filters the context keys that a block type uses.
237+
*
238+
* @since n.e.x.t
239+
*
240+
* @param string[] $uses_context Array of registered uses context for a block type.
241+
* @param WP_Block_Type $block_type The full block type object.
242+
* @return string[] The filtered context keys used by the block type.
243+
*/
244+
function auto_sizes_filter_uses_context( array $uses_context, WP_Block_Type $block_type ): array {
245+
// The list of blocks that can consume outer layout context.
246+
$consumer_blocks = array(
247+
'core/cover',
248+
'core/image',
249+
);
250+
251+
if ( in_array( $block_type->name, $consumer_blocks, true ) ) {
252+
// Use array_values to reset the array keys after merging.
253+
return array_values( array_unique( array_merge( $uses_context, array( 'max_alignment' ) ) ) );
254+
}
255+
return $uses_context;
256+
}
257+
258+
/**
259+
* Modifies the block context during rendering to blocks.
260+
*
261+
* @since n.e.x.t
262+
*
263+
* @param array<string, mixed> $context Current block context.
264+
* @param array<string, mixed> $block The block being rendered.
265+
* @return array<string, mixed> Modified block context.
266+
*/
267+
function auto_sizes_filter_render_block_context( array $context, array $block ): array {
268+
// When no max alignment is set, the maximum is assumed to be 'full'.
269+
$context['max_alignment'] = $context['max_alignment'] ?? 'full';
270+
271+
// The list of blocks that can modify outer layout context.
272+
$provider_blocks = array(
273+
'core/columns',
274+
'core/group',
275+
);
276+
277+
if ( in_array( $block['blockName'], $provider_blocks, true ) ) {
278+
$alignment = $block['attrs']['align'] ?? '';
279+
280+
// If the container block doesn't have alignment, it's assumed to be 'default'.
281+
if ( '' === $alignment ) {
282+
$context['max_alignment'] = 'default';
283+
} elseif ( 'wide' === $alignment ) {
284+
$context['max_alignment'] = 'wide';
285+
}
286+
}
287+
288+
return $context;
289+
}
290+
291+
/**
292+
* Retrieves the layout settings defined in theme.json.
293+
*
294+
* @since n.e.x.t
295+
*
296+
* @return array<string, mixed> Associative array of layout settings.
297+
*/
298+
function auto_sizes_get_layout_settings(): array {
299+
static $layout = array();
300+
if ( count( $layout ) === 0 ) {
301+
$layout = wp_get_global_settings( array( 'layout' ) );
302+
}
303+
return $layout;
184304
}

0 commit comments

Comments
 (0)