PluginProbe
Gutenberg / 22.3.0
Gutenberg v22.3.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 / build / scripts / block-library / tabs.php

tabs.php in Gutenberg 22.3.0, at build/scripts/block-library/tabs.php

239 lines 7.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Tabs Block
4 *
5 * @package WordPress
6 */
7
8 /**
9 * Build inline CSS custom properties for color settings.
10 *
11 * @param array $attributes Block attributes.
12 *
13 * @return string Inline CSS string.
14 */
15 function gutenberg_block_core_tabs_generate_color_styles( array $attributes ): string {
16 $tab_inactive = $attributes['customTabInactiveColor'] ?? '';
17 $tab_hover = $attributes['customTabHoverColor'] ?? '';
18 $tab_active = $attributes['customTabActiveColor'] ?? '';
19 $tab_text = $attributes['customTabTextColor'] ?? '';
20 $hover_text = $attributes['customTabHoverTextColor'] ?? '';
21 $active_text = $attributes['customTabActiveTextColor'] ?? '';
22
23 $styles = array(
24 '--custom-tab-inactive-color' => $tab_inactive,
25 '--custom-tab-hover-color' => $tab_hover,
26 '--custom-tab-active-color' => $tab_active,
27 '--custom-tab-text-color' => $tab_text,
28 '--custom-tab-hover-text-color' => $hover_text,
29 '--custom-tab-active-text-color' => $active_text,
30 );
31
32 $style_string = array_map(
33 static function ( string $key, string $value ): string {
34 return ! empty( $value ) ? $key . ': ' . $value . ';' : '';
35 },
36 array_keys( $styles ),
37 $styles
38 );
39
40 return implode( ' ', array_filter( $style_string ) );
41 }
42
43 /**
44 * Build inline CSS custom properties for gap settings.
45 *
46 * @param array $attributes Block attributes.
47 * @param bool $is_vertical Whether the tabs are vertical.
48 *
49 * @return string Inline CSS string.
50 */
51 function gutenberg_block_core_tabs_generate_gap_styles( array $attributes, bool $is_vertical ): string {
52 if ( empty( $attributes['style'] ) || ! is_array( $attributes['style'] ) ) {
53 return '--wp--style--tabs-gap-default: 0.5em;';
54 }
55 if ( empty( $attributes['style']['spacing'] ) || ! is_array( $attributes['style']['spacing'] ) ) {
56 return '--wp--style--tabs-gap-default: 0.5em;';
57 }
58 if ( ! array_key_exists( 'blockGap', $attributes['style']['spacing'] ) ) {
59 return '--wp--style--tabs-gap-default: 0.5em;';
60 }
61
62 $block_gap = $attributes['style']['spacing']['blockGap'];
63
64 if ( is_array( $block_gap ) ) {
65 if ( array_key_exists( 'left', $block_gap ) && array_key_exists( 'top', $block_gap ) ) {
66 $block_gap_horizontal = $block_gap['left'];
67 $block_gap_vertical = $block_gap['top'];
68 } elseif ( array_key_exists( 'left', $block_gap ) ) {
69 $block_gap_horizontal = $block_gap['left'];
70 $block_gap_vertical = '0.5em';
71 } elseif ( array_key_exists( 'top', $block_gap ) ) {
72 $block_gap_horizontal = '0.5em';
73 $block_gap_vertical = $block_gap['top'];
74 } else {
75 return '--wp--style--tabs-gap-default: 0.5em;';
76 }
77 } elseif ( is_string( $block_gap ) ) {
78 return '--wp--style--tabs-gap-default: 0.5em;';
79 }
80
81 $block_gap_horizontal = preg_match( '/^var:preset\|spacing\|\d+$/', (string) $block_gap_horizontal )
82 ? 'var(--wp--preset--spacing--' . substr( (string) $block_gap_horizontal, strrpos( (string) $block_gap_horizontal, '|' ) + 1 ) . ')'
83 : (string) $block_gap_horizontal;
84
85 $block_gap_vertical = preg_match( '/^var:preset\|spacing\|\d+$/', (string) $block_gap_vertical )
86 ? 'var(--wp--preset--spacing--' . substr( (string) $block_gap_vertical, strrpos( (string) $block_gap_vertical, '|' ) + 1 ) . ')'
87 : (string) $block_gap_vertical;
88
89 $list_gap = $block_gap_horizontal;
90 $block_gap = $block_gap_vertical;
91
92 if ( $is_vertical ) {
93 $list_gap = $block_gap_vertical;
94 $block_gap = $block_gap_horizontal;
95 }
96
97 return wp_sprintf(
98 '--wp--style--unstable-tabs-list-gap: %s; --wp--style--unstable-tabs-gap: %s;',
99 $list_gap,
100 $block_gap
101 );
102 }
103
104 /**
105 * Extract tabs list from inner blocks for hydration.
106 *
107 * @param array $innerblocks Parsed inner blocks.
108 *
109 * @return array List of tabs with id, label, index.
110 */
111 function gutenberg_block_core_tabs_generate_tabs_list_from_innerblocks( array $innerblocks = array() ): array {
112 $tab_index = 0;
113
114 return array_map(
115 static function ( array $tab ) use ( &$tab_index ): array {
116 $attrs = $tab['attrs'] ?? array();
117
118 $tag_processor = new WP_HTML_Tag_Processor( $tab['innerHTML'] ?? '' );
119 $tag_processor->next_tag( array( 'class_name' => 'wp-block-tab' ) );
120
121 $tab_id = $tag_processor->get_attribute( 'id' );
122 $tab_label = $attrs['label'] ?? '';
123
124 $attrs['id'] = $tab_id;
125 $attrs['label'] = esc_html( (string) $tab_label );
126
127 $tab_index++;
128
129 return $attrs;
130 },
131 $innerblocks
132 );
133 }
134
135 /**
136 * Render callback for core/tabs.
137 *
138 * @param array $attributes Block attributes.
139 * @param string $content Block content.
140 * @param \WP_Block $block WP_Block instance.
141 *
142 * @return string Updated HTML.
143 */
144 function gutenberg_block_core_tabs_render_block_callback( array $attributes, string $content, \WP_Block $block ): string {
145 $active_tab_index = $attributes['activeTabIndex'] ?? 0;
146
147 $tabs_list = gutenberg_block_core_tabs_generate_tabs_list_from_innerblocks( $block->parsed_block['innerBlocks'] ?? array() );
148
149 $tabs_id = wp_unique_id( 'tabs_' );
150
151 /**
152 * Builds a client side state for just this tabs instance.
153 * This allows 3rd party extensibility of tabs while retaining
154 * client side state management per core/tabs instance, like context.
155 */
156 wp_interactivity_state(
157 'core/tabs/private',
158 array(
159 $tabs_id => $tabs_list,
160 )
161 );
162
163 $is_vertical = 'vertical' === ( $attributes['orientation'] ?? 'horizontal' );
164
165 $tag_processor = new WP_HTML_Tag_Processor( $content );
166 $tag_processor->next_tag( array( 'class_name' => 'wp-block-tabs' ) );
167 $tag_processor->add_class( $is_vertical ? 'is-vertical' : 'is-horizontal' );
168 $tag_processor->set_attribute( 'data-wp-interactive', 'core/tabs/private' );
169 $tag_processor->set_attribute(
170 'data-wp-context',
171 wp_json_encode(
172 array(
173 'tabsId' => $tabs_id,
174 'activeTabIndex' => $active_tab_index,
175 'isVertical' => $is_vertical,
176 )
177 )
178 );
179 $tag_processor->set_attribute( 'data-wp-init', 'callbacks.onTabsInit' );
180 $tag_processor->set_attribute( 'data-wp-on--keydown', 'actions.handleTabKeyDown' );
181
182 /**
183 * Process style attribute.
184 */
185 $style = (string) $tag_processor->get_attribute( 'style' );
186 $style .= gutenberg_block_core_tabs_generate_color_styles( $attributes );
187 $style .= gutenberg_block_core_tabs_generate_gap_styles( $attributes, $is_vertical );
188 $tag_processor->set_attribute( 'style', $style );
189
190 $updated_content = $tag_processor->get_updated_html();
191
192 /**
193 * Build the tabs list markup.
194 * We're doing this manually instead of using <template/> to make it possible
195 * for other blocks to extend the tabs list via HTML api.
196 */
197 $tabs_list_markup = array_map(
198 static function ( array $tab ): string {
199 return wp_sprintf(
200 '<a id="tab__%1$s" class="tabs__tab-label" href="#%1$s" role="tab" aria-controls="%1$s" data-wp-on--click="actions.handleTabClick" data-wp-on--keydown="actions.handleTabKeyDown" data-wp-bind--aria-selected="state.isActiveTab" data-wp-bind--tabindex="state.tabIndexAttribute">%2$s</a>',
201 $tab['id'],
202 html_entity_decode( $tab['label'] )
203 );
204 },
205 $tabs_list
206 );
207 $tabs_list_markup = implode( '', $tabs_list_markup );
208
209 /**
210 * Splice the tabs list into the content.
211 */
212 $content = preg_replace(
213 '/<ul\s+class="tabs__list">\s*<\/ul>/i',
214 '<div class="tabs__list" role="tablist">' . $tabs_list_markup . '</div>',
215 (string) $updated_content
216 );
217
218 /**
219 * In the event preg_replace fails, return the tabs content without the list spliced in.
220 * This ensures the block content is still rendered, albeit without the tabs list.
221 */
222 return is_string( $content ) ? $content : (string) $updated_content;
223 }
224
225 /**
226 * Registers the `core/tabs` block on the server.
227 *
228 * @since 6.8.0
229 */
230 function gutenberg_register_block_core_tabs() {
231 register_block_type_from_metadata(
232 __DIR__ . '/tabs',
233 array(
234 'render_callback' => 'gutenberg_block_core_tabs_render_block_callback',
235 )
236 );
237 }
238 add_action( 'init', 'gutenberg_register_block_core_tabs', 20 );
239