PluginProbe
Gutenberg / 12.1.0
Gutenberg v12.1.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 12.6.0 7.4.0 All 402 releases
gutenberg / lib / block-supports / layout.php

layout.php in Gutenberg 12.1.0, at lib/block-supports/layout.php

241 lines 9.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Layout block support flag.
4 *
5 * @package gutenberg
6 */
7
8 /**
9 * Registers the layout block attribute for block types that support it.
10 *
11 * @param WP_Block_Type $block_type Block Type.
12 */
13 function gutenberg_register_layout_support( $block_type ) {
14 $support_layout = gutenberg_block_has_support( $block_type, array( '__experimentalLayout' ), false );
15 if ( $support_layout ) {
16 if ( ! $block_type->attributes ) {
17 $block_type->attributes = array();
18 }
19
20 if ( ! array_key_exists( 'layout', $block_type->attributes ) ) {
21 $block_type->attributes['layout'] = array(
22 'type' => 'object',
23 );
24 }
25 }
26 }
27
28 /**
29 * Generates the CSS corresponding to the provided layout.
30 *
31 * @param string $selector CSS selector.
32 * @param array $layout Layout object. The one that is passed has already checked the existance of default block layout.
33 * @param boolean $has_block_gap_support Whether the theme has support for the block gap.
34 *
35 * @return string CSS style.
36 */
37 function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support = false ) {
38 $layout_type = isset( $layout['type'] ) ? $layout['type'] : 'default';
39
40 $style = '';
41 if ( 'default' === $layout_type ) {
42 $content_size = isset( $layout['contentSize'] ) ? $layout['contentSize'] : null;
43 $wide_size = isset( $layout['wideSize'] ) ? $layout['wideSize'] : null;
44
45 $all_max_width_value = $content_size ? $content_size : $wide_size;
46 $wide_max_width_value = $wide_size ? $wide_size : $content_size;
47
48 // Make sure there is a single CSS rule, and all tags are stripped for security.
49 // TODO: Use `safecss_filter_attr` instead - once https://core.trac.wordpress.org/ticket/46197 is patched.
50 $all_max_width_value = wp_strip_all_tags( explode( ';', $all_max_width_value )[0] );
51 $wide_max_width_value = wp_strip_all_tags( explode( ';', $wide_max_width_value )[0] );
52
53 $style = '';
54 if ( $content_size || $wide_size ) {
55 $style = "$selector > * {";
56 $style .= 'max-width: ' . esc_html( $all_max_width_value ) . ';';
57 $style .= 'margin-left: auto !important;';
58 $style .= 'margin-right: auto !important;';
59 $style .= '}';
60
61 $style .= "$selector > .alignwide { max-width: " . esc_html( $wide_max_width_value ) . ';}';
62 $style .= "$selector .alignfull { max-width: none; }";
63 }
64
65 $style .= "$selector .alignleft { float: left; margin-right: 2em; }";
66 $style .= "$selector .alignright { float: right; margin-left: 2em; }";
67 if ( $has_block_gap_support ) {
68 $style .= "$selector > * { margin-top: 0; margin-bottom: 0; }";
69 $style .= "$selector > * + * { margin-top: var( --wp--style--block-gap ); margin-bottom: 0; }";
70 }
71 } elseif ( 'flex' === $layout_type ) {
72 $layout_orientation = isset( $layout['orientation'] ) ? $layout['orientation'] : 'horizontal';
73
74 $justify_content_options = array(
75 'left' => 'flex-start',
76 'right' => 'flex-end',
77 'center' => 'center',
78 );
79
80 if ( 'horizontal' === $layout_orientation ) {
81 $justify_content_options += array( 'space-between' => 'space-between' );
82 }
83
84 $flex_wrap_options = array( 'wrap', 'nowrap' );
85 $flex_wrap = ! empty( $layout['flexWrap'] ) && in_array( $layout['flexWrap'], $flex_wrap_options, true ) ?
86 $layout['flexWrap'] :
87 'wrap';
88
89 $style = "$selector {";
90 $style .= 'display: flex;';
91 if ( $has_block_gap_support ) {
92 $style .= 'gap: var( --wp--style--block-gap, 0.5em );';
93 } else {
94 $style .= 'gap: 0.5em;';
95 }
96 $style .= "flex-wrap: $flex_wrap;";
97 if ( 'horizontal' === $layout_orientation ) {
98 $style .= 'align-items: center;';
99 /**
100 * Add this style only if is not empty for backwards compatibility,
101 * since we intend to convert blocks that had flex layout implemented
102 * by custom css.
103 */
104 if ( ! empty( $layout['justifyContent'] ) && array_key_exists( $layout['justifyContent'], $justify_content_options ) ) {
105 $style .= "justify-content: {$justify_content_options[ $layout['justifyContent'] ]};";
106 if ( ! empty( $layout['setCascadingProperties'] ) && $layout['setCascadingProperties'] ) {
107 // --layout-justification-setting allows children to inherit the value regardless or row or column direction.
108 $style .= "--layout-justification-setting: {$justify_content_options[ $layout['justifyContent'] ]};";
109 $style .= '--layout-direction: row;';
110 $style .= "--layout-wrap: $flex_wrap;";
111 $style .= "--layout-justify: {$justify_content_options[ $layout['justifyContent'] ]};";
112 $style .= '--layout-align: center;';
113 }
114 }
115 } else {
116 $style .= 'flex-direction: column;';
117 if ( ! empty( $layout['justifyContent'] ) && array_key_exists( $layout['justifyContent'], $justify_content_options ) ) {
118 $style .= "align-items: {$justify_content_options[ $layout['justifyContent'] ]};";
119 if ( ! empty( $layout['setCascadingProperties'] ) && $layout['setCascadingProperties'] ) {
120 // --layout-justification-setting allows children to inherit the value regardless or row or column direction.
121 $style .= "--layout-justification-setting: {$justify_content_options[ $layout['justifyContent'] ]};";
122 $style .= '--layout-direction: column;';
123 $style .= '--layout-justify: initial;';
124 $style .= "--layout-align: {$justify_content_options[ $layout['justifyContent'] ]};";
125 }
126 }
127 }
128 $style .= '}';
129
130 $style .= "$selector > * { margin: 0; }";
131 }
132
133 return $style;
134 }
135
136 /**
137 * Renders the layout config to the block wrapper.
138 *
139 * @param string $block_content Rendered block content.
140 * @param array $block Block object.
141 * @return string Filtered block content.
142 */
143 function gutenberg_render_layout_support_flag( $block_content, $block ) {
144 $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
145 $support_layout = gutenberg_block_has_support( $block_type, array( '__experimentalLayout' ), false );
146
147 if ( ! $support_layout ) {
148 return $block_content;
149 }
150
151 $block_gap = wp_get_global_settings( array( 'spacing', 'blockGap' ) );
152 $default_layout = wp_get_global_settings( array( 'layout' ) );
153 $has_block_gap_support = isset( $block_gap ) ? null !== $block_gap : false;
154 $default_block_layout = _wp_array_get( $block_type->supports, array( '__experimentalLayout', 'default' ), array() );
155 $used_layout = isset( $block['attrs']['layout'] ) ? $block['attrs']['layout'] : $default_block_layout;
156 if ( isset( $used_layout['inherit'] ) && $used_layout['inherit'] ) {
157 if ( ! $default_layout ) {
158 return $block_content;
159 }
160 $used_layout = $default_layout;
161 }
162
163 $id = uniqid();
164 $style = gutenberg_get_layout_style( ".wp-container-$id", $used_layout, $has_block_gap_support );
165 // This assumes the hook only applies to blocks with a single wrapper.
166 // I think this is a reasonable limitation for that particular hook.
167 $content = preg_replace(
168 '/' . preg_quote( 'class="', '/' ) . '/',
169 'class="wp-container-' . $id . ' ',
170 $block_content,
171 1
172 );
173
174 // Ideally styles should be loaded in the head, but blocks may be parsed
175 // after that, so loading in the footer for now.
176 // See https://core.trac.wordpress.org/ticket/53494.
177 add_action(
178 'wp_footer',
179 function () use ( $style ) {
180 echo '<style>' . $style . '</style>';
181 }
182 );
183
184 return $content;
185 }
186
187 // Register the block support. (overrides core one).
188 WP_Block_Supports::get_instance()->register(
189 'layout',
190 array(
191 'register_attribute' => 'gutenberg_register_layout_support',
192 )
193 );
194 if ( function_exists( 'wp_render_layout_support_flag' ) ) {
195 remove_filter( 'render_block', 'wp_render_layout_support_flag' );
196 }
197 add_filter( 'render_block', 'gutenberg_render_layout_support_flag', 10, 2 );
198
199 /**
200 * For themes without theme.json file, make sure
201 * to restore the inner div for the group block
202 * to avoid breaking styles relying on that div.
203 *
204 * @param string $block_content Rendered block content.
205 * @param array $block Block object.
206 * @return string Filtered block content.
207 */
208 function gutenberg_restore_group_inner_container( $block_content, $block ) {
209 $tag_name = isset( $block['attrs']['tagName'] ) ? $block['attrs']['tagName'] : 'div';
210 $group_with_inner_container_regex = sprintf(
211 '/(^\s*<%1$s\b[^>]*wp-block-group(\s|")[^>]*>)(\s*<div\b[^>]*wp-block-group__inner-container(\s|")[^>]*>)((.|\S|\s)*)/U',
212 preg_quote( $tag_name, '/' )
213 );
214 if (
215 'core/group' !== $block['blockName'] ||
216 WP_Theme_JSON_Resolver_Gutenberg::theme_has_support() ||
217 1 === preg_match( $group_with_inner_container_regex, $block_content ) ||
218 ( isset( $block['attrs']['layout']['type'] ) && 'default' !== $block['attrs']['layout']['type'] )
219 ) {
220 return $block_content;
221 }
222
223 $replace_regex = sprintf(
224 '/(^\s*<%1$s\b[^>]*wp-block-group[^>]*>)(.*)(<\/%1$s>\s*$)/ms',
225 preg_quote( $tag_name, '/' )
226 );
227 $updated_content = preg_replace_callback(
228 $replace_regex,
229 function( $matches ) {
230 return $matches[1] . '<div class="wp-block-group__inner-container">' . $matches[2] . '</div>' . $matches[3];
231 },
232 $block_content
233 );
234 return $updated_content;
235 }
236
237 if ( function_exists( 'wp_restore_group_inner_container' ) ) {
238 remove_filter( 'render_block', 'wp_restore_group_inner_container', 10, 2 );
239 }
240 add_filter( 'render_block', 'gutenberg_restore_group_inner_container', 10, 2 );
241