PluginProbe
Gutenberg / 12.6.0
Gutenberg v12.6.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.6.0, at lib/block-supports/layout.php

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