PluginProbe
Gutenberg / trunk
Gutenberg vtrunk
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
← All changes | lib/block-supports/position.php +148 -48 23.5.1 → trunk View file →
@@ -25,25 +25,111 @@
25 25 }
26 26 }
27 27
28 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 +/**
29 91 * Renders position styles to the block wrapper.
30 92 *
31 - * @param string $block_content Rendered block content.
32 - * @param array $block Block object.
33 - * @return string Filtered block content.
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.
34 99 */
35 100 function gutenberg_render_position_support( $block_content, $block ) {
36 101 $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
37 102 $has_position_support = block_has_support( $block_type, array( 'position' ), false );
38 103
39 - if (
40 - ! $has_position_support ||
41 - empty( $block['attrs']['style']['position'] )
42 - ) {
104 + $style_attribute = $block['attrs']['style'] ?? null;
105 +
106 + if ( ! $has_position_support || ! is_array( $style_attribute ) ) {
43 107 return $block_content;
44 108 }
45 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 +
46 132 $global_settings = gutenberg_get_global_settings();
47 133 $theme_has_sticky_support = $global_settings['position']['sticky'] ?? false;
48 134 $theme_has_fixed_support = $global_settings['position']['fixed'] ?? false;
49 135
@@ -55,60 +141,73 @@
55 141 if ( true === $theme_has_fixed_support ) {
56 142 $allowed_position_types[] = 'fixed';
57 143 }
58 144
59 - $style_attribute = $block['attrs']['style'] ?? null;
145 + $viewport_settings = $global_settings['viewport'] ?? null;
146 + $responsive_media_queries = WP_Theme_JSON_Gutenberg::get_viewport_media_queries( $viewport_settings );
147 +
60 148 $class_name = wp_unique_id( 'wp-container-' );
61 149 $selector = ".$class_name";
62 150 $position_styles = array();
63 - $position_type = $style_attribute['position']['type'] ?? '';
64 151 $wrapper_classes = array();
152 + $base_position = $style_attribute['position'] ?? null;
65 153
66 - if (
67 - in_array( $position_type, $allowed_position_types, true )
68 - ) {
69 - $wrapper_classes[] = $class_name;
70 - $wrapper_classes[] = 'is-position-' . $position_type;
71 - $sides = array( 'top', 'right', 'bottom', 'left' );
154 + // Default viewport (base) position styles.
155 + $base_styles = gutenberg_get_position_support_styles(
156 + $selector,
157 + $base_position,
158 + $allowed_position_types
159 + );
72 160
73 - foreach ( $sides as $side ) {
74 - $side_value = $style_attribute['position'][ $side ] ?? null;
75 - if ( null !== $side_value ) {
76 - /*
77 - * For fixed or sticky top positions,
78 - * ensure the value includes an offset for the logged in admin bar.
79 - */
80 - if (
81 - 'top' === $side &&
82 - ( 'fixed' === $position_type || 'sticky' === $position_type )
83 - ) {
84 - // Ensure 0 values can be used in `calc()` calculations.
85 - if ( '0' === $side_value || 0 === $side_value ) {
86 - $side_value = '0px';
87 - }
161 + if ( ! empty( $base_styles ) ) {
162 + $position_styles = $base_styles;
163 + $wrapper_classes[] = 'is-position-' . $base_position['type'];
164 + }
88 165
89 - // Ensure current side value also factors in the height of the logged in admin bar.
90 - $side_value = "calc($side_value + var(--wp-admin--admin-bar--position-offset, 0px))";
91 - }
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;
92 173
93 - $position_styles[] =
94 - array(
95 - 'selector' => $selector,
96 - 'declarations' => array(
97 - $side => $side_value,
98 - ),
99 - );
100 - }
174 + if ( empty( $viewport_position_style ) || ! is_array( $viewport_position_style ) ) {
175 + continue;
101 176 }
102 177
103 - $position_styles[] =
104 - array(
105 - 'selector' => $selector,
106 - 'declarations' => array(
107 - 'position' => $position_type,
108 - 'z-index' => '10', // TODO: Replace hard-coded z-index value with a z-index preset approach in theme.json.
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' ),
109 199 ),
110 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 );
111 210 }
112 211
113 212 if ( ! empty( $position_styles ) ) {
114 213 /*
@@ -124,9 +223,10 @@
124 223
125 224 // Inject class name to block container markup.
126 225 $content = new WP_HTML_Tag_Processor( $block_content );
127 226 $content->next_tag();
128 - foreach ( $wrapper_classes as $class ) {
227 + $content->add_class( $class_name );
228 + foreach ( array_unique( $wrapper_classes ) as $class ) {
129 229 $content->add_class( $class );
130 230 }
131 231 return (string) $content;
132 232 }