PluginProbe
Gutenberg / 24.1.0
Gutenberg v24.1.0
24.1.0 24.0.0 23.9.1 23.9.0 23.8.0 23.7.2 23.7.1 23.7.0 23.6.1 23.6.2 23.6.0 23.5.3 23.5.2 23.5.1 23.5.0 23.4.0 23.3.2 23.3.1 23.3.0 23.2.0 23.2.1 23.2.2 23.1.1 23.1.0 23.0.1 All 404 releases
gutenberg / lib / block-supports / position.php

position.php in Gutenberg 24.1.0, at lib/block-supports/position.php

249 lines 7.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Position block support flag.
4 *
5 * @package gutenberg
6 */
7
8 /**
9 * Registers the style block attribute for block types that support it.
10 *
11 * @param WP_Block_Type $block_type Block Type.
12 */
13 function gutenberg_register_position_support( $block_type ) {
14 $has_position_support = block_has_support( $block_type, array( 'position' ), false );
15
16 // Set up attributes and styles within that if needed.
17 if ( ! $block_type->attributes ) {
18 $block_type->attributes = array();
19 }
20
21 if ( $has_position_support && ! array_key_exists( 'style', $block_type->attributes ) ) {
22 $block_type->attributes['style'] = array(
23 'type' => 'object',
24 );
25 }
26 }
27
28 /**
29 * Returns the CSS rules for a position style configuration.
30 *
31 * @param string $selector CSS selector to scope the rules to.
32 * @param mixed $position Position style configuration.
33 * @param array $allowed_position_types Position types the theme supports.
34 * @return array CSS rules, or an empty array when the configuration is not allowed.
35 */
36 function gutenberg_get_position_support_styles( $selector, $position, $allowed_position_types ) {
37 $styles = array();
38
39 if ( ! is_array( $position ) ) {
40 return $styles;
41 }
42
43 $position_type = $position['type'] ?? '';
44
45 if ( ! in_array( $position_type, $allowed_position_types, true ) ) {
46 return $styles;
47 }
48
49 $sides = array( 'top', 'right', 'bottom', 'left' );
50
51 foreach ( $sides as $side ) {
52 $side_value = $position[ $side ] ?? null;
53 if ( null !== $side_value ) {
54 /*
55 * For fixed or sticky top positions,
56 * ensure the value includes an offset for the logged in admin bar.
57 */
58 if ( 'top' === $side ) {
59 // Ensure 0 values can be used in `calc()` calculations.
60 if ( '0' === $side_value || 0 === $side_value ) {
61 $side_value = '0px';
62 }
63
64 // Ensure current side value also factors in the height of the logged in admin bar.
65 $side_value = "calc($side_value + var(--wp-admin--admin-bar--position-offset, 0px))";
66 }
67
68 $styles[] =
69 array(
70 'selector' => $selector,
71 'declarations' => array(
72 $side => $side_value,
73 ),
74 );
75 }
76 }
77
78 $styles[] =
79 array(
80 'selector' => $selector,
81 'declarations' => array(
82 'position' => $position_type,
83 'z-index' => '10', // TODO: Replace hard-coded z-index value with a z-index preset approach in theme.json.
84 ),
85 );
86
87 return $styles;
88 }
89
90 /**
91 * Renders position styles to the block wrapper.
92 *
93 * @since 6.2.0
94 * @since 7.2.0 Added support for viewport states.
95 *
96 * @param string $block_content Rendered block content.
97 * @param array $block Block object.
98 * @return string Filtered block content.
99 */
100 function gutenberg_render_position_support( $block_content, $block ) {
101 $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
102 $has_position_support = block_has_support( $block_type, array( 'position' ), false );
103
104 $style_attribute = $block['attrs']['style'] ?? null;
105
106 if ( ! $has_position_support || ! is_array( $style_attribute ) ) {
107 return $block_content;
108 }
109
110 /*
111 * Position styles can exist in either the default state or a viewport state.
112 */
113 $has_position_style = ! empty( $style_attribute['position'] );
114 if ( ! $has_position_style ) {
115 foreach ( $style_attribute as $key => $style ) {
116 if (
117 is_string( $key ) &&
118 str_starts_with( $key, '@' ) &&
119 is_array( $style ) &&
120 ! empty( $style['position'] )
121 ) {
122 $has_position_style = true;
123 break;
124 }
125 }
126 }
127
128 if ( ! $has_position_style ) {
129 return $block_content;
130 }
131
132 $global_settings = gutenberg_get_global_settings();
133 $theme_has_sticky_support = $global_settings['position']['sticky'] ?? false;
134 $theme_has_fixed_support = $global_settings['position']['fixed'] ?? false;
135
136 // Only allow output for position types that the theme supports.
137 $allowed_position_types = array();
138 if ( true === $theme_has_sticky_support ) {
139 $allowed_position_types[] = 'sticky';
140 }
141 if ( true === $theme_has_fixed_support ) {
142 $allowed_position_types[] = 'fixed';
143 }
144
145 $viewport_settings = $global_settings['viewport'] ?? null;
146 $responsive_media_queries = WP_Theme_JSON_Gutenberg::get_viewport_media_queries( $viewport_settings );
147
148 $class_name = wp_unique_id( 'wp-container-' );
149 $selector = ".$class_name";
150 $position_styles = array();
151 $wrapper_classes = array();
152 $base_position = $style_attribute['position'] ?? null;
153
154 // Default viewport (base) position styles.
155 $base_styles = gutenberg_get_position_support_styles(
156 $selector,
157 $base_position,
158 $allowed_position_types
159 );
160
161 if ( ! empty( $base_styles ) ) {
162 $position_styles = $base_styles;
163 $wrapper_classes[] = 'is-position-' . $base_position['type'];
164 }
165
166 /*
167 * Responsive viewport state styles. A viewport state inherits any values
168 * it does not set from the default state, so a state that only overrides
169 * e.g. the `top` offset keeps the base position type.
170 */
171 foreach ( $responsive_media_queries as $breakpoint => $media_query ) {
172 $viewport_position_style = $style_attribute[ $breakpoint ]['position'] ?? null;
173
174 if ( empty( $viewport_position_style ) || ! is_array( $viewport_position_style ) ) {
175 continue;
176 }
177
178 $viewport_position = is_array( $base_position )
179 ? array_replace( $base_position, $viewport_position_style )
180 : $viewport_position_style;
181
182 $viewport_styles = gutenberg_get_position_support_styles(
183 $selector,
184 $viewport_position,
185 $allowed_position_types
186 );
187
188 if ( ! empty( $viewport_styles ) ) {
189 $wrapper_classes[] = 'is-position-' . $viewport_position['type'];
190 } elseif ( ! empty( $base_styles ) ) {
191 /*
192 * The viewport state can explicitly clear the position type inherited from the
193 * default state.
194 */
195 $viewport_styles = array(
196 array(
197 'selector' => $selector,
198 'declarations' => array( 'position' => 'static' ),
199 ),
200 );
201 } else {
202 continue;
203 }
204
205 foreach ( $viewport_styles as $index => $rule ) {
206 $viewport_styles[ $index ]['rules_group'] = $media_query;
207 }
208
209 $position_styles = array_merge( $position_styles, $viewport_styles );
210 }
211
212 if ( ! empty( $position_styles ) ) {
213 /*
214 * Add to the style engine store to enqueue and render position styles.
215 */
216 gutenberg_style_engine_get_stylesheet_from_css_rules(
217 $position_styles,
218 array(
219 'context' => 'block-supports',
220 'prettify' => false,
221 )
222 );
223
224 // Inject class name to block container markup.
225 $content = new WP_HTML_Tag_Processor( $block_content );
226 $content->next_tag();
227 $content->add_class( $class_name );
228 foreach ( array_unique( $wrapper_classes ) as $class ) {
229 $content->add_class( $class );
230 }
231 return (string) $content;
232 }
233
234 return $block_content;
235 }
236
237 // Register the block support. (overrides core one).
238 WP_Block_Supports::get_instance()->register(
239 'position',
240 array(
241 'register_attribute' => 'gutenberg_register_position_support',
242 )
243 );
244
245 if ( function_exists( 'wp_render_position_support' ) ) {
246 remove_filter( 'render_block', 'wp_render_position_support' );
247 }
248 add_filter( 'render_block', 'gutenberg_render_position_support', 10, 2 );
249