PluginProbe
Content Blocks Builder – Create blocks, repeater blocks with carousel, grid, popup layouts / 2.8.5
Content Blocks Builder – Create blocks, repeater blocks with carousel, grid, popup layouts v2.8.5
2.8.14 2.8.13 2.8.12 2.7.5 2.7.6 2.7.7 2.7.8 2.7.9 2.8.0 2.8.1 2.8.10 2.8.11 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.8.7 2.8.8 2.8.9 trunk 1.0.1 1.0.2 1.1.0 1.1.1 All 147 releases
content-blocks-builder / includes / custom-style.php

custom-style.php in Content Blocks Builder – Create blocks, repeater blocks with carousel, grid, popup layouts 2.8.5, at includes/custom-style.php

3,961 lines 130.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * The custom style component
4 *
5 * @package BoldBlocks
6 * @author Phi Phan <mrphipv@gmail.com>
7 * @copyright Copyright (c) 2022, Phi Phan
8 */
9
10 namespace BoldBlocks;
11
12 // Exit if accessed directly.
13 defined( 'ABSPATH' ) || exit;
14
15 if ( ! class_exists( CustomStyle::class ) ) :
16 /**
17 * The controller class for style.
18 */
19 class CustomStyle extends CoreComponent {
20 /**
21 * The style handle for feature styles
22 *
23 * @var array
24 */
25 private $feature_style_handle = 'cbb-dynamic-style';
26
27 /**
28 * Store the context of styles
29 *
30 * @var array
31 */
32 private $style_contexts = [
33 'loops' => [],
34 'loop' => '',
35 'post' => 0,
36 'counter' => 0,
37 'content' => false,
38 'content_counter' => 0,
39 ];
40
41 /**
42 * Store the ID of query loop with carousel or grid layout
43 *
44 * @var array
45 */
46 private $query_ids = [];
47
48 /**
49 * The cached styles
50 *
51 * @var array
52 */
53 private $styles = [];
54
55 /**
56 * The refined breakpoints
57 *
58 * @var array
59 */
60 private $breakpoints = [];
61
62 /**
63 * The list of non-cbb blocks that support dynamic style
64 *
65 * @var array
66 */
67 public $non_cbb_blocks = [ 'core/template-part' ];
68
69 /**
70 * Run main hooks
71 *
72 * @return void
73 */
74 public function run() {
75 // Register setting fields.
76 add_action( 'init', [ $this, 'register_setting_fields' ] );
77
78 // Register style handle.
79 add_action( 'init', [ $this, 'register_feature_style' ] );
80
81 // Render block style.
82 add_filter( 'render_block', [ $this, 'render_block_style' ], 10, 3 );
83
84 // Render link to post first.
85 add_filter( 'render_block', [ $this, 'build_block_link_to_post' ], 20, 3 );
86
87 // Build dynamic block overlay.
88 add_filter( 'render_block', [ $this, 'build_block_overlay' ], 20, 3 );
89
90 // Build dynamic block background.
91 add_filter( 'render_block', [ $this, 'build_block_background' ], 30, 3 );
92
93 // Build animations.
94 add_filter( 'render_block', [ $this, 'build_block_animations' ], 30, 3 );
95
96 // Render accordions.
97 add_filter( 'render_block_data', [ $this, 'render_accordion_block_data' ] );
98
99 // Render accordion item blocks.
100 add_filter( 'render_block', [ $this, 'render_accordion_item_block' ], 10, 3 );
101
102 // Render grid style for the query loop block.
103 add_filter( 'render_block_core/query', [ $this, 'render_query_loop_grid_style' ], 10, 3 );
104
105 // Render carousel settings for the query loop block.
106 add_filter( 'render_block_core/query', [ $this, 'render_query_loop_carousel_layout' ], 10, 3 );
107
108 // Enqueue block styles.
109 add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_responsive_styles' ] );
110
111 // Enqueue breakpoints.
112 add_action( 'init', [ $this, 'enqueue_reponsive_breakpoints' ] );
113
114 // Update loop id.
115 add_filter( 'render_block_context', [ $this, 'update_loop_id' ], 10, 2 );
116
117 // Reset loop id.
118 add_filter( 'render_block', [ $this, 'reset_loop_id' ], 10, 2 );
119
120 // Render preload style for carousels.
121 add_filter( 'cbb_get_block_style', [ $this, 'get_carousel_preload_style' ], 10, 2 );
122
123 // Support dynamic style for none-cbb blocks.
124 add_filter( 'cbb_support_block_style', [ $this, 'support_block_style' ], 10, 2 );
125
126 // Set a block layout for non-cbb blocks.
127 add_filter( 'cbb_get_block_layout', [ $this, 'get_block_layout' ], 10, 2 );
128
129 // Render sticky data for the core/template-part block.
130 add_filter( 'render_block_core/template-part', [ $this, 'render_template_part_sticky' ], 10, 3 );
131
132 // Render settings for carousels.
133 add_filter( 'render_block', [ $this, 'render_carousel_settings' ], 30, 3 );
134
135 // Migrate CBB class for old blocks.
136 add_filter( 'render_block', [ $this, 'migrate_cbb_class' ], 30, 3 );
137 }
138
139 /**
140 * Register custom setting fields
141 *
142 * @return void
143 */
144 public function register_setting_fields() {
145 // phpcs:ignore PluginCheck.CodeAnalysis.SettingSanitization.register_settingDynamic
146 register_setting(
147 'cbb',
148 'cbb_breakpoints',
149 [
150 'type' => 'array',
151 'show_in_rest' => [
152 'name' => 'CBBBreakpoints',
153 'schema' => [
154 'items' => [
155 'type' => 'object',
156 'properties' => [
157 'breakpoint' => [
158 'type' => 'number',
159 ],
160 'prefix' => [
161 'type' => 'string',
162 ],
163 ],
164 'additionalProperties' => [
165 'type' => 'string',
166 ],
167 ],
168 ],
169 ],
170 'default' => [
171 [
172 'breakpoint' => 576,
173 'prefix' => 'sm',
174 ],
175 [
176 'breakpoint' => 768,
177 'prefix' => 'md',
178 ],
179 [
180 'breakpoint' => 1024,
181 'prefix' => 'lg',
182 ],
183 ],
184 ]
185 );
186 }
187
188 /**
189 * Enqueue responsive breakpoints
190 *
191 * @return void
192 */
193 public function enqueue_reponsive_breakpoints() {
194 // Custom blocks handle.
195 $custom_blocks_handler = $this->the_plugin_instance->get_component( CustomBlocks::class );
196
197 // Get breakpoint setting.
198 $breakpoints = $this->get_breakpoints();
199
200 // Get breakpoint scripts.
201 $breakpoint_script = 'var CBBBreakpoints=' . wp_json_encode( $breakpoints );
202
203 // Load JS breakpoints.
204 wp_add_inline_script( $custom_blocks_handler->custom_blocks_handle, $breakpoint_script, 'before' );
205 wp_add_inline_script( $custom_blocks_handler->carousel_blocks_frontend_handle, $breakpoint_script, 'before' );
206 }
207
208 /**
209 * Register a handle for enqueue block styles.
210 *
211 * @return void
212 */
213 public function register_feature_style() {
214 // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
215 wp_register_style( $this->feature_style_handle, '' );
216 }
217
218 /**
219 * Enqueue all feature styles
220 *
221 * @return void
222 */
223 public function enqueue_responsive_styles() {
224 if ( wp_is_block_theme() ) {
225 wp_enqueue_style( $this->feature_style_handle );
226 }
227 wp_add_inline_style( $this->feature_style_handle, $this->get_responsive_styles() );
228 }
229
230 /**
231 * Build responsive styles
232 *
233 * @return string
234 */
235 private function get_responsive_styles() {
236 $style = '';
237
238 $breakpoints = $this->get_breakpoints();
239 $md_breakpoint = absint( $breakpoints['md']['breakpoint'] ?? 768 );
240 $md_breakpoint_max = $md_breakpoint - 1;
241 $lg_breakpoint = absint( $breakpoints['lg']['breakpoint'] ?? 1024 );
242 $lg_breakpoint_max = $lg_breakpoint - 1;
243 $md_start = "@media(min-width:{$md_breakpoint}px){";
244 $lg_start = "@media(min-width:{$lg_breakpoint}px){";
245 $end = '}';
246
247 // Padding.
248 $padding_style = '.sm-cbb-padding-top{padding-top:var(--cbb--padding-top) !important;}.sm-cbb-padding-right{padding-right:var(--cbb--padding-right) !important;}.sm-cbb-padding-bottom{padding-bottom:var(--cbb--padding-bottom) !important;}.sm-cbb-padding-left{padding-left:var(--cbb--padding-left) !important;}';
249 $padding_style .= "{$md_start}.md-cbb-padding-top{padding-top:var(--cbb--padding-top) !important;}.md-cbb-padding-right{padding-right:var(--cbb--padding-right) !important;}.md-cbb-padding-bottom{padding-bottom:var(--cbb--padding-bottom) !important;}.md-cbb-padding-left{padding-left:var(--cbb--padding-left) !important;}{$end}";
250 $padding_style .= "{$lg_start}.lg-cbb-padding-top{padding-top:var(--cbb--padding-top) !important;}.lg-cbb-padding-right{padding-right:var(--cbb--padding-right) !important;}.lg-cbb-padding-bottom{padding-bottom:var(--cbb--padding-bottom) !important;}.lg-cbb-padding-left{padding-left:var(--cbb--padding-left) !important;}{$end}";
251 $style .= $padding_style;
252
253 // Margin.
254 $margin_style = '.sm-cbb-margin-top{margin-top:var(--cbb--margin-top) !important;}.sm-cbb-margin-right{margin-right:var(--cbb--margin-right) !important;}.sm-cbb-margin-bottom{margin-bottom:var(--cbb--margin-bottom) !important;}.sm-cbb-margin-left{margin-left:var(--cbb--margin-left) !important;}';
255 $margin_style .= "{$md_start}.md-cbb-margin-top{margin-top:var(--cbb--margin-top) !important;}.md-cbb-margin-right{margin-right:var(--cbb--margin-right) !important;}.md-cbb-margin-bottom{margin-bottom:var(--cbb--margin-bottom) !important;}.md-cbb-margin-left{margin-left:var(--cbb--margin-left) !important;}{$end}";
256 $margin_style .= "{$lg_start}.lg-cbb-margin-top{margin-top:var(--cbb--margin-top) !important;}.lg-cbb-margin-right{margin-right:var(--cbb--margin-right) !important;}.lg-cbb-margin-bottom{margin-bottom:var(--cbb--margin-bottom) !important;}.lg-cbb-margin-left{margin-left:var(--cbb--margin-left) !important;}{$end}";
257 $style .= $margin_style;
258
259 // Block gap.
260 $block_gap_style = '.sm-cbb-block-gap > *{margin-block-start:0;margin-block-end:0;}.sm-cbb-block-gap > * + *{margin-block-start:var(--cbb--block-gap) !important;margin-block-end:0;}';
261 $block_gap_style .= "{$md_start}.md-cbb-block-gap > *{margin-block-start:0;margin-block-end:0;}.md-cbb-block-gap > * + *{margin-block-start:var(--cbb--block-gap) !important;margin-block-end:0;}{$end}";
262 $block_gap_style .= "{$lg_start}.lg-cbb-block-gap > *{margin-block-start:0;margin-block-end:0;}.lg-cbb-block-gap > * + *{margin-block-start:var(--cbb--block-gap) !important;margin-block-end:0;}{$end}";
263 $style .= $block_gap_style;
264
265 // Border.
266 $border_style = '.sm-cbb-border-top{border-top:var(--cbb--border-top) !important;}.sm-cbb-border-right{border-right:var(--cbb--border-right) !important;}.sm-cbb-border-bottom{border-bottom:var(--cbb--border-bottom) !important;}.sm-cbb-border-left{border-left:var(--cbb--border-left) !important;}';
267 $border_style .= "{$md_start}.md-cbb-border-top{border-top:var(--cbb--border-top) !important;}.md-cbb-border-right{border-right:var(--cbb--border-right) !important;}.md-cbb-border-bottom{border-bottom:var(--cbb--border-bottom) !important;}.md-cbb-border-left{border-left:var(--cbb--border-left) !important;}{$end}";
268 $border_style .= "{$lg_start}.lg-cbb-border-top{border-top:var(--cbb--border-top) !important;}.lg-cbb-border-right{border-right:var(--cbb--border-right) !important;}.lg-cbb-border-bottom{border-bottom:var(--cbb--border-bottom) !important;}.lg-cbb-border-left{border-left:var(--cbb--border-left) !important;}{$end}";
269 $style .= $border_style;
270
271 // Border radius.
272 $radius_style = '.sm-cbb-border-radius{overflow:hidden;border-radius: var(--cbb--border-radius) !important;}';
273 $radius_style .= "{$md_start}.md-cbb-border-radius{overflow:hidden;border-radius: var(--cbb--border-radius) !important;}{$end}";
274 $radius_style .= "{$lg_start}.lg-cbb-border-radius{overflow:hidden;border-radius: var(--cbb--border-radius) !important;}{$end}";
275 $style .= $radius_style;
276
277 // Width.
278 $width_style = '.sm-cbb-width{width:var(--cbb--width) !important;}';
279 $width_style .= "{$md_start}.md-cbb-width{width:var(--cbb--width) !important;}{$end}";
280 $width_style .= "{$lg_start}.lg-cbb-width{width:var(--cbb--width) !important;}{$end}";
281 $style .= $width_style;
282
283 // Height.
284 $height_style = '.sm-cbb-height{height:var(--cbb--height) !important;}';
285 $height_style .= "{$md_start}.md-cbb-height{height:var(--cbb--height) !important;}{$end}";
286 $height_style .= "{$lg_start}.lg-cbb-height{height:var(--cbb--height) !important;}{$end}";
287 $style .= $height_style;
288
289 // Aspect ratio.
290 $aspect_ratio_style = '.sm-cbb-aspect-ratio{aspect-ratio:var(--cbb--aspect-ratio);}';
291 $aspect_ratio_style .= "{$md_start}.md-cbb-aspect-ratio{aspect-ratio:var(--cbb--aspect-ratio);}{$end}";
292 $aspect_ratio_style .= "{$lg_start}.lg-cbb-aspect-ratio{aspect-ratio:var(--cbb--aspect-ratio);}{$end}";
293 $style .= $aspect_ratio_style;
294
295 // Text alignment.
296 $text_align_style = '.sm-cbb-text-align{text-align:var(--cbb--text-align);}';
297 $text_align_style .= "{$md_start}.md-cbb-text-align{text-align:var(--cbb--text-align);}{$end}";
298 $text_align_style .= "{$lg_start}.lg-cbb-text-align{text-align:var(--cbb--text-align);}{$end}";
299 $style .= $text_align_style;
300
301 // Vertical alignment.
302 $v_align_style = '.sm-cbb-v-align.cbb-layout-grid{display:grid;align-content:var(--cbb--v-align);}.sm-cbb-v-align.cbb-layout-grid>*{width:100%;}.sm-cbb-v-align.cbb-align-items{align-items:var(--cbb--v-align);}.sm-cbb-v-align.cbb-align-self{align-self:var(--cbb--v-align);}';
303 $v_align_style .= "{$md_start}.md-cbb-v-align.cbb-layout-grid{display:grid;align-content:var(--cbb--v-align);}.md-cbb-v-align.cbb-layout-grid>*{width:100%;}.md-cbb-v-align.cbb-align-items{align-items:var(--cbb--v-align);}.md-cbb-v-align.cbb-align-self{align-self:var(--cbb--v-align);}{$end}";
304 $v_align_style .= "{$lg_start}.lg-cbb-v-align.cbb-layout-grid{display:grid;align-content:var(--cbb--v-align);}.lg-cbb-v-align.cbb-layout-grid>*{width:100%;}.lg-cbb-v-align.cbb-align-items{align-items:var(--cbb--v-align);}.lg-cbb-v-align.cbb-align-self{align-self:var(--cbb--v-align);}{$end}";
305 $style .= $v_align_style;
306
307 // Justify alignment.
308 $h_align_style = '.sm-cbb-h-align.cbb-layout-grid{display:grid;justify-content:var(--cbb--h-align);}.sm-cbb-h-align.cbb-layout-grid>*{width:auto;}.sm-cbb-h-align.cbb-justify-items{justify-items:var(--cbb--h-align);}.sm-cbb-h-align.cbb-justify-self{justify-self:var(--cbb--h-align);}';
309 $h_align_style .= "{$md_start}.md-cbb-h-align.cbb-layout-grid{display:grid;justify-content:var(--cbb--h-align);}.md-cbb-h-align.cbb-layout-grid>*{width:auto;}.md-cbb-h-align.cbb-justify-items{justify-items:var(--cbb--h-align);}.md-cbb-h-align.cbb-justify-self{justify-self:var(--cbb--h-align);}{$end}";
310 $h_align_style .= "{$lg_start}.lg-cbb-h-align.cbb-layout-grid{display:grid;justify-content:var(--cbb--h-align);}.lg-cbb-h-align.cbb-layout-grid>*{width:auto;}.lg-cbb-h-align.cbb-justify-items{justify-items:var(--cbb--h-align);}.lg-cbb-h-align.cbb-justify-self{justify-self:var(--cbb--h-align);}{$end}";
311 $style .= $h_align_style;
312
313 // Transform.
314 $transform_style = '.sm-cbb-transform{transform:var(--cbb--transform);transform-origin:var(--cbb--transform-origin)}';
315 $transform_style .= "{$md_start}.md-cbb-transform{transform:var(--cbb--transform);transform-origin:var(--cbb--transform-origin)}{$end}";
316 $transform_style .= "{$lg_start}.lg-cbb-transform{transform:var(--cbb--transform);transform-origin:var(--cbb--transform-origin)}{$end}";
317 $style .= $transform_style;
318
319 // Visibility.
320 $visibility_style = "@media(max-width:{$md_breakpoint_max}px){.cbb-hidden-sm{display:none !important;}}";
321 $visibility_style .= "@media(min-width:{$md_breakpoint}px) and (max-width:{$lg_breakpoint_max}px){.cbb-hidden-md{display:none !important;}}";
322 $visibility_style .= "@media(min-width:{$lg_breakpoint}px){.cbb-hidden-lg{display:none !important;}}";
323 $style .= $visibility_style;
324
325 // Grid: Columns, rows, auto-rows, & gap.
326 $grid_style = '.sm-cbb-grid-columns > *,.sm-cbb-grid-columns > * + *{margin:0}.sm-cbb-grid-columns{grid-template-columns:var(--cbb--grid-columns);}.sm-cbb-grid-rows{grid-template-rows:var(--cbb--grid-rows);}.sm-cbb-grid-auto-rows{grid-auto-rows:var(--cbb--grid-auto-rows);}.sm-cbb-grid-gap-column{column-gap:var(--cbb--grid-gap-column);}.sm-cbb-grid-gap-row{row-gap:var(--cbb--grid-gap-row);}';
327 $grid_style .= "{$md_start}.md-cbb-grid-columns > * {margin:0}.md-cbb-grid-columns{grid-template-columns:var(--cbb--grid-columns);}.md-cbb-grid-rows{grid-template-rows:var(--cbb--grid-rows);}.md-cbb-grid-auto-rows{grid-auto-rows:var(--cbb--grid-auto-rows);}.md-cbb-grid-gap-column{column-gap:var(--cbb--grid-gap-column);}.md-cbb-grid-gap-row{row-gap:var(--cbb--grid-gap-row);}{$end}";
328 $grid_style .= "{$lg_start}.lg-cbb-grid-columns > * {margin:0}.lg-cbb-grid-columns{grid-template-columns:var(--cbb--grid-columns);}.lg-cbb-grid-rows{grid-template-rows:var(--cbb--grid-rows);}.lg-cbb-grid-auto-rows{grid-auto-rows:var(--cbb--grid-auto-rows);}.lg-cbb-grid-gap-column{column-gap:var(--cbb--grid-gap-column);}.lg-cbb-grid-gap-row{row-gap:var(--cbb--grid-gap-row);}{$end}";
329 $style .= $grid_style;
330
331 // Grid item: columnSpan & rowSpan.
332 $grid_item_style = '.sm-cbb-grid-item-column{grid-column:var(--cbb--grid-item-column);}.sm-cbb-grid-item-row{grid-row:var(--cbb--grid-item-row);}.sm-cbb-grid-item-order{order:var(--cbb--grid-item-order);}';
333 $grid_item_style .= "{$md_start}.md-cbb-grid-item-column{grid-column:var(--cbb--grid-item-column);}.md-cbb-grid-item-row{grid-row:var(--cbb--grid-item-row);}.md-cbb-grid-item-order{order:var(--cbb--grid-item-order);}{$end}";
334 $grid_item_style .= "{$lg_start}.lg-cbb-grid-item-column{grid-column:var(--cbb--grid-item-column);}.lg-cbb-grid-item-row{grid-row:var(--cbb--grid-item-row);}.lg-cbb-grid-item-order{order:var(--cbb--grid-item-order);}{$end}";
335 $style .= $grid_item_style;
336
337 // Accordion gap.
338 $accordion_gap_style = '.sm-cbb-accordion-gap{display:flex;flex-direction:column;gap:var(--cbb--accordion-gap);}.sm-cbb-a-has-border > .is-accordion-item{border-top:var(--cbb--ab-top);}.sm-cbb-a-no-border > .is-accordion-item{border-top:0;}';
339 $accordion_gap_style .= "{$md_start}.md-cbb-accordion-gap{display:flex;flex-direction:column;gap:var(--cbb--accordion-gap);}.md-cbb-a-has-border > .is-accordion-item{border-top:var(--cbb--ab-top);}.md-cbb-a-no-border > .is-accordion-item{border-top:0;}{$end}";
340 $accordion_gap_style .= "{$lg_start}.lg-cbb-accordion-gap{display:flex;flex-direction:column;gap:var(--cbb--accordion-gap);}.lg-cbb-a-has-border > .is-accordion-item{border-top:var(--cbb--ab-top);}.lg-cbb-a-no-border > .is-accordion-item{border-top:0;}{$end}";
341 $style .= $accordion_gap_style;
342
343 // Accordion padding.
344 // We don't need padding style, because it has a default value.
345
346 // Sticky offset.
347 $sticky_offset_style = '.is-stick-to-top.sm-cbb-sticky-offset{top: var(--cbb--sticky-offset);}.is-stick-to-bottom.sm-cbb-sticky-offset{bottom: var(--cbb--sticky-offset);}';
348 $sticky_offset_style .= "{$md_start}.is-stick-to-top.md-cbb-sticky-offset{top: var(--cbb--sticky-offset);}.is-stick-to-bottom.md-cbb-sticky-offset{bottom: var(--cbb--sticky-offset);}{$end}";
349 $sticky_offset_style .= "{$lg_start}.is-stick-to-top.lg-cbb-sticky-offset{top: var(--cbb--sticky-offset);}.is-stick-to-bottom.lg-cbb-sticky-offset{bottom: var(--cbb--sticky-offset);}{$end}";
350 $style .= $sticky_offset_style;
351
352 // Offcanvas.
353 $offcanvas_style = "{$md_start}.is-offcanvas.placement-start,.is-offcanvas.placement-end{width:var(--bb--modal-width--md,25rem);}.is-offcanvas.placement-top,.is-offcanvas.placement-bottom{height:var(--bb--modal-height--md,30vh);}{$end}";
354 $offcanvas_style .= "{$lg_start}.is-offcanvas.placement-start,.is-offcanvas.placement-end{width:var(--bb--modal-width--lg,25rem);}.is-offcanvas.placement-top,.is-offcanvas.placement-bottom{height:var(--bb--modal-height--lg,30vh);}{$end}";
355 $style .= $offcanvas_style;
356
357 // Popover.
358 $popover_style = "{$md_start}.is-popover{width:var(--bb--modal-width--md,auto);height:var(--bb--modal-height--md,auto);}{$end}";
359 $popover_style .= "{$lg_start}.is-popover{width:var(--bb--modal-width--lg,auto);height:var(--bb--modal-height--lg,auto);}{$end}";
360 $style .= $popover_style;
361
362 // Modal.
363 $modal_style = "{$md_start}.is-modal > .bb-modal-dialog{width:var(--bb--modal-width--md,32rem);}.is-modal > .bb-modal-dialog[style*=\"--bb--modal-height--md:\"]{height:var(--bb--modal-height--md);}.modal--custom-position{align-items:var(--bb--modal-v-align--md);justify-content:var(--bb--modal-h-align--md);}{$end}";
364 $modal_style .= "{$lg_start}.is-modal > .bb-modal-dialog{width:var(--bb--modal-width--md,50rem);}.is-modal > .bb-modal-dialog[style*=\"--bb--modal-height--lg:\"]{height:var(--bb--modal-height--lg);}.modal--custom-position{align-items:var(--bb--modal-v-align--lg);justify-content:var(--bb--modal-h-align--lg);}{$end}";
365 $style .= $modal_style;
366
367 // Mobile background.
368 $background_style = '.cbb-mobile-image > .bb\:block-background--img{display:none !important;}.cbb-mobile-image > .is-mobile-image {display:block !important;}';
369 $background_style .= "@media(min-width:{$md_breakpoint}px){.cbb-mobile-image > .bb\:block-background--img{display:block !important;}.cbb-mobile-image > .is-mobile-image {display:none !important;}}";
370 $background_style .= "@media(max-width:{$md_breakpoint_max}px){.cbb-mobile-background{background-image: var(--cbb-mobile-background) !important;}}";
371 $style .= $background_style;
372
373 return $style;
374 }
375
376 /**
377 * Render style for all supported blocks
378 *
379 * @param string $block_content
380 * @param array $block
381 * @param WP_Block $block_instance
382 * @return string
383 */
384 public function render_block_style( $block_content, $block, $block_instance ) {
385 // Ignore admin side.
386 if ( is_admin() ) {
387 return $block_content;
388 }
389
390 // Bail if the block has no style.
391 if ( ! $this->has_style( $block, $block_instance ) ) {
392 return $block_content;
393 }
394
395 // Buil selector.
396 $selector = $this->generate_selector( $block['blockName'], $block_instance );
397
398 // Get custom style.
399 $is_in_cache = isset( $this->styles[ $selector ] );
400
401 if ( $is_in_cache ) {
402 $block_styles = $this->styles[ $selector ];
403 } else {
404 $block_styles = $this->get_block_style( $block, $selector, $block_instance );
405
406 // Add to the cache.
407 $this->styles[ $selector ] = $block_styles;
408 }
409
410 if ( empty( $block_styles['style'] ?? '' ) ) {
411 if ( isset( $block_styles['classes'] ) && count( $block_styles['classes'] ) > 1 ) {
412 // There is no style, but we have class-features like visibility.
413 $block_content = $this->add_class_to_block( $block_content, implode( ' ', $block_styles['classes'] ) );
414 }
415
416 return $block_content;
417 }
418
419 if ( ! wp_is_block_theme() ) {
420 // Enqueue style.
421 wp_enqueue_style( $this->feature_style_handle );
422 }
423
424 if ( ! $is_in_cache ) {
425 wp_add_inline_style( $this->feature_style_handle, $block_styles['style'] );
426 }
427
428 // Add selector to block wrapper element.
429 $block_content = $this->add_class_to_block( $block_content, implode( ' ', $block_styles['classes'] ) );
430
431 return $block_content;
432 }
433
434 /**
435 * Get block custom style
436 *
437 * @param array $block
438 * @param string $selector
439 * @param WP_Block $block_instance
440 * @return string
441 */
442 private function get_block_style( $block, $selector, $block_instance ) {
443 $style = '';
444 $class_selector = '.' . $selector;
445
446 $classes = [ $selector ];
447 $style_array = [];
448 $responsive_style_array = [];
449
450 // Get responsive settings.
451 $breakpoints = $this->get_breakpoints();
452
453 // Get the settings.
454 $settings = $block['attrs']['boldblocks'];
455
456 // Features.
457 $features = [
458 'padding' => [
459 'func_build_responsive_style' => [ $this, 'build_padding_style' ],
460 'group' => 'spacing',
461 ],
462 'margin' => [
463 'func_build_responsive_style' => [ $this, 'build_margin_style' ],
464 'group' => 'spacing',
465 ],
466 'blockGap' => [
467 'func_build_responsive_style' => [ $this, 'build_block_gap_style' ],
468 'group' => 'spacing',
469 ],
470 'border' => [ 'func_build_responsive_style' => [ $this, 'build_border_style' ] ],
471 'borderRadius' => [ 'func_build_responsive_style' => [ $this, 'build_border_radius_style' ] ],
472 'width' => [ 'func_build_responsive_style' => [ $this, 'build_width_style' ] ],
473 'height' => [ 'func_build_responsive_style' => [ $this, 'build_height_style' ] ],
474 'aspectRatio' => [ 'func_build_responsive_style' => [ $this, 'build_aspect_ratio_style' ] ],
475 'textAlignment' => [ 'func_build_responsive_style' => [ $this, 'build_text_align_style' ] ],
476 'verticalAlignment' => [
477 'func_build_responsive_style' => [ $this, 'build_vertical_align_style' ],
478 'func_build_dependent_style' => [ $this, 'build_vertical_align_dependent_style' ],
479 ],
480 'justifyAlignment' => [
481 'func_build_responsive_style' => [ $this, 'build_justify_align_style' ],
482 'func_build_dependent_style' => [ $this, 'build_justify_align_dependent_style' ],
483 ],
484 'transform' => [
485 'func_build_responsive_style' => [ $this, 'build_transform_style' ],
486 'func_build_dependent_style' => [ $this, 'build_transform_dependent_style' ],
487 ],
488 'hidden' => [
489 'func_build_style' => [ $this, 'build_hidden_style' ],
490 'group' => 'visibility',
491 ],
492 'columns' => [
493 'func_build_responsive_style' => [ $this, 'build_columns_style' ],
494 'group' => 'grid',
495 'layout_type' => 'grid',
496 ],
497 'rows' => [
498 'func_build_responsive_style' => [ $this, 'build_rows_style' ],
499 'group' => 'grid',
500 'layout_type' => 'grid',
501 ],
502 'autoRows' => [
503 'func_build_responsive_style' => [ $this, 'build_auto_rows_style' ],
504 'group' => 'grid',
505 'layout_type' => 'grid',
506 ],
507 'gap' => [
508 'func_build_responsive_style' => [ $this, 'build_gap_style' ],
509 'group' => 'grid',
510 'layout_type' => 'grid',
511 ],
512 'columnSpan' => [
513 'func_build_responsive_style' => [ $this, 'build_column_span_style' ],
514 'group' => 'gridItem',
515 'layout_type' => 'gridItem',
516 ],
517 'rowSpan' => [
518 'func_build_responsive_style' => [ $this, 'build_row_span_style' ],
519 'group' => 'gridItem',
520 'layout_type' => 'gridItem',
521 ],
522 'accordionGap' => [
523 'func_build_responsive_style' => [ $this, 'build_accordion_gap_style' ],
524 'func_build_dependent_style' => [ $this, 'build_accordion_gap_dependent_style' ],
525 'group' => 'accordion',
526 'setting_name' => 'gap',
527 'layout_type' => 'accordion',
528 ],
529 'accordionPadding' => [
530 'func_build_responsive_style' => [ $this, 'build_accordion_padding_style' ],
531 'group' => 'accordion',
532 'setting_name' => 'padding',
533 'layout_type' => 'accordion',
534 ],
535 'accordionBorder' => [
536 'func_build_style' => [ $this, 'build_accordion_border_style' ],
537 'group' => 'accordion',
538 'setting_name' => 'border',
539 'layout_type' => 'accordion',
540 ],
541 'accordionRadius' => [
542 'func_build_style' => [ $this, 'build_accordion_radius_style' ],
543 'group' => 'accordion',
544 'setting_name' => 'radius',
545 'layout_type' => 'accordion',
546 ],
547 'stickyOffset' => [
548 'func_build_responsive_style' => [ $this, 'build_sticky_offset_style' ],
549 'group' => 'sticky',
550 'setting_name' => 'offset',
551 ],
552 ];
553
554 $block_layout = apply_filters( 'cbb_get_block_layout', $block_instance->block_type->supports['layoutType'] ?? '', $block );
555 foreach ( $features as $feature => $feature_args ) {
556 if ( isset( $feature_args['layout_type'] ) && $block_layout !== $feature_args['layout_type'] ) {
557 continue;
558 }
559
560 if ( ! in_array( $feature, [ 'border', 'hidden' ], true ) && 'accordionItem' === $block_layout ) {
561 continue;
562 }
563
564 if ( ! in_array( $feature, [ 'border', 'hidden', 'accordionGap', 'accordionPadding', 'accordionBorder', 'accordionRadius' ], true ) && 'accordion' === $block_layout ) {
565 continue;
566 }
567
568 if ( 'stickyOffset' === $feature ) {
569 // Not supported block layouts.
570 if ( ! in_array( $block_layout, [ 'standalone', 'gridItem' ], true ) ) {
571 continue;
572 }
573
574 // Not enable sticky or enabling scrolling up.
575 if ( ! ( $settings['sticky']['enable'] ?? false ) || ( $settings['sticky']['isStickOnScrollingUp'] ?? false ) ) {
576 continue;
577 }
578 }
579
580 $group_name = $feature_args['group'] ?? null;
581 $setting_name = $feature_args['setting_name'] ?? $feature;
582 if ( $this->is_valid_value( $group_name ) ) {
583 $setting_value = $settings[ $group_name ][ $setting_name ] ?? null;
584 } else {
585 $setting_value = $settings[ $setting_name ] ?? null;
586 }
587
588 if ( empty( $setting_value ) ) {
589 continue;
590 }
591
592 $args = array_merge(
593 [
594 'settings' => $settings,
595 'setting_value' => $setting_value,
596 'feature' => $feature,
597 'selector' => $class_selector,
598 'breakpoints' => $breakpoints,
599 'block' => $block,
600 'block_layout' => $block_layout,
601 ],
602 $feature_args
603 );
604
605 if ( \is_callable( $args['func_build_style'] ?? '' ) ) {
606 $this->build_style( array_merge( $args, [ 'value' => $setting_value ] ), $style_array, $classes );
607 }
608
609 if ( \is_callable( $args['func_build_responsive_style'] ?? '' ) && $this->is_valid_responsive_value( $args['setting_value'] ) ) {
610 $this->build_responsive_style( $args, $responsive_style_array, $classes );
611 }
612 }
613
614 if ( $style_array ) {
615 foreach ( $style_array as $item_style ) {
616 $style .= $item_style;
617 }
618 }
619
620 if ( $responsive_style_array ) {
621 foreach ( $responsive_style_array as $responsive_style ) {
622 $style .= $responsive_style;
623 }
624 }
625
626 $custom_css = $this->get_custom_css_style( $block );
627
628 if ( $custom_css ) {
629 // Get & refine custom style.
630 $custom_style = $this->refine_custom_value( $custom_css['value'] ?? '', [ 'selector' => $class_selector ], 'CSS' );
631
632 if ( $custom_style ) {
633 $style .= $custom_style;
634 }
635 }
636
637 // Allow altering the style.
638 $style = apply_filters(
639 'cbb_get_block_style',
640 $style,
641 [
642 'settings' => $settings,
643 'selector' => $class_selector,
644 'breakpoints' => $breakpoints,
645 'block' => $block,
646 'block_layout' => $block_layout,
647 ]
648 );
649
650 return [
651 'style' => $style,
652 'classes' => $classes,
653 ];
654 }
655
656 /**
657 * Build custom style
658 *
659 * @param array $args
660 * @param array &$style_array
661 * @param array &$classes
662 * @return string
663 */
664 private function build_style( $args, &$style_array, &$classes ) {
665 $return_value = false;
666 $func_build_style = $args['func_build_style'] ?? '';
667
668 if ( ! \is_callable( $func_build_style ) ) {
669 return $return_value;
670 }
671
672 $setting_value = $args['setting_value'] ?? [];
673 if ( ! $this->is_valid_value( $setting_value ) ) {
674 return $return_value;
675 }
676
677 // Build style.
678 return $func_build_style( $args, $style_array, $classes );
679 }
680
681 /**
682 * Build style for padding
683 *
684 * @param array $args
685 * @return string
686 */
687 private function build_padding_style( $args ) {
688 $style_array = [];
689 $value = $args['value'];
690 if ( is_array( $value ) ) {
691 if ( isset( $value['top'] ) && is_string( $value['top'] ) ) {
692 $top_style = $this->get_spacing_value( $value['top'] );
693 if ( $this->is_valid_value( $top_style ) ) {
694 $style_array['--cbb--padding-top'] = $top_style;
695 }
696 }
697
698 if ( isset( $value['right'] ) && is_string( $value['right'] ) ) {
699 $right_style = $this->get_spacing_value( $value['right'] );
700 if ( $this->is_valid_value( $right_style ) ) {
701 $style_array['--cbb--padding-right'] = $right_style;
702 }
703 }
704
705 if ( isset( $value['bottom'] ) && is_string( $value['bottom'] ) ) {
706 $bottom_style = $this->get_spacing_value( $value['bottom'] );
707 if ( $this->is_valid_value( $bottom_style ) ) {
708 $style_array['--cbb--padding-bottom'] = $bottom_style;
709 }
710 }
711
712 if ( isset( $value['left'] ) && is_string( $value['left'] ) ) {
713 $left_style = $this->get_spacing_value( $value['left'] );
714 if ( $this->is_valid_value( $left_style ) ) {
715 $style_array['--cbb--padding-left'] = $left_style;
716 }
717 }
718 }
719
720 return $style_array;
721 }
722
723 /**
724 * Build style for margin
725 *
726 * @param array $args
727 * @return string
728 */
729 private function build_margin_style( $args ) {
730 $style_array = [];
731 $value = $args['value'];
732 if ( is_array( $value ) ) {
733 if ( isset( $value['top'] ) && is_string( $value['top'] ) ) {
734 $top_style = $this->get_spacing_value( $value['top'] );
735 if ( $this->is_valid_value( $top_style ) ) {
736 $style_array['--cbb--margin-top'] = $top_style;
737 }
738 }
739
740 if ( isset( $value['right'] ) && is_string( $value['right'] ) ) {
741 $right_style = $this->get_spacing_value( $value['right'] );
742 if ( $this->is_valid_value( $right_style ) ) {
743 $style_array['--cbb--margin-right'] = $right_style;
744 }
745 }
746
747 if ( isset( $value['bottom'] ) && is_string( $value['bottom'] ) ) {
748 $bottom_style = $this->get_spacing_value( $value['bottom'] );
749 if ( $this->is_valid_value( $bottom_style ) ) {
750 $style_array['--cbb--margin-bottom'] = $bottom_style;
751 }
752 }
753
754 if ( isset( $value['left'] ) && is_string( $value['left'] ) ) {
755 $left_style = $this->get_spacing_value( $value['left'] );
756 if ( $this->is_valid_value( $left_style ) ) {
757 $style_array['--cbb--margin-left'] = $left_style;
758 }
759 }
760 }
761
762 return $style_array;
763 }
764
765 /**
766 * Build style for gap
767 *
768 * @param array $args
769 * @return string
770 */
771 private function build_block_gap_style( $args ) {
772 $style_array = [];
773 $value = $args['value'];
774 if ( is_array( $value ) && ( $value['top'] ?? false ) ) {
775 $gap_style = $this->get_spacing_value( $value['top'] );
776 if ( $this->is_valid_value( $gap_style ) ) {
777 $style_array['--cbb--block-gap'] = $gap_style;
778 }
779 }
780
781 return $style_array;
782 }
783
784 /**
785 * Build style for border
786 *
787 * @param array $args
788 * @return string
789 */
790 private function build_border_style( $args ) {
791 $style_array = [];
792 $value = $args['value'];
793 if ( is_array( $value ) ) {
794 if ( ( $value['top'] ?? false ) || ( $value['right'] ?? false ) || ( $value['bottom'] ?? false ) || ( $value['left'] ?? false ) ) {
795 $top = $value['top'] ?? null;
796 $right = $value['right'] ?? null;
797 $bottom = $value['bottom'] ?? null;
798 $left = $value['left'] ?? null;
799 } else {
800 $top = $value;
801 $right = $value;
802 $bottom = $value;
803 $left = $value;
804 }
805
806 if ( $top ) {
807 $top_style = $this->build_border_side_css_value( $top, 'top' );
808 if ( $top_style ) {
809 $style_array['--cbb--border-top'] = $top_style;
810 }
811 }
812
813 if ( $right ) {
814 $right_style = $this->build_border_side_css_value( $right, 'right' );
815 if ( $right_style ) {
816 $style_array['--cbb--border-right'] = $right_style;
817 }
818 }
819
820 if ( $bottom ) {
821 $bottom_style = $this->build_border_side_css_value( $bottom, 'bottom' );
822 if ( $bottom_style ) {
823 $style_array['--cbb--border-bottom'] = $bottom_style;
824 }
825 }
826
827 if ( $left ) {
828 $left_style = $this->build_border_side_css_value( $left, 'left' );
829 if ( $left_style ) {
830 $style_array['--cbb--border-left'] = $left_style;
831 }
832 }
833 }
834
835 return $style_array;
836 }
837
838 /**
839 * Build the css variable for border by side
840 *
841 * @param array $value
842 * @param string $side
843 * @return string
844 */
845 private function build_border_side_css_value( $value, $side ) {
846 $style_array = [];
847 if ( is_array( $value ) ) {
848 if ( $this->is_valid_value( $value['width'] ?? null ) && is_string( $value['width'] ) ) {
849 $style_array[] = $value['width'];
850 }
851
852 if ( $this->is_valid_value( $value['style'] ?? null ) && is_string( $value['style'] ) ) {
853 $style_array[] = $value['style'];
854 }
855
856 if ( $this->is_valid_value( $value['color'] ?? null ) ) {
857 $color = $this->get_css_color_value( $value['color'] );
858 if ( $color ) {
859 $style_array[] = $color;
860 }
861 }
862 }
863
864 return implode( ' ', $style_array );
865 }
866
867 /**
868 * Build style for radius
869 *
870 * @param array $args
871 * @return string
872 */
873 private function build_border_radius_style( $args ) {
874 $style_array = [];
875 $value = $args['value'];
876 if ( is_array( $value ) ) {
877 $css_value = '';
878 $top_left = $value['top-left'] ?? 0;
879 $top_right = $value['top-right'] ?? 0;
880 $bottom_right = $value['bottom-right'] ?? 0;
881 $bottom_left = $value['bottom-left'] ?? 0;
882
883 if ( ! empty( $top_left ) || ! empty( $top_right ) || ! empty( $bottom_right ) || ! empty( $bottom_left ) ) {
884 $css_value = "{$top_left} {$top_right} {$bottom_right} {$bottom_left}";
885 }
886
887 if ( $args['settings']['enableEllipticalRadius'] ?? false ) {
888 $top_left = $value['top-left-vertical'] ?? 0;
889 $top_right = $value['top-right-vertical'] ?? 0;
890 $bottom_right = $value['bottom-right-vertical'] ?? 0;
891 $bottom_left = $value['bottom-left-vertical'] ?? 0;
892
893 if ( ! empty( $top_left ) || ! empty( $top_right ) || ! empty( $bottom_right ) || ! empty( $bottom_left ) ) {
894 $css_value = "{$css_value} / {$top_left} {$top_right} {$bottom_right} {$bottom_left}";
895 }
896 }
897
898 if ( $css_value ) {
899 $style_array['--cbb--border-radius'] = $css_value;
900 }
901 }
902
903 return $style_array;
904 }
905
906 /**
907 * Build style for width
908 *
909 * @param array $args
910 * @return string
911 */
912 private function build_width_style( $args ) {
913 $style_array = [];
914 $value = $args['value'];
915 if ( is_array( $value ) && ( $value['value'] ?? false ) ) {
916 $width_style = $value['value'];
917 if ( $this->is_valid_value( $width_style ) ) {
918 $style_array['--cbb--width'] = $width_style;
919 }
920 }
921
922 return $style_array;
923 }
924
925 /**
926 * Build style for height
927 *
928 * @param array $args
929 * @return string
930 */
931 private function build_height_style( $args ) {
932 $style_array = [];
933 $value = $args['value'];
934 if ( is_array( $value ) && ( $value['value'] ?? false ) ) {
935 $style = $value['value'];
936 if ( $this->is_valid_value( $style ) ) {
937 $style_array['--cbb--height'] = $style;
938 }
939 }
940
941 return $style_array;
942 }
943
944 /**
945 * Build style for aspect ratio
946 *
947 * @param array $args
948 * @return string
949 */
950 private function build_aspect_ratio_style( $args ) {
951 $style_array = [];
952 $value = $args['value'];
953 if ( is_array( $value ) && ( $value['value'] ?? false ) ) {
954 $style = $value['value'];
955 if ( $this->is_valid_value( $style ) ) {
956 $style_array['--cbb--aspect-ratio'] = $style;
957 }
958 }
959
960 return $style_array;
961 }
962
963 /**
964 * Build style for text alignment
965 *
966 * @param array $args
967 * @return string
968 */
969 private function build_text_align_style( $args ) {
970 $style_array = [];
971 $value = $args['value'];
972 if ( $this->is_valid_value( $value ) ) {
973 $style_array['--cbb--text-align'] = $value;
974 }
975
976 return $style_array;
977 }
978
979 /**
980 * Build style for vertical alignment
981 *
982 * @param array $args
983 * @return string
984 */
985 private function build_vertical_align_style( $args ) {
986 $style_array = [];
987 $value = $args['value'] ?? null;
988 if ( $this->is_valid_value( $value ) ) {
989 if ( 'top' === $value ) {
990 $value = 'start';
991 } elseif ( 'bottom' === $value ) {
992 $value = 'end';
993 }
994
995 $style_array['--cbb--v-align'] = $value;
996 }
997
998 return $style_array;
999 }
1000
1001 /**
1002 * Build dependent style for vertical alignment
1003 *
1004 * @param array $args
1005 * @param array &$classes
1006 * @return string
1007 */
1008 private function build_vertical_align_dependent_style( $args, &$classes ) {
1009 $style = '';
1010 $value = $args['setting_value'] ?? null;
1011 if ( $this->is_valid_value( $value ) ) {
1012 $block_layout = $args['block_layout'] ?? '';
1013 if ( 'grid' === $block_layout ) {
1014 $class = 'cbb-align-items';
1015 } elseif ( 'gridItem' === $block_layout ) {
1016 $class = 'cbb-align-self';
1017 } else {
1018 $class = 'cbb-layout-grid';
1019 }
1020
1021 if ( ! in_array( $class, $classes, true ) ) {
1022 $classes[] = $class;
1023 }
1024 }
1025
1026 return $style;
1027 }
1028
1029 /**
1030 * Build style for justify alignment
1031 *
1032 * @param array $args
1033 * @return string
1034 */
1035 private function build_justify_align_style( $args ) {
1036 $style_array = [];
1037 $value = $args['value'] ?? null;
1038 if ( $this->is_valid_value( $value ) ) {
1039 $block_layout = $args['block_layout'] ?? '';
1040 if ( in_array( $block_layout, [ 'standalone', 'carouselItem', 'vstackItem' ], true ) ) {
1041 if ( 'left' === $value ) {
1042 $value = 'start';
1043 } elseif ( 'right' === $value ) {
1044 $value = 'end';
1045 }
1046 }
1047 $style_array['--cbb--h-align'] = $value;
1048 }
1049
1050 return $style_array;
1051 }
1052
1053 /**
1054 * Build dependent style for justify alignment
1055 *
1056 * @param array $args
1057 * @param array &$classes
1058 * @return string
1059 */
1060 private function build_justify_align_dependent_style( $args, &$classes ) {
1061 $style = '';
1062 $value = $args['setting_value'] ?? null;
1063 if ( $this->is_valid_value( $value ) ) {
1064 $block_layout = $args['block_layout'] ?? '';
1065 if ( 'grid' === $block_layout ) {
1066 $class = 'cbb-justify-items';
1067 } elseif ( 'gridItem' === $block_layout ) {
1068 $class = 'cbb-justify-self';
1069 } else {
1070 // if ( in_array( $block_layout, [ 'standalone', 'carouselItem', 'vstackItem' ], true ) ) {.
1071 $class = 'cbb-layout-grid';
1072 }
1073
1074 if ( ! in_array( $class, $classes, true ) ) {
1075 $classes[] = $class;
1076 }
1077 }
1078
1079 return $style;
1080 }
1081
1082 /**
1083 * Build style for transform
1084 *
1085 * @param array $args
1086 * @return string
1087 */
1088 private function build_transform_style( $args ) {
1089 $style_array = [];
1090 $value = $args['value'];
1091 if ( ! empty( $value ) && is_array( $value ) ) {
1092 $style = implode( ' ', array_filter( array_map( [ $this, 'build_transform_item' ], $value ) ) );
1093 if ( $this->is_valid_value( $value ) ) {
1094 $style_array['--cbb--transform'] = $style;
1095 }
1096 }
1097
1098 return $style_array;
1099 }
1100
1101 /**
1102 * Build transform item: translate, rotate, scale, skew
1103 *
1104 * @return string
1105 */
1106 private function build_transform_item( $transform_item ) {
1107 $style = '';
1108 if ( is_array( $transform_item ) && count( $transform_item ) > 0 ) {
1109 // Get tranform type.
1110 $transform_type = array_keys( $transform_item )[0];
1111 $transform_value = $transform_item[ $transform_type ] ?? '';
1112
1113 if ( $transform_type === 'rotate' ) {
1114 if ( is_numeric( $transform_value ) ) {
1115 $style = "rotate({$transform_value}deg)";
1116 }
1117 } elseif ( is_array( $transform_value ) ) {
1118 $suffix = 'skew' === $transform_type ? 'deg' : '';
1119
1120 $x = $transform_value['x'] ?? '';
1121 $y = $transform_value['y'] ?? '';
1122 $x_value = preg_replace( '/[^0-9.]/', '', $x );
1123 $y_value = preg_replace( '/[^0-9.]/', '', $y );
1124
1125 if ( \is_numeric( $x_value ) || \is_numeric( $y_value ) ) {
1126 if ( ! \is_numeric( $x_value ) ) {
1127 if ( 'scale' === $transform_type ) {
1128 $x = 1;
1129 } else {
1130 $x = 0;
1131 }
1132 }
1133
1134 if ( ! \is_numeric( $y_value ) ) {
1135 if ( 'scale' === $transform_type ) {
1136 $y = 1;
1137 } else {
1138 $y = 0;
1139 }
1140 }
1141
1142 $style = "{$transform_type}({$x}{$suffix}, {$y}{$suffix})";
1143 }
1144 }
1145 }
1146
1147 return $style;
1148 }
1149
1150 /**
1151 * Build dependent style for transform
1152 *
1153 * @param array $args
1154 * @param array &$classes
1155 * @return string
1156 */
1157 private function build_transform_dependent_style( $args, &$classes ) {
1158 $transform_origin = $args['settings']['transformOrigin'] ?? [];
1159 $x = $transform_origin['x'] ?? .5;
1160 if ( ! \is_numeric( $x ) ) {
1161 $x = .5;
1162 }
1163 $x = round( $x, 2 ) * 100;
1164 $y = $transform_origin['y'] ?? .5;
1165 if ( ! \is_numeric( $y ) ) {
1166 $y = .5;
1167 }
1168 $y = round( $y, 2 ) * 100;
1169
1170 return "{$args['selector']}{--cbb--transform-origin: {$x}% {$y}%;}";
1171 }
1172
1173 /**
1174 * Build style for hidden
1175 *
1176 * @param array $args
1177 * @param array &$style_array
1178 * @param array &$classes
1179 * @return array
1180 */
1181 private function build_hidden_style( $args, &$style_array, &$classes ) {
1182 $value = $args['value'] ?? null;
1183 if ( ! empty( $value ) && is_array( $value ) ) {
1184 foreach ( $value as $breakpoint ) {
1185 $class = "cbb-hidden-{$breakpoint}";
1186 if ( ! in_array( $class, $classes, true ) ) {
1187 $classes[] = $class;
1188 }
1189 }
1190 }
1191 }
1192
1193 /**
1194 * Build style for grid columns
1195 *
1196 * @param array $args
1197 * @return string
1198 */
1199 private function build_columns_style( $args ) {
1200 $style_array = [];
1201 $value = $args['value'] ?? null;
1202 if ( $this->is_valid_value( $value ) ) {
1203 $style_array['--cbb--grid-columns'] = $value;
1204 }
1205
1206 return $style_array;
1207 }
1208
1209 /**
1210 * Build style for grid rows
1211 *
1212 * @param array $args
1213 * @return string
1214 */
1215 private function build_rows_style( $args ) {
1216 $style_array = [];
1217 $value = $args['value'] ?? null;
1218 if ( $this->is_valid_value( $value ) ) {
1219 $style_array['--cbb--grid-rows'] = $value;
1220 }
1221
1222 return $style_array;
1223 }
1224
1225 /**
1226 * Build style for grid auto rows
1227 *
1228 * @param array $args
1229 * @return string
1230 */
1231 private function build_auto_rows_style( $args ) {
1232 $style_array = [];
1233 $value = $args['value'] ?? null;
1234 if ( $this->is_valid_value( $value ) ) {
1235 $style_array['--cbb--grid-auto-rows'] = $value;
1236 }
1237
1238 return $style_array;
1239 }
1240
1241 /**
1242 * Build style for grid gap
1243 *
1244 * @param array $args
1245 * @return string
1246 */
1247 private function build_gap_style( $args ) {
1248 $style_array = [];
1249 $value = $args['value'] ?? null;
1250 if ( $value && is_array( $value ) ) {
1251 $column_gap = $value['column'] ?? null;
1252 if ( $this->is_valid_value( $column_gap ) ) {
1253 $style_array['--cbb--grid-gap-column'] = $column_gap;
1254 }
1255 $row_gap = $value['row'] ?? null;
1256 if ( $this->is_valid_value( $row_gap ) ) {
1257 $style_array['--cbb--grid-gap-row'] = $row_gap;
1258 }
1259 }
1260
1261 return $style_array;
1262 }
1263
1264 /**
1265 * Build style for grid item column span
1266 *
1267 * @param array $args
1268 * @return string
1269 */
1270 private function build_column_span_style( $args ) {
1271 $style_array = [];
1272 $value = $args['value'] ?? null;
1273 if ( $value && is_array( $value ) ) {
1274 $start = $value['start'] ?? null;
1275 $span = $value['span'] ?? null;
1276 $order = $value['order'] ?? null;
1277
1278 if ( $this->is_valid_value( $start ) || $this->is_valid_value( $span ) ) {
1279 $start = $this->is_valid_value( $start ) ? $start : 'auto';
1280 $span = $this->is_valid_value( $span ) ? $span : 'auto';
1281
1282 if ( 'auto' !== $span && absint( $span ) > 0 ) {
1283 $span = "span {$span}";
1284 }
1285
1286 $style_array['--cbb--grid-item-column'] = "{$start} / {$span}";
1287 }
1288
1289 if ( $this->is_valid_value( $order ) && $order !== 'auto' ) {
1290 $style_array['--cbb--grid-item-order'] = $order;
1291 }
1292 }
1293
1294 return $style_array;
1295 }
1296
1297 /**
1298 * Build style for grid item row span
1299 *
1300 * @param array $args
1301 * @return string
1302 */
1303 private function build_row_span_style( $args ) {
1304 $style_array = [];
1305 $value = $args['value'] ?? null;
1306 if ( $value && is_array( $value ) ) {
1307 $start = $value['start'] ?? null;
1308 $span = $value['span'] ?? null;
1309
1310 if ( $this->is_valid_value( $start ) || $this->is_valid_value( $span ) ) {
1311 $start = $this->is_valid_value( $start ) ? $start : 'auto';
1312 $span = $this->is_valid_value( $span ) ? $span : 'auto';
1313
1314 if ( 'auto' !== $span && absint( $span ) > 0 ) {
1315 $span = "span {$span}";
1316 }
1317
1318 $style_array['--cbb--grid-item-row'] = "{$start} / {$span}";
1319 }
1320 }
1321
1322 return $style_array;
1323 }
1324
1325 /**
1326 * Build style for accordion gap
1327 *
1328 * @param array $args
1329 * @return string
1330 */
1331 private function build_accordion_gap_style( $args ) {
1332 $style_array = [];
1333 $value = $args['value'];
1334 if ( $this->is_valid_value( $value ) ) {
1335 $style_array['--cbb--accordion-gap'] = $this->get_spacing_value( $value );
1336 }
1337
1338 return $style_array;
1339 }
1340
1341 /**
1342 * Build dependent style for accordion gap
1343 *
1344 * @param array $args
1345 * @param array &$classes
1346 * @return string
1347 */
1348 private function build_accordion_gap_dependent_style( $args, &$classes ) {
1349 $style = '';
1350 $setting_values = $args['setting_values'];
1351 if ( $this->is_valid_value( $setting_values ) && is_array( $setting_values ) ) {
1352 foreach ( $setting_values as $breakpoint => $value ) {
1353 if ( strpos( $value, 'var:preset|spacing|' ) !== false || absint( $value ) > 0 ) {
1354 $classes[] = "{$breakpoint}-cbb-a-has-border";
1355 } else {
1356 $classes[] = "{$breakpoint}-cbb-a-no-border";
1357 }
1358 }
1359 }
1360
1361 return $style;
1362 }
1363
1364 /**
1365 * Build style for accordion gap
1366 *
1367 * @param array $args
1368 * @return string
1369 */
1370 private function build_accordion_padding_style( $args ) {
1371 $style_array = [];
1372 $value = $args['value'];
1373 if ( $value && is_array( $value ) ) {
1374 $x = $value['x'] ?? null;
1375 if ( ! $this->is_valid_value( $x ) ) {
1376 $x = '1.25rem';
1377 }
1378 $x = $this->get_spacing_value( $x );
1379
1380 $y = $value['y'] ?? null;
1381 if ( ! $this->is_valid_value( $y ) ) {
1382 $y = '1rem';
1383 }
1384 $y = $this->get_spacing_value( $y );
1385
1386 $style_array['--cbb--accordion-padding'] = "{$y} {$x}";
1387 }
1388
1389 return $style_array;
1390 }
1391
1392 /**
1393 * Build style for accordion border
1394 *
1395 * @param array $args
1396 * @return string
1397 */
1398 private function build_accordion_border_style( $args, &$style_array ) {
1399 $value = $args['value'] ?? null;
1400 if ( $value && is_array( $value ) ) {
1401 if ( ( $value['top'] ?? false ) || ( $value['right'] ?? false ) || ( $value['bottom'] ?? false ) || ( $value['left'] ?? false ) ) {
1402 $border_styles = $this->build_border_style( $args );
1403 } else {
1404 $border_styles = $this->build_border_style( [ 'value' => ['top' => $value, 'right' => $value, 'bottom' => $value, 'left' => $value] ] );
1405 }
1406
1407 if ( $border_styles ) {
1408 $border_style = implode( ';', array_map( function ($attr_key) use ($border_styles) {
1409 return "{$attr_key}:{$border_styles[ $attr_key ]}";
1410 }, array_keys($border_styles) ) );
1411
1412 $style_array[] = "{$args['selector']}{{$border_style};}";
1413 }
1414 }
1415
1416 return $style_array;
1417 }
1418
1419 /**
1420 * Build style for accordion radius
1421 *
1422 * @param array $args
1423 * @return string
1424 */
1425 private function build_accordion_radius_style( $args, &$style_array ) {
1426 $value = $args['value'] ?? null;
1427 if ( $value ) {
1428 if ( is_array( $value ) ) {
1429 $top_left = $value['topLeft'] ?? 0;
1430 $top_right = $value['topRight'] ?? 0;
1431 $bottom_right = $value['bottomRight'] ?? 0;
1432 $bottom_left = $value['bottomLeft'] ?? 0;
1433
1434 $css_value = "{$top_left} {$top_right} {$bottom_right} {$bottom_left}";
1435 } elseif ( is_string( $value ) ) {
1436 $css_value = $value;
1437 }
1438 $style_array[] = "{$args['selector']}{--cbb--border-radius:{$css_value};}";
1439 }
1440
1441 return $style_array;
1442 }
1443
1444 /**
1445 * Build style for sticky offset
1446 *
1447 * @param array $args
1448 * @return array
1449 */
1450 private function build_sticky_offset_style( $args ) {
1451 $style_array = [];
1452 $value = $args['value'];
1453 if ( $value && is_array( $value ) ) {
1454 $offset = $value['value'] ?? null;
1455 if ( $this->is_valid_value( $offset ) ) {
1456 $style_array['--cbb--sticky-offset'] = $offset;
1457 }
1458 }
1459
1460 return $style_array;
1461 }
1462
1463 /**
1464 * Check whether current block has custom style or not.
1465 *
1466 * @param [type] $block
1467 * @return boolean
1468 */
1469 private function get_custom_css_style( $block ) {
1470 // There is no boldblocks value.
1471 if ( ! isset( $block['attrs']['boldblocks'] ) || ! isset( $block['attrs']['boldblocks']['customCSS'] ) ) {
1472 return false;
1473 }
1474
1475 $custom_css = $block['attrs']['boldblocks']['customCSS'];
1476 if ( ! is_array( $custom_css ) || empty( $custom_css['value'] ) ) {
1477 return false;
1478 }
1479
1480 return $custom_css;
1481 }
1482
1483 /**
1484 * Render dynamic link to post element
1485 *
1486 * @param string $block_content
1487 * @param array $block
1488 * @param WP_Block $block_instance
1489 * @return string
1490 */
1491 public function build_block_link_to_post( $block_content, $block, $block_instance ) {
1492 // Ignore admin side.
1493 if ( is_admin() ) {
1494 return $block_content;
1495 }
1496
1497 // There is no post context.
1498 $post_id = $block_instance->context['postId'] ?? false;
1499 if ( ! $post_id ) {
1500 return $block_content;
1501 }
1502
1503 // There is no background value.
1504 if ( ! ( $block['attrs']['boldblocks']['background'] ?? false ) ) {
1505 return $block_content;
1506 }
1507
1508 $background = $block['attrs']['boldblocks']['background'];
1509 $media_type = $background['mediaType'] ?? 'image';
1510
1511 if ( 'image' === $media_type && ( $background['image']['useFeaturedImage'] ?? false ) && ( $background['image']['linkToPost'] ?? false ) ) {
1512 $block_content = $this->add_inner_content_to_block( $block_content, '<a class="bb:link-to-post" href="' . esc_url( get_permalink( $post_id ) ) . '" aria-label="' . esc_attr( get_the_title( $post_id ) ) . '" rel="permalink"></a>' );
1513 }
1514
1515 return $block_content;
1516 }
1517
1518 /**
1519 * Render dynamic background image
1520 *
1521 * @param string $block_content
1522 * @param array $block
1523 * @param WP_Block $block_instance
1524 * @return string
1525 */
1526 public function build_block_background( $block_content, $block, $block_instance ) {
1527 // Ignore admin side.
1528 if ( is_admin() ) {
1529 return $block_content;
1530 }
1531
1532 // Ignore legacy markup.
1533 if ( strpos( $block['innerHTML'] ?? '', 'bb:block-background' ) !== false ) {
1534 return $block_content;
1535 }
1536
1537 $context_background = [];
1538 $meta_name = $block['attrs']['metadata']['name'] ?? '';
1539 if ( $meta_name && 'cbb/block-overrides' === ( $block['attrs']['metadata']['bindings']['background']['source'] ?? '' ) ) {
1540 $context_background = $block_instance->context['cbb/block-overrides'][ $meta_name ]['background'] ?? [];
1541 }
1542
1543 $background = $block['attrs']['boldblocks']['background'] ?? [];
1544 if ( $context_background ) {
1545 $background = array_replace_recursive( $background, $context_background );
1546 }
1547
1548 // There is no background value.
1549 if ( ! $background ) {
1550 return $block_content;
1551 }
1552
1553 $media_type = $background['mediaType'] ?? 'image';
1554 $media_url = $background[ $media_type ]['url'] ?? '';
1555
1556 if ( 'image' === $media_type ) {
1557 if ( $background['image']['useFeaturedImage'] ?? false ) {
1558 return $this->build_thumbnail_background( $background, $block_content, $block, $block_instance );
1559 } elseif ( $media_url ) {
1560 return $this->build_custom_image_background( $background, $block_content, $block, $block_instance );
1561 }
1562 } elseif ( 'video' === $media_type && $media_url ) {
1563 return $this->build_video_background( $background, $block_content, $block, $block_instance );
1564 }
1565
1566 return $block_content;
1567 }
1568
1569 /**
1570 * Build the background using custom image
1571 *
1572 * @param array $background
1573 * @param string $block_content
1574 * @param array $block
1575 * @param WP_Block $block_instance
1576 * @return string
1577 */
1578 private function build_custom_image_background( $background, $block_content, $block, $block_instance ) {
1579 $block_class = 'bb:has-background bb:has-background--image';
1580 $block_background = $this->build_image_background( $background['image']['id'] ?? false, $background, $block_class );
1581
1582 if ( strpos( $block['innerHTML'] ?? '', $block_background['block_class'] ) === false ) {
1583 $block_content = $this->add_class_to_block( $block_content, $block_background['block_class'] );
1584 }
1585
1586 $block_content = $this->add_inner_content_to_block( $block_content, $block_background['background_markup'] );
1587
1588 return $block_content;
1589 }
1590
1591 /**
1592 * Build the background using post thumbnail
1593 *
1594 * @param array $background
1595 * @param string $block_content
1596 * @param array $block
1597 * @param WP_Block $block_instance
1598 * @return string
1599 */
1600 private function build_thumbnail_background( $background, $block_content, $block, $block_instance ) {
1601 $post_id = $block_instance->context['postId'] ?? false;
1602 if ( ! $post_id ) {
1603 return $block_content;
1604 }
1605
1606 $image_id = get_post_thumbnail_id( $post_id );
1607
1608 $block_class = 'bb:has-background bb:has-background--image';
1609 if ( $image_id ) {
1610 $block_background = $this->build_image_background( $image_id, $background, $block_class );
1611
1612 if ( strpos( $block['innerHTML'] ?? '', $block_background['block_class'] ) === false ) {
1613 $block_content = $this->add_class_to_block( $block_content, $block_background['block_class'] );
1614 }
1615
1616 $block_content = $this->add_inner_content_to_block( $block_content, $block_background['background_markup'] );
1617 } else {
1618 // Add custom background as a fallback.
1619 if ( $background['image']['url'] ?? '' ) {
1620 $block_content = $this->add_class_to_block( $block_content, 'has-no-thumbnail' );
1621 $block_content = $this->build_custom_image_background( $background, $block_content, $block, $block_instance );
1622 } else {
1623 $block_content = $this->add_class_to_block( $block_content, $block_class . ' has-no-thumbnail' );
1624 }
1625 }
1626
1627 return $block_content;
1628 }
1629
1630 /**
1631 * Build image background
1632 *
1633 * @param int $image_id
1634 * @param array $background
1635 * @param string $block_class
1636 * @return array
1637 */
1638 private function build_image_background( $image_id, $background, $block_class ) {
1639 $settings = $background['settings'] ?? [];
1640 $media = $background['image'] ?? [];
1641 $mobile_background = ! ( $media['useFeaturedImage'] ?? false ) && ( $media['hasMobileBackground'] ?? false ) && ( $media['mobileBackground']['url'] ?? '' ) ? $media['mobileBackground'] : false;
1642 $background_classes = [ 'bb:block-background bb:block-background--image cbb-bg-image' ];
1643 $background_position = round( ( $settings['focalPoint']['x'] ?? .5 ) * 100 ) . '% ' . round( ( $settings['focalPoint']['y'] ?? .5 ) * 100 ) . '%';
1644
1645 $image_element = '';
1646 $dataset = [];
1647 $image_size = $media['size'] ?? 'full';
1648
1649 $is_internal_image = false;
1650 $image_url = $image_id ? wp_get_attachment_image_url( $image_id, $image_size ) : false;
1651 if ( $image_url ) {
1652 $is_internal_image = true;
1653 } else {
1654 $image_url = $media['url'] ?? '';
1655 }
1656
1657 $custom_alt_text = $media['customAlt'] ?? '';
1658 $object_fit = $settings['objectFit'] ?? 'cover';
1659 if ( empty( $object_fit ) ) {
1660 $object_fit = 'cover';
1661 }
1662 $img_style = 'display:block;width:100%;height:100%;object-fit:' . $object_fit . ';object-position:' . $background_position . ';';
1663 $background_styles = [];
1664 if ( $settings['isImgElement'] ?? false ) {
1665 $attrs = [
1666 'style' => esc_attr( $img_style ),
1667 'class' => 'bb:block-background--img',
1668 ];
1669
1670 $loading_priority = $settings['loadingPriority'] ?? '';
1671 if ( $loading_priority ) {
1672 if ( $loading_priority === 'lazy' ) {
1673 $attrs['loading'] = 'lazy';
1674 } elseif ( in_array( $loading_priority, [ 'high', 'low' ], true ) ) {
1675 $attrs['fetchpriority'] = $loading_priority;
1676 }
1677 }
1678
1679 if ( $is_internal_image ) {
1680 $alt = trim( wp_strip_all_tags( get_post_meta( $image_id, '_wp_attachment_image_alt', true ) ) );
1681 if ( ! $alt ) {
1682 $attrs['alt'] = $custom_alt_text;
1683 }
1684 $image_element = wp_get_attachment_image(
1685 $image_id,
1686 $image_size,
1687 false,
1688 $attrs
1689 );
1690 } else {
1691 $image_element = $this->generate_image_element( $image_url, array_merge( $attrs, [ 'alt' => $custom_alt_text ] ) );
1692 }
1693
1694 if ( $mobile_background ) {
1695 $background_classes[] = 'cbb-mobile-image';
1696 $attrs['class'] .= ' is-mobile-image';
1697
1698 $mobile_image = '';
1699 if ( $mobile_background['id'] ?? false ) {
1700 $alt = trim( wp_strip_all_tags( get_post_meta( $mobile_background['id'], '_wp_attachment_image_alt', true ) ) );
1701 $attrs['alt'] = $alt ? $alt : $custom_alt_text;
1702
1703 $mobile_image = wp_get_attachment_image(
1704 $mobile_background['id'],
1705 $image_size,
1706 false,
1707 $attrs
1708 );
1709 }
1710
1711 if ( ! $mobile_image ) {
1712 $mobile_image = $this->generate_image_element( $mobile_background['url'], array_merge( $attrs, [ 'alt' => $custom_alt_text ] ) );
1713 }
1714
1715 $image_element .= $mobile_image;
1716 }
1717 } else {
1718 $image_as_background = $this->get_background_image_url( $image_url );
1719 $background_styles = [
1720 'background-image' => 'url(' . $image_as_background . ')',
1721 'background-position' => $background_position,
1722 ];
1723
1724 if ( ! empty( $settings['isFixed'] ) ) {
1725 $background_styles['background-attachment'] = 'fixed';
1726 }
1727
1728 $background_styles['background-repeat'] = $settings['repeat'] ?? 'no-repeat';
1729 $background_styles['background-size'] = $settings['size'] ?? 'cover';
1730
1731 if ( $mobile_background ) {
1732 $background_styles['--cbb-mobile-background'] = "url('{$this->get_background_image_url( $mobile_background['url'] )}')";
1733 $background_classes[] = 'cbb-mobile-background';
1734 }
1735 }
1736
1737 $animation_type = $settings['animation']['type'] ?? '';
1738 $animation_settings = $settings['animation']['settings'] ?? [];
1739 if ( $animation_type && 'none' !== $animation_type ) {
1740 $background_classes[] = $animation_type;
1741
1742 if ( in_array( $animation_type, [ 'bg-scroll-horizontal', 'bg-scroll-vertical' ], true ) ) {
1743 if ( ! ( $settings['isImgElement'] ?? false ) ) {
1744 if ( $this->has_animation_setting_value( 'scroll', 'duration', $animation_settings ) ) {
1745 $background_styles['animation-duration'] = $this->get_animation_setting_value( 'scroll', 'duration', $animation_settings ) . 's';
1746 }
1747
1748 if ( $background_styles['background-repeat'] !== 'repeat' ) {
1749 $background_styles['background-repeat'] = 'bg-scroll-horizontal' === $animation_type ? 'repeat-x' : 'repeat-y';
1750 }
1751
1752 if ( $this->has_animation_setting_value( 'scroll', 'reverseDirection', $animation_settings ) ) {
1753 $background_classes[] = 'is-reverse';
1754 }
1755 }
1756 } elseif ( 'bg-parallax' === $animation_type ) {
1757 unset( $background_styles['background-attachment'] );
1758
1759 $dataset['data-speed'] = $this->get_animation_setting_value( 'parallax', 'speed', $animation_settings, 0.5 );
1760
1761 if ( $this->get_animation_setting_value( 'parallax', 'opacity', $animation_settings, '' ) ) {
1762 $dataset['data-opacity'] = 'true';
1763 }
1764
1765 if ( $this->get_animation_setting_value( 'parallax', 'disableParallax', $animation_settings, '' ) ) {
1766 $dataset['data-disable-parallax'] = 'true';
1767
1768 $breakpoint = $this->get_animation_setting_value( 'parallax', 'disableParallaxBreakpoint', $animation_settings );
1769 if ( ! $breakpoint ) {
1770 $breakpoint = 767;
1771 }
1772
1773 $dataset['data-disable-parallax-breakpoint'] = $breakpoint;
1774 }
1775 } elseif ( 'bg-zoom' === $animation_type ) {
1776 if ( isset( $animation_settings['zoom']['initialScale'] ) ) {
1777 $background_styles['--cbb--zoom-initial-scale'] = $animation_settings['zoom']['initialScale'];
1778 }
1779 if ( isset( $animation_settings['zoom']['scale'] ) ) {
1780 $background_styles['--cbb--zoom-scale'] = $animation_settings['zoom']['scale'];
1781 }
1782 if ( isset( $animation_settings['zoom']['duration'] ) ) {
1783 $background_styles['--cbb--zoom-duration'] = $animation_settings['zoom']['duration'] . 's';
1784 }
1785 if ( isset( $animation_settings['zoom']['timingFunction'] ) ) {
1786 $background_styles['--cbb--zoom-timing-function'] = $animation_settings['zoom']['timingFunction'];
1787 }
1788
1789 $zoom_event = $animation_settings['zoom']['event'] ?? 'hover';
1790 $background_classes[] = "bg-zoom-{$zoom_event}";
1791
1792 if ( 'reveal' === $zoom_event ) {
1793 $dataset['data-reveal-animation'] = esc_attr(
1794 wp_json_encode(
1795 [
1796 'name' => 'bgZoom',
1797 'animation' => 'bgZoom var(--cbb--zoom-duration) var(--cbb--zoom-timing-function)',
1798 'multipleTimes' => $animation_settings['zoom']['multipleTimes'] ?? false,
1799 'forwards' => true,
1800 ]
1801 )
1802 );
1803 }
1804
1805 if ( ! empty( $animation_settings['zoom']['withOpacity'] ) ) {
1806 $background_styles['--cbb--zoom-initial-opacity'] = $animation_settings['zoom']['initialOpacity'] ?? 0;
1807 $background_styles['--cbb--zoom-opacity'] = $animation_settings['zoom']['opacity'] ?? 1;
1808 }
1809 } elseif ( 'bg-zoomIn' === $animation_type ) {
1810 if ( isset( $animation_settings['zoomIn']['initialScale'] ) ) {
1811 $background_styles['--cbb--zoom-initial-scale'] = $animation_settings['zoomIn']['initialScale'];
1812 }
1813 if ( isset( $animation_settings['zoomIn']['duration'] ) ) {
1814 $background_styles['--cbb--zoom-duration'] = $animation_settings['zoomIn']['duration'] . 's';
1815 }
1816 if ( isset( $animation_settings['zoomIn']['timingFunction'] ) ) {
1817 $background_styles['--cbb--zoom-timing-function'] = $animation_settings['zoomIn']['timingFunction'];
1818 }
1819
1820 if ( ! empty( $animation_settings['zoomIn']['withOpacity'] ) ) {
1821 $background_styles['--cbb--zoom-initial-opacity'] = $animation_settings['zoomIn']['initialOpacity'] ?? 0;
1822 $background_styles['--cbb--zoom-opacity'] = $animation_settings['zoomIn']['opacity'] ?? 1;
1823 }
1824
1825 $dataset['data-reveal-animation'] = esc_attr(
1826 wp_json_encode(
1827 [
1828 'name' => 'bgZoomIn',
1829 'animation' => 'bgZoomIn var(--cbb--zoom-duration) var(--cbb--zoom-timing-function)',
1830 'multipleTimes' => $animation_settings['zoomIn']['multipleTimes'] ?? false,
1831 'forwards' => true,
1832 ]
1833 )
1834 );
1835 }
1836 }
1837
1838 $background_style = $background_styles ? array_reduce(
1839 array_keys( $background_styles ),
1840 function ( $carry, $key ) use ( $background_styles ) {
1841 return $carry . $key . ':' . $background_styles[ $key ] . ';';
1842 },
1843 ''
1844 ) : '';
1845
1846 if ( $background_style ) {
1847 $background_style = ' style="' . $background_style . '"';
1848 }
1849
1850 // Get image full size.
1851 $image_full_size = $is_internal_image ? $this->generate_image_full_attributes( $image_id ) : false;
1852 $dataset['data-src'] = $image_full_size ? $image_full_size['src'] : $image_url;
1853 if ( $image_full_size ) {
1854 $dataset['data-width'] = $image_full_size['width'];
1855 $dataset['data-height'] = $image_full_size['height'];
1856 }
1857
1858 $data_attribute = $dataset ? array_reduce(
1859 array_keys( $dataset ),
1860 function ( $carry, $key ) use ( $dataset ) {
1861 return $carry . ' ' . $key . '="' . esc_attr( $dataset[ $key ] ) . '"';
1862 },
1863 ' '
1864 ) : '';
1865
1866 $background_image = sprintf( '<div class="%1$s"%2$s %3$s>%4$s</div>', \implode( ' ', $background_classes ), $background_style, $data_attribute, $image_element );
1867
1868 if ( $animation_type && 'none' !== $animation_type ) {
1869 $block_class .= ' js-animation-' . $animation_type;
1870 }
1871
1872 return [
1873 'background_markup' => $background_image,
1874 'block_class' => $block_class,
1875 ];
1876 }
1877
1878 /**
1879 * Generate data for full size of an image
1880 *
1881 * @param int $image_id
1882 * @return array
1883 */
1884 private function generate_image_full_attributes( $image_id ) {
1885 $image = wp_get_attachment_image_src( $image_id, 'full' );
1886 if ( $image ) {
1887 list( $src, $width, $height ) = $image;
1888
1889 return [
1890 'src' => $src,
1891 'width' => $width,
1892 'height' => $height,
1893 ];
1894 }
1895
1896 return [];
1897 }
1898
1899 /**
1900 * Generate img tag from an image url
1901 *
1902 * @param string $image_url
1903 * @param array $attrs
1904 * @return string
1905 */
1906 private function generate_image_element( $image_url, $attrs ) {
1907 $attr_html = '';
1908 foreach ( $attrs as $name => $value ) {
1909 $attr_html .= " $name=" . '"' . esc_attr( $value ) . '"';
1910 }
1911
1912 // phpcs:ignore PluginCheck.CodeAnalysis.ImageFunctions.NonEnqueuedImage
1913 return sprintf( '<img src="%1$s"%2$s/>', esc_attr( $image_url ), $attr_html );
1914 }
1915
1916 /**
1917 * Get image as background url
1918 *
1919 * @param string $image_url
1920 * @return string
1921 */
1922 private function get_background_image_url( $image_url ) {
1923 return strpos( $image_url, 'data:image/svg+xml' ) === 0 ? '&quot;' . esc_attr( $image_url ) . '&quot;' : esc_attr( $image_url );
1924 }
1925
1926 /**
1927 * Render animations
1928 *
1929 * @param string $block_content
1930 * @param array $block
1931 * @param WP_Block $block_instance
1932 * @return string
1933 */
1934 public function build_block_animations( $block_content, $block, $block_instance ) {
1935 // There is no animations value.
1936 if ( ! isset( $block['attrs']['boldblocks'] ) || ! isset( $block['attrs']['boldblocks']['animations'] ) ) {
1937 return $block_content;
1938 }
1939
1940 // Get animations.
1941 $animations = $block['attrs']['boldblocks']['animations'];
1942 if ( ! is_array( $animations ) ) {
1943 return $block_content;
1944 }
1945
1946 $animation_names = $this->get_animation_names();
1947
1948 $animation_object = array_reduce(
1949 $animations,
1950 function ( $accumulator, $animation ) use ( $animation_names ) {
1951 $name = $animation['name'] ?? '';
1952 $duration = $animation['duration'] ?? '';
1953
1954 if ( ! $name || ! $duration ) {
1955 return $accumulator;
1956 }
1957
1958 $delay = $animation['delay'] ?? 0;
1959 $repeat = $animation['repeat'] ?? 1;
1960 $infinite = $animation['infinite'] ?? false;
1961 $timing_function = $animation['timingFunction'] ?? '';
1962 $animation_name = isset( $animation_names[ $name ]['name'] ) ? $animation_names[ $name ]['name'] : $name;
1963 if ( ! $timing_function && isset( $animation_names[ $name ]['timingFunction'] ) ) {
1964 $timing_function = $animation_names[ $name ]['timingFunction'];
1965 }
1966 $timing_function = $timing_function ? " $timing_function" : '';
1967 $count = $infinite ? 'infinite' : $repeat;
1968
1969 $animation_string = "$animation_name {$duration}s{$timing_function} {$delay}s $count";
1970
1971 // Save animation.
1972 $accumulator['animation'] = $accumulator['animation']
1973 ? "{$accumulator['animation']}, $animation_string"
1974 : $animation_string;
1975
1976 // Save the name.
1977 $accumulator['name'] = $accumulator['name']
1978 ? "{$accumulator['name']},$name"
1979 : $name;
1980
1981 return $accumulator;
1982 },
1983 [
1984 'animation' => '',
1985 'name' => '',
1986 ]
1987 );
1988
1989 if ( $animation_object['name'] && $animation_object['animation'] ) {
1990 $animation_object['multipleTimes'] = $block['attrs']['boldblocks']['animateMultipleTimes'] ?? false;
1991
1992 $block_content = $this->add_data_to_block( $block_content, 'data-reveal-animation', esc_attr( wp_json_encode( $animation_object ) ) );
1993 $block_content = $this->add_class_to_block( $block_content, 'animate__waiting' );
1994 }
1995
1996 return $block_content;
1997 }
1998
1999 /**
2000 * Get all predefined animations
2001 *
2002 * @return array
2003 */
2004 private function get_animation_names() {
2005 $animation_names = [
2006 'bounce' => false,
2007 'flash' => false,
2008 'pulse' => [ 'timingFunction' => 'ease-in-out' ],
2009 'rubberBand' => false,
2010 'shakeX' => false,
2011 'shakeY' => false,
2012 'headShake' => [ 'timingFunction' => 'ease-in-out' ],
2013 'swing' => false,
2014 'tada' => false,
2015 'wobble' => false,
2016 'jello' => false,
2017 'heartBeat' => [ 'timingFunction' => 'ease-in-out' ],
2018
2019 'backInDown' => false,
2020 'backInLeft' => false,
2021 'backInRight' => false,
2022 'backInUp' => false,
2023
2024 'bounceIn' => false,
2025 'bounceInDown' => false,
2026 'bounceInLeft' => false,
2027 'bounceInRight' => false,
2028 'bounceInUp' => false,
2029
2030 'fadeIn' => false,
2031 'fadeInDown' => [ 'name' => 'fadeInXY' ],
2032 'fadeInDownBig' => [ 'name' => 'fadeInXY' ],
2033 'fadeInLeft' => [ 'name' => 'fadeInXY' ],
2034 'fadeInLeftBig' => [ 'name' => 'fadeInXY' ],
2035 'fadeInRight' => [ 'name' => 'fadeInXY' ],
2036 'fadeInRightBig' => [ 'name' => 'fadeInXY' ],
2037 'fadeInUp' => [ 'name' => 'fadeInXY' ],
2038 'fadeInUpBig' => [ 'name' => 'fadeInXY' ],
2039 'fadeInTopLeft' => [ 'name' => 'fadeInXY' ],
2040 'fadeInTopRight' => [ 'name' => 'fadeInXY' ],
2041 'fadeInBottomLeft' => [ 'name' => 'fadeInXY' ],
2042 'fadeInBottomRight' => [ 'name' => 'fadeInXY' ],
2043
2044 'fadeInUpSmall' => [ 'name' => 'fadeInXY' ],
2045 'fadeInRightSmall' => [ 'name' => 'fadeInXY' ],
2046 'fadeInDownSmall' => [ 'name' => 'fadeInXY' ],
2047 'fadeInLeftSmall' => [ 'name' => 'fadeInXY' ],
2048
2049 'flip' => false,
2050 'flipInX' => false,
2051 'flipInY' => false,
2052
2053 'lightSpeedInRight' => [ 'timingFunction' => 'ease-out' ],
2054 'lightSpeedInLeft' => [ 'timingFunction' => 'ease-out' ],
2055
2056 'rotateIn' => [ 'name' => 'rotateInZ' ],
2057 'rotateInDownLeft' => [ 'name' => 'rotateInZ' ],
2058 'rotateInDownRight' => [ 'name' => 'rotateInZ' ],
2059 'rotateInUpLeft' => [ 'name' => 'rotateInZ' ],
2060 'rotateInUpRight' => [ 'name' => 'rotateInZ' ],
2061
2062 'jackInTheBox' => false,
2063 'rollIn' => false,
2064
2065 'zoomIn' => false,
2066 'zoomInDown' => [ 'name' => 'zoomInXY' ],
2067 'zoomInLeft' => [ 'name' => 'zoomInXY' ],
2068 'zoomInRight' => [ 'name' => 'zoomInXY' ],
2069 'zoomInUp' => [ 'name' => 'zoomInXY' ],
2070
2071 'slideInDown' => [ 'name' => 'slideInXY' ],
2072 'slideInLeft' => [ 'name' => 'slideInXY' ],
2073 'slideInRight' => [ 'name' => 'slideInXY' ],
2074 'slideInUp' => [ 'name' => 'slideInXY' ],
2075
2076 'slideInDownSmall' => [ 'name' => 'slideInXY' ],
2077 'slideInLeftSmall' => [ 'name' => 'slideInXY' ],
2078 'slideInRightSmall' => [ 'name' => 'slideInXY' ],
2079 'slideInUpSmall' => [ 'name' => 'slideInXY' ],
2080
2081 'spin' => false,
2082 'zoomOutIn' => false,
2083 ];
2084
2085 return $animation_names;
2086 }
2087
2088 /**
2089 * Has a animation setting value or not
2090 *
2091 * @param string $type
2092 * @param string $name
2093 * @param array $settings
2094 * @return mixed
2095 */
2096 private function has_animation_setting_value( $type, $name, $settings ) {
2097 return isset( $settings[ $type ][ $name ] ) || isset( $settings[ $name ] );
2098 }
2099
2100 /**
2101 * Get background animation setting value
2102 *
2103 * @param string $type
2104 * @param string $name
2105 * @param array $settings
2106 * @param mixed $default_val
2107 * @return mixed
2108 */
2109 private function get_animation_setting_value( $type, $name, $settings, $default_val = '' ) {
2110 if ( isset( $settings[ $type ][ $name ] ) ) {
2111 return $settings[ $type ][ $name ];
2112 } else {
2113 return $settings[ $name ] ?? $default_val;
2114 }
2115 }
2116
2117 /**
2118 * Build the video background
2119 *
2120 * @param array $background
2121 * @param string $block_content
2122 * @param array $block
2123 * @param WP_Block $block_instance
2124 * @return string
2125 */
2126 private function build_video_background( $background, $block_content, $block, $block_instance ) {
2127 $settings = $background['settings'] ?? [];
2128 $media = $background['video'] ?? [];
2129 $block_class = 'bb:has-background bb:has-background--video';
2130
2131 $background_position = round( ( $settings['focalPoint']['x'] ?? .5 ) * 100 ) . '% ' . round( ( $settings['focalPoint']['y'] ?? .5 ) * 100 ) . '%';
2132 $object_fit = $settings['objectFit'] ?? 'cover';
2133 if ( empty( $object_fit ) ) {
2134 $object_fit = 'cover';
2135 }
2136
2137 $video_style = 'object-fit:' . $object_fit . ';object-position:' . $background_position . ';';
2138 $block_background = sprintf( '<div class="bb:block-background bb:block-background--video"><video src="%1$s" autoplay="autoplay" muted loop playsinline preload="auto" style="%2$s"/></div>', $media['url'], $video_style );
2139
2140 if ( $media['isPausable'] ?? false ) {
2141 $block_class .= ' cbb-has-video-controls';
2142 $block_background .= '<button class="cbb-play-pause cbb-video-play-pause is-playing"></button>';
2143 }
2144
2145 if ( strpos( $block['innerHTML'] ?? '', $block_class ) === false ) {
2146 $block_content = $this->add_class_to_block( $block_content, $block_class );
2147 }
2148
2149 $block_content = $this->add_inner_content_to_block( $block_content, $block_background );
2150
2151 return $block_content;
2152 }
2153
2154 /**
2155 * Render block overlay
2156 *
2157 * @param string $block_content
2158 * @param array $block
2159 * @param WP_Block $block_instance
2160 * @return string
2161 */
2162 public function build_block_overlay( $block_content, $block, $block_instance ) {
2163 // Ignore admin side.
2164 if ( is_admin() ) {
2165 return $block_content;
2166 }
2167
2168 $block_overlay = $block['attrs']['boldblocks']['overlay'] ?? false;
2169
2170 // There is no overlay value.
2171 if ( ! ( $block_overlay['overlayColor'] ?? false ) ) {
2172 return $block_content;
2173 }
2174
2175 // Ignore legacy markup.
2176 if ( strpos( $block['innerHTML'] ?? '', 'bb:block-overlay' ) !== false ) {
2177 return $block_content;
2178 }
2179
2180 $bg_color = '';
2181 $overlay_color = $block_overlay['overlayColor'] ?? [];
2182 if ( ( $overlay_color['gradientSlug'] ?? false ) || ( $overlay_color['gradientValue'] ?? false ) ) {
2183 if ( $overlay_color['gradientSlug'] ?? false ) {
2184 $bg_color = sprintf( 'var(--wp--preset--gradient--%1$s, %2$s)', $overlay_color['gradientSlug'], $overlay_color['gradientValue'] ?? '' );
2185 } else {
2186 $bg_color = $overlay_color['gradientValue'];
2187 }
2188 } elseif ( ( $overlay_color['slug'] ?? false ) || ( $overlay_color['value'] ?? false ) ) {
2189 if ( $overlay_color['slug'] ?? false ) {
2190 $bg_color = sprintf( 'var(--wp--preset--color--%1$s, %2$s)', $overlay_color['slug'], $overlay_color['value'] ?? '' );
2191 } else {
2192 $bg_color = $overlay_color['value'];
2193 }
2194 }
2195
2196 if ( empty( $bg_color ) ) {
2197 return $block_content;
2198 }
2199
2200 $opacity = absint( $block_overlay['opacity'] ?? 100 );
2201
2202 // Build the markup.
2203 $overlay_markup = sprintf( '<div aria-hidden="true" class="bb:block-overlay" style="background:%1$s;opacity:%2$s;"></div>', $bg_color, $opacity / 100 );
2204
2205 $block_class = 'bb:has-overlay';
2206 if ( strpos( $block['innerHTML'] ?? '', $block_class ) === false ) {
2207 $block_content = $this->add_class_to_block( $block_content, $block_class );
2208 }
2209
2210 $block_content = $this->add_inner_content_to_block( $block_content, $overlay_markup );
2211
2212 return $block_content;
2213 }
2214
2215 /**
2216 * Render accordions block data
2217 *
2218 * @param array $parsed_block
2219 * @return array
2220 */
2221 public function render_accordion_block_data( $parsed_block ) {
2222 $block_type = \WP_Block_Type_Registry::get_instance()->get_registered( $parsed_block['blockName'] );
2223
2224 if ( ! $block_type ) {
2225 return $parsed_block;
2226 }
2227
2228 $layout_type = $block_type->supports['layoutType'] ?? '';
2229 if ( ! in_array( $layout_type, [ 'accordion', 'accordionItem' ], true ) ) {
2230 return $parsed_block;
2231 }
2232
2233 static $accordion_counter = 0;
2234 static $accordion_id = '';
2235 static $item_index = [];
2236
2237 if ( 'accordion' === $layout_type ) {
2238 ++$accordion_counter;
2239 $processor = new \WP_HTML_Tag_Processor( $parsed_block['innerHTML'] ?? '' );
2240
2241 $accordion_id = '';
2242 if ( $processor->next_tag() ) {
2243 $accordion_id = $processor->get_attribute( 'id' );
2244 }
2245
2246 if ( ! $accordion_id ) {
2247 $accordion_id = 'accordion-' . $accordion_counter;
2248 }
2249 } elseif ( 'accordionItem' === $layout_type ) {
2250 if ( ! isset( $item_index[ $accordion_id ] ) ) {
2251 $item_index[ $accordion_id ] = 1;
2252 } else {
2253 ++$item_index[ $accordion_id ];
2254 }
2255
2256 $parsed_block['attrs']['boldblocks']['accordionItem']['block_id'] = $accordion_id . '-item-' . $item_index[ $accordion_id ];
2257 }
2258
2259 return $parsed_block;
2260 }
2261
2262 /**
2263 * Render accordion item blocks
2264 *
2265 * @param string $block_content
2266 * @param array $block
2267 * @param WP_Block $block_instance
2268 * @return string
2269 */
2270 public function render_accordion_item_block( $block_content, $block, $block_instance ) {
2271 // Ignore admin side.
2272 if ( is_admin() ) {
2273 return $block_content;
2274 }
2275
2276 // Bail if it is not an accordion item block.
2277 if ( 'accordionItem' !== ( $block_instance->block_type->supports['layoutType'] ?? '' ) ) {
2278 return $block_content;
2279 }
2280
2281 $block_id = $block['attrs']['boldblocks']['accordionItem']['block_id'] ?? '';
2282 $block_id_selector = '#' . $block_id;
2283
2284 if ( $block_id ) {
2285 $processor = new \WP_HTML_Tag_Processor( $block_content );
2286
2287 while ( $processor->next_tag() ) {
2288 $tag_class = $processor->get_attribute( 'class' );
2289 if ( ! $tag_class ) {
2290 continue;
2291 }
2292
2293 if ( strpos( $tag_class, 'accordion-header' ) !== false ) {
2294 $processor->set_attribute( 'data-toggle-target', $block_id_selector );
2295 $processor->set_attribute( 'aria-controls', $block_id );
2296 } elseif ( strpos( $tag_class, 'accordion-link' ) !== false ) {
2297 $processor->set_attribute( 'href', $block_id_selector );
2298 } elseif ( strpos( $tag_class, 'accordion-collapse' ) !== false ) {
2299 $processor->set_attribute( 'id', $block_id );
2300 $processor->set_attribute( 'role', 'region' );
2301 break;
2302 }
2303 }
2304
2305 $block_content = $processor->get_updated_html();
2306 }
2307
2308 return $block_content;
2309 }
2310
2311 /**
2312 * Render grid style for the query loop's grid layout
2313 *
2314 * @param string $block_content
2315 * @param array $block
2316 * @param WP_Block $block_instance
2317 * @return string
2318 */
2319 public function render_query_loop_grid_style( $block_content, $block, $block_instance ) {
2320 // Ignore admin side.
2321 if ( is_admin() ) {
2322 return $block_content;
2323 }
2324
2325 // If this is a core/query block that has grid layout.
2326 $grid_data = $this->get_query_loop_grid_data( $block, $block_instance );
2327 if ( ! $grid_data ) {
2328 return $block_content;
2329 }
2330
2331 $display_layout = $grid_data['displayType'] ?? '';
2332 if ( 'responsiveGrid' === $display_layout && ( $grid_data['grid'] ?? false ) ) {
2333 // Buil selector.
2334 $selector = 'cbb-query-' . $this->get_query_loop_id( $block );
2335
2336 // Get responsive settings.
2337 $breakpoints = $this->get_breakpoints();
2338
2339 // Get custom style.
2340 $block_style = $this->get_query_grid_style(
2341 [
2342 'selector' => ".{$selector} > ul",
2343 'block' => $block,
2344 'data' => $grid_data['grid'],
2345 'breakpoints' => $breakpoints,
2346 ]
2347 );
2348
2349 if ( empty( $block_style ) ) {
2350 return $block_content;
2351 }
2352
2353 $handle = $selector;
2354 // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
2355 wp_register_style( $handle, '' );
2356 wp_add_inline_style( $handle, $block_style );
2357 wp_enqueue_style( $handle );
2358
2359 // Add selector to block wrapper element.
2360 $block_content = $this->add_class_to_block( $block_content, $selector );
2361 }
2362
2363 return $block_content;
2364 }
2365
2366 /**
2367 * Build an unique ID for a query loop
2368 *
2369 * @param array $block
2370 * @return string
2371 */
2372 private function get_query_loop_id( $block ) {
2373 $query_id = $block['attrs']['queryId'] ?? 1;
2374 $key = "id_{$query_id}";
2375 if ( ! isset( $this->query_ids[ $key ] ) ) {
2376 $this->query_ids[ $key ] = 1;
2377 } else {
2378 ++$this->query_ids[ $key ];
2379 }
2380
2381 return $query_id . '-' . $this->query_ids[ $key ];
2382 }
2383
2384 /**
2385 * Get grid layout data
2386 *
2387 * @param array $block
2388 * @param WP_Block $block_instance
2389 * @return array
2390 */
2391 private function get_query_loop_grid_data( $block, $block_instance ) {
2392 $data = [];
2393 $old_layout_type = $block['attrs']['displayLayout']['type'] ?? '';
2394 if ( 'grid' === $old_layout_type ) {
2395 // Convert to the new name.
2396 $old_layout_type = 'responsiveGrid';
2397 }
2398 $data['displayType'] = $old_layout_type;
2399 $data['grid'] = $block['attrs']['boldblocks']['grid'] ?? [];
2400
2401 if ( $block_instance->block_type->api_version >= 3 ) {
2402 $post_template = $this->get_nested_post_template( $block_instance );
2403
2404 if ( $post_template ) {
2405 if ( 'responsiveGrid' === ( $post_template->attributes['boldblocks']['layout']['type'] ?? '' ) ) {
2406 $data['displayType'] = 'responsiveGrid';
2407 $data['grid'] = $post_template->attributes['boldblocks']['grid'] ?? [];
2408 }
2409 }
2410 }
2411
2412 return $data;
2413 }
2414
2415 /**
2416 * Build grid style
2417 *
2418 * @param array $args
2419 * @return string
2420 */
2421 private function get_query_grid_style( $args ) {
2422 $selector = $args['selector'];
2423 $data = $args['data'];
2424
2425 // None-responsive style.
2426 $style = "{$selector}{display:grid;padding:0;margin:0;}{$selector} > li{padding:0;margin:0;}{$selector} > li + li{margin-block-start:0;margin-block-end:0;}";
2427
2428 // Columns style.
2429 $style .= $this->build_query_loop_responsive_style(
2430 array_merge(
2431 $args,
2432 [
2433 'setting_value' => $data['columns'] ?? null,
2434 'func_build_responsive_style' => function ( $args ) {
2435 $style_array = [];
2436 $value = $args['value'];
2437 if ( $value ) {
2438 $style_array = [
2439 '--cbb--grid--columns' => $value,
2440 ];
2441 }
2442
2443 return $style_array;
2444 },
2445 'func_build_dependent_style' => function ( $args ) {
2446 $responsive_styles = $args['responsive_styles'] ?? [];
2447 $selector = $args['selector'];
2448 $breakpoints = $args['breakpoints'] ?? [];
2449 $style = "{$selector}{grid-template-columns:var(--cbb--grid--columns);}";
2450
2451 foreach ( $responsive_styles as $breakpoint => $attribute_array ) {
2452 $media_query = $breakpoints[ $breakpoint ]['minQuery'] ?? '';
2453 if ( $media_query ) {
2454 $style = \str_replace( '##CONTENT##', $style, $media_query );
2455 }
2456
2457 break;
2458 }
2459
2460 return $style;
2461 },
2462 ]
2463 )
2464 );
2465
2466 // Rows style.
2467 $style .= $this->build_query_loop_responsive_style(
2468 array_merge(
2469 $args,
2470 [
2471 'setting_value' => $data['rows'] ?? null,
2472 'func_build_responsive_style' => function ( $args ) {
2473 $style_array = [];
2474 $value = $args['value'];
2475 if ( $value ) {
2476 $style_array = [
2477 '--cbb--grid--rows' => $value,
2478 ];
2479 }
2480
2481 return $style_array;
2482 },
2483 'func_build_dependent_style' => function ( $args ) {
2484 $responsive_styles = $args['responsive_styles'] ?? [];
2485 $selector = $args['selector'];
2486 $breakpoints = $args['breakpoints'] ?? [];
2487 $style = "{$selector}{grid-template-rows:var(--cbb--grid--rows);}";
2488
2489 foreach ( $responsive_styles as $breakpoint => $attribute_array ) {
2490 $media_query = $breakpoints[ $breakpoint ]['minQuery'] ?? '';
2491 if ( $media_query ) {
2492 $style = \str_replace( '##CONTENT##', $style, $media_query );
2493 }
2494
2495 break;
2496 }
2497
2498 return $style;
2499 },
2500 ]
2501 )
2502 );
2503
2504 // Auto rows style.
2505 $style .= $this->build_query_loop_responsive_style(
2506 array_merge(
2507 $args,
2508 [
2509 'setting_value' => $data['autoRows'] ?? null,
2510 'func_build_responsive_style' => function ( $args ) {
2511 $style_array = [];
2512 $value = $args['value'];
2513 if ( $value ) {
2514 $style_array = [
2515 '--cbb--grid--auto-rows' => $value,
2516 ];
2517 }
2518
2519 return $style_array;
2520 },
2521 'func_build_dependent_style' => function ( $args ) {
2522 $responsive_styles = $args['responsive_styles'] ?? [];
2523 $selector = $args['selector'];
2524 $breakpoints = $args['breakpoints'] ?? [];
2525 $style = "{$selector}{grid-auto-rows:var(--cbb--grid--auto-rows);}";
2526
2527 foreach ( $responsive_styles as $breakpoint => $attribute_array ) {
2528 $media_query = $breakpoints[ $breakpoint ]['minQuery'] ?? '';
2529 if ( $media_query ) {
2530 $style = \str_replace( '##CONTENT##', $style, $media_query );
2531 }
2532
2533 break;
2534 }
2535
2536 return $style;
2537 },
2538 ]
2539 )
2540 );
2541
2542 // Gap style.
2543 $style .= $this->build_query_loop_responsive_style(
2544 array_merge(
2545 $args,
2546 [
2547 'setting_value' => $data['gap'] ?? null,
2548 'func_build_responsive_style' => function ( $args ) {
2549 $style_array = [];
2550 $value = $args['value'];
2551 if ( $value && is_array( $value ) ) {
2552 $row = $value['row'] ?? null;
2553
2554 if ( $this->is_valid_value( $row ) ) {
2555 $style_array['--cbb--grid--gap--row'] = $row;
2556 }
2557
2558 $column = $value['column'] ?? null;
2559
2560 if ( $this->is_valid_value( $column ) ) {
2561 $style_array['--cbb--grid--gap--column'] = $column;
2562 }
2563 }
2564
2565 return $style_array;
2566 },
2567 'func_build_dependent_style' => function ( $args ) {
2568 $responsive_styles = $args['responsive_styles'] ?? [];
2569 $selector = $args['selector'];
2570 $breakpoints = $args['breakpoints'] ?? [];
2571 $style = '';
2572
2573 foreach ( $responsive_styles as $breakpoint => $style_array ) {
2574 if ( strpos( $style, 'row-gap' ) !== false && strpos( $style, 'column-gap' ) !== false ) {
2575 break;
2576 }
2577
2578 $media_query = $breakpoints[ $breakpoint ]['minQuery'] ?? '';
2579
2580 if ( strpos( $style, 'row-gap' ) === false && isset( $style_array['--cbb--grid--gap--row'] ) ) {
2581 $row_style = "{$selector}{row-gap:var(--cbb--grid--gap--row);}";
2582 if ( $media_query ) {
2583 $style .= \str_replace( '##CONTENT##', $row_style, $media_query );
2584 } else {
2585 $style .= $row_style;
2586 }
2587 }
2588 if ( strpos( $style, 'column-gap' ) === false && isset( $style_array['--cbb--grid--gap--column'] ) ) {
2589 $column_style = "{$selector}{column-gap:var(--cbb--grid--gap--column);}";
2590 if ( $media_query ) {
2591 $style .= \str_replace( '##CONTENT##', $column_style, $media_query );
2592 } else {
2593 $style .= $column_style;
2594 }
2595 }
2596 }
2597
2598 return $style;
2599 },
2600 ]
2601 )
2602 );
2603
2604 $items = $data['items'] ?? [];
2605 if ( $items ) {
2606 foreach ( $items as $item_key => $item_value ) {
2607 $item_index = str_replace( 'item', '', $item_key );
2608
2609 // Column style.
2610 $style .= $this->build_query_loop_responsive_style(
2611 array_merge(
2612 $args,
2613 [
2614 'setting_value' => $item_value['columnSpan'] ?? [],
2615 'setting_name' => 'columnSpan',
2616 'selector' => "{$selector} > li:nth-of-type({$item_index})",
2617 'func_build_responsive_style' => [ $this, 'build_grid_item_responsive_style' ],
2618 'func_build_dependent_style' => [ $this, 'build_grid_item_dependent_style' ],
2619 ]
2620 )
2621 );
2622
2623 // Row style.
2624 $style .= $this->build_query_loop_responsive_style(
2625 array_merge(
2626 $args,
2627 [
2628 'setting_value' => $item_value['rowSpan'] ?? [],
2629 'setting_name' => 'rowSpan',
2630 'selector' => "{$selector} > li:nth-of-type({$item_index})",
2631 'func_build_responsive_style' => [ $this, 'build_grid_item_responsive_style' ],
2632 'func_build_dependent_style' => [ $this, 'build_grid_item_dependent_style' ],
2633 ]
2634 )
2635 );
2636 }
2637 }
2638
2639 return $style;
2640 }
2641
2642 /**
2643 * Build responsive style for grid item
2644 *
2645 * @param array $args
2646 * @return array
2647 */
2648 private function build_grid_item_responsive_style( $args ) {
2649 $setting_name = $args['setting_name'] ?? 'columnSpan';
2650 $variable_name = 'columnSpan' === $setting_name ? '--cbb--grid-item--column' : '--cbb--grid-item--row';
2651 $style_array = [];
2652 $value = $args['value'];
2653 if ( $value && is_array( $value ) ) {
2654 $span = $value['span'] ?? null;
2655 $start = $value['start'] ?? null;
2656
2657 if ( $this->is_valid_value( $start ) || $this->is_valid_value( $span ) ) {
2658 if ( ! $this->is_valid_value( $start ) ) {
2659 $start = 'auto';
2660 }
2661
2662 if ( ! $this->is_valid_value( $span ) ) {
2663 $span = 'auto';
2664 }
2665 if ( $span !== 'auto' && $span > 0 ) {
2666 $span = "span {$span}";
2667 }
2668
2669 $style_array[ $variable_name ] = "{$start} / {$span}";
2670 }
2671
2672 if ( 'columnSpan' === $setting_name ) {
2673 $order = $value['order'] ?? null;
2674 if ( is_numeric( $order ) ) {
2675 $style_array['--cbb--grid-item--order'] = $order;
2676 }
2677 }
2678 }
2679
2680 return $style_array;
2681 }
2682
2683 /**
2684 * Build dependent style for grid item
2685 *
2686 * @param array $args
2687 * @return array
2688 */
2689 private function build_grid_item_dependent_style( $args ) {
2690 $setting_name = $args['setting_name'] ?? 'columnSpan';
2691 $variable_name = 'columnSpan' === $setting_name ? '--cbb--grid-item--column' : '--cbb--grid-item--row';
2692 $css_name = 'columnSpan' === $setting_name ? 'grid-column' : 'grid-row';
2693
2694 $responsive_styles = $args['responsive_styles'] ?? [];
2695 $selector = $args['selector'];
2696 $breakpoints = $args['breakpoints'] ?? [];
2697 $style = '';
2698
2699 foreach ( $responsive_styles as $breakpoint => $style_array ) {
2700 if ( 'columnSpan' === $setting_name ) {
2701 if ( strpos( $style, $css_name ) !== false && strpos( $style, 'order' ) !== false ) {
2702 break;
2703 }
2704 } elseif ( strpos( $style, $css_name ) !== false ) {
2705 break;
2706 }
2707
2708 $media_query = $breakpoints[ $breakpoint ]['minQuery'] ?? '';
2709
2710 if ( strpos( $style, $css_name ) === false && isset( $style_array[ $variable_name ] ) ) {
2711 $column_style = "{$selector}{{$css_name}:var($variable_name);}";
2712 if ( $media_query ) {
2713 $style .= \str_replace( '##CONTENT##', $column_style, $media_query );
2714 } else {
2715 $style .= $column_style;
2716 }
2717 }
2718 if ( 'columnSpan' === $setting_name && strpos( $style, 'order' ) === false && isset( $style_array['--cbb--grid-item--order'] ) ) {
2719 $order_style = "{$selector}{order:var(--cbb--grid-item--order);}";
2720 if ( $media_query ) {
2721 $style .= \str_replace( '##CONTENT##', $order_style, $media_query );
2722 } else {
2723 $style .= $order_style;
2724 }
2725 }
2726 }
2727
2728 return $style;
2729 }
2730
2731 /**
2732 * Render carousel layout for query loop blocks
2733 *
2734 * @param string $block_content
2735 * @param array $block
2736 * @param WP_Block $block_instance
2737 * @return string
2738 */
2739 public function render_query_loop_carousel_layout( $block_content, $block, $block_instance ) {
2740 // Ignore admin side.
2741 if ( is_admin() ) {
2742 return $block_content;
2743 }
2744
2745 $post_template = $this->get_nested_post_template( $block_instance );
2746 if ( $post_template ) {
2747 // If this has carousel layout.
2748 if ( 'carousel' === ( $post_template->attributes['boldblocks']['layout']['type'] ?? '' ) ) {
2749 // Build carousel settings.
2750 $carousel_settings = $post_template->attributes['boldblocks']['carousel'] ?? [];
2751 if ( $carousel_settings ) {
2752 if ( ! $this->block_has_attribute( 'data-carousel-settings', $block_content ) ) {
2753 $carousel_attr = $this->build_carousel_settings( $carousel_settings, true );
2754
2755 if ( $carousel_attr ) {
2756 $block_content = $this->add_data_to_block( $block_content, 'data-carousel-settings', esc_attr( $carousel_attr ) );
2757 }
2758 }
2759
2760 // Buil selector.
2761 $selector = 'cbb-query-' . $this->get_query_loop_id( $block );
2762
2763 // Get responsive settings.
2764 $breakpoints = $this->get_breakpoints();
2765
2766 // Get custom style.
2767 $block_style = $this->get_carousel_preload_style(
2768 '',
2769 [
2770 'selector' => ".{$selector}",
2771 'settings' => [ 'carousel' => $carousel_settings ],
2772 'block_layout' => 'carousel',
2773 'breakpoints' => $breakpoints,
2774 ]
2775 );
2776
2777 $carousel_class = 'js-carousel-layout cbb-carousel-preload';
2778
2779 if ( $block_style ) {
2780 $handle = $selector;
2781 // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
2782 wp_register_style( $handle, '' );
2783 wp_add_inline_style( $handle, $block_style );
2784 wp_enqueue_style( $handle );
2785
2786 $carousel_class .= ' ' . $selector;
2787 }
2788
2789 if ( $carousel_settings['displayLoader'] ?? false ) {
2790 $carousel_class .= ' has-preloader';
2791
2792 if ( 'coverflow' === ( $carousel_settings['effect'] ?? '' ) ) {
2793 $carousel_class .= ' is-fading-loader';
2794 }
2795
2796 // Add preloader.
2797 $block_content = $this->add_inner_content_to_block( $block_content, $this->get_carousel_preloader_markup( $block, $block_instance ) );
2798 }
2799
2800 // Add selector to block wrapper element.
2801 $block_content = $this->add_class_to_block( $block_content, $carousel_class );
2802 }
2803 }
2804 }
2805
2806 return $block_content;
2807 }
2808
2809 /**
2810 * Add preload style for carousels
2811 *
2812 * @param string $style
2813 * @param array $args
2814 */
2815 public function get_carousel_preload_style( $style, $args ) {
2816 if ( 'carousel' === ( $args['block_layout'] ?? '' ) ) {
2817 $preload_style = '';
2818 $value = $args['settings']['carousel'] ?? false;
2819 $selector = $args['selector'];
2820 $breakpoints = $args['breakpoints'];
2821 if ( $value && is_array( $value ) ) {
2822 if ( $value['enableResponsive'] ?? false ) {
2823 $setting_value = $value['breakpoints'] ?? [];
2824 $responsive_styles = [];
2825 foreach ( $setting_value as $breakpoint => $value_by_breakpoint ) {
2826 $value = null;
2827 if ( $this->is_valid_value( $value_by_breakpoint['value'] ?? null ) ) {
2828 $value = $value_by_breakpoint['value'];
2829 } elseif ( isset( $value_by_breakpoint['inherit'] ) && is_string( $value_by_breakpoint['inherit'] ) ) {
2830 $value = $setting_value[ $value_by_breakpoint['inherit'] ]['value'] ?? null;
2831 }
2832
2833 if ( $this->is_valid_value( $value ) ) {
2834 $style_by_breakpoint = $this->build_carousel_preload_style( $value );
2835 if ( $style_by_breakpoint ) {
2836 $responsive_styles[ $breakpoint ] = $style_by_breakpoint;
2837 }
2838 }
2839 }
2840
2841 $this->sort_styles( $responsive_styles );
2842
2843 $last_responsive_style = '';
2844 foreach ( $responsive_styles as $breakpoint => $style_by_breakpoint ) {
2845 if ( ! $style_by_breakpoint || ! is_array( $style_by_breakpoint ) ) {
2846 continue;
2847 }
2848
2849 $responsive_style = '';
2850 foreach ( $style_by_breakpoint as $attr_key => $attr_value ) {
2851 $responsive_style .= "{$attr_key}:{$attr_value};";
2852 }
2853
2854 if ( $responsive_style !== $last_responsive_style ) {
2855 $style_with_selector = "{$selector}{{$responsive_style}}";
2856 $media_query = $breakpoints[ $breakpoint ]['minQuery'] ?? '';
2857 if ( $media_query ) {
2858 $preload_style .= \str_replace( '##CONTENT##', $style_with_selector, $media_query );
2859 } else {
2860 $preload_style .= $style_with_selector;
2861 }
2862 $last_responsive_style = $responsive_style;
2863 }
2864 }
2865 } else {
2866 $style_array = $this->build_carousel_preload_style( $value );
2867 $attr_style = '';
2868 foreach ( $style_array as $attr_key => $attr_value ) {
2869 $attr_style .= "{$attr_key}:{$attr_value};";
2870 }
2871
2872 if ( $attr_style ) {
2873 $preload_style .= "{$selector}{{$attr_style}}";
2874 }
2875 }
2876 }
2877
2878 if ( $preload_style ) {
2879 $style .= $preload_style;
2880 }
2881 }
2882
2883 return $style;
2884 }
2885
2886 /**
2887 * Build preload style for carousels
2888 *
2889 * @param array $value
2890 * @return void
2891 */
2892 private function build_carousel_preload_style( $value ) {
2893 $style_array = [];
2894 $slides_perview = $value['slidesPerView'] ?? 1;
2895 if ( is_array( $slides_perview ) ) {
2896 if ( $slides_perview['auto'] ?? false ) {
2897 $slides_perview = 'auto';
2898 } else {
2899 $slides_perview = $slides_perview['value'] ?? 1;
2900 }
2901 }
2902
2903 if ( $slides_perview !== 'auto' ) {
2904 $gap_count = absint( ceil( $slides_perview ) ) - 1;
2905 if ( $slides_perview <= 0 ) {
2906 $slides_perview = 1;
2907 }
2908 } else {
2909 $gap_count = 0;
2910 }
2911
2912 $space_between = $value['spaceBetween'] ?? 0;
2913 if ( $space_between < 0 ) {
2914 $space_between = 0;
2915 }
2916
2917 $total_gap = $gap_count * $space_between;
2918
2919 $style_array['--cbb-slide-width'] = $slides_perview === 'auto' ? 'auto' : "calc((100% - {$total_gap}px ) / {$slides_perview})";
2920 $style_array['--cbb-carousel-gap'] = "{$space_between}px";
2921
2922 return $style_array;
2923 }
2924
2925 /**
2926 * Build a json string of carousel settings
2927 *
2928 * @param array $carousel_settings
2929 * @param boolean $exclude_li_role
2930 * @return string
2931 */
2932 private function build_carousel_settings( $carousel_settings, $exclude_li_role = false ) {
2933 // phpcs:disable
2934 $dataset = [];
2935
2936 // Speed.
2937 if ( $carousel_settings['speed'] ?? false ) {
2938 $dataset['speed'] = $carousel_settings['speed'];
2939 }
2940
2941 // Loop and rewind.
2942 $dataset['loop'] = false;
2943 $dataset['rewind'] = false;
2944
2945 $loop_type = $carousel_settings['loopType'] ?? '';
2946 if ( 'infinite' === $loop_type ) {
2947 $dataset['loop'] = true;
2948 } elseif ( 'rewind' === $loop_type ) {
2949 $dataset['rewind'] = true;
2950 }
2951
2952 // Autoplay.
2953 $dataset['autoplay'] = false;
2954 $autoplay = $carousel_settings['autoplay'] ?? false;
2955 if ( $autoplay && is_array( $autoplay ) && ( $autoplay['enable'] ?? false ) ) {
2956 unset( $autoplay['enable'] );
2957
2958 if ( !$dataset['loop'] && !$dataset['rewind']) {
2959 $autoplay['stopOnLastSlide'] = true;
2960 }
2961
2962 $dataset['autoplay'] = $autoplay;
2963 }
2964
2965 // Play/pause.
2966 if ( $carousel_settings['playPause']['enable'] ?? false ) {
2967 $dataset['playPause'] = $carousel_settings['playPause'];
2968 }
2969
2970 // Direction.
2971 if ( $carousel_settings['direction'] ?? false ) {
2972 $dataset['direction'] = $carousel_settings['direction'];
2973 }
2974
2975 // Effect.
2976 $effect = $carousel_settings['effect'] ?? 'slide';
2977 // Handle wrong default data.
2978 if ( is_array( $effect ) ) {
2979 $effect = 'slide';
2980 }
2981
2982 if ( $effect ) {
2983 $dataset['effect'] = $effect;
2984 $effectSettings = $carousel_settings['effectSettings'] ?? [];
2985 $settings = $effectSettings[ $effect . 'Effect' ] ?? false;
2986
2987 // Fade.
2988 if ( 'fade' === $effect ) {
2989 $dataset['fadeEffect'] = [ 'crossFade' => true ];
2990 } elseif ( 'coverflow' === $effect ) {
2991 // Coverflow.
2992 if ( is_array( $settings ) ) {
2993 $dataset['coverflowEffect'] = $settings;
2994 } else {
2995 $dataset['coverflowEffect'] = [ 'slideShadows' => false ];
2996 }
2997 } elseif ( 'creative' === $effect ) {
2998 // Creative.
2999 if ( is_array( $settings ) ) {
3000 $dataset['creativeEffect'] = $settings;
3001 } else {
3002 $dataset['creativeEffect'] = [
3003 'prev' => [
3004 'translate' => [ '-20%', 0, -1 ],
3005 ],
3006 'next' => [
3007 'translate' => [ '100%', 0, 0 ],
3008 ],
3009 ];
3010 }
3011
3012 $dataset['direction'] = "horizontal";
3013 }
3014 }
3015
3016 // slidesPerView.
3017 $slidesPerViewDependencies = [
3018 'fade',
3019 'flip',
3020 'cube',
3021 'cards',
3022 'creative',
3023 ];
3024
3025 if ( in_array($effect, $slidesPerViewDependencies, true ) ) {
3026 $dataset['slidesPerView'] = 1;
3027 } else {
3028 $slidesPerView = $this->carousel_refine_slides_per_view( $carousel_settings['slidesPerView'] ?? 1 );
3029 $slidesPerGroup = absint( $carousel_settings['slidesPerGroup'] ?? 0 );
3030
3031 if ( $slidesPerView ) {
3032 $dataset['slidesPerView'] = $slidesPerView;
3033
3034 if ( $slidesPerView >= 2 && $slidesPerGroup ) {
3035 $dataset['slidesPerGroup'] = $slidesPerGroup;
3036 }
3037 }
3038
3039 // spaceBetween.
3040 $spaceBetween = absint( $carousel_settings['spaceBetween'] ?? 0 );
3041 if ( $spaceBetween ) {
3042 $dataset['spaceBetween'] = $spaceBetween;
3043 }
3044
3045 // Breakpoints.
3046 $breakpoint_settings = $this->get_breakpoints();
3047 $breakpoint_values = [
3048 'sm' => $breakpoint_settings['sm']['breakpoint'] ?? 576,
3049 'md' => $breakpoint_settings['md']['breakpoint'] ?? 768,
3050 'lg' => $breakpoint_settings['lg']['breakpoint'] ?? 1024,
3051 ];
3052
3053 $breakpoints = $carousel_settings['breakpoints'] ?? [];
3054 if ( ($carousel_settings['enableResponsive'] ?? false) && is_array( $breakpoints ) ) {
3055 // Mobile first.
3056 $this->sort_styles( $breakpoints );
3057
3058 $breakpoint_atts = [];
3059 foreach ( array_keys( $breakpoints ) as $breakpoint ) :
3060 $value = $breakpoints[ $breakpoint ]['value'] ?? '';
3061 $inherit = $breakpoints[ $breakpoint ]['inherit'] ?? '';
3062
3063 $value_by_breakpoint = false;
3064 if ( $value && is_array( $value ) ) {
3065 $value_by_breakpoint = $value;
3066 }
3067
3068 if ( ! $value_by_breakpoint && is_string( $inherit ) ) {
3069 $inheritValue = $breakpoints[ $inherit ] ?? '';
3070 if ( $inheritValue && is_array( $inheritValue ) ) {
3071 $value_by_breakpoint = $inheritValue;
3072 }
3073 }
3074
3075 if ( is_array( $value_by_breakpoint ) ) {
3076 $value_by_breakpoint['slidesPerView'] = $this->carousel_refine_slides_per_view( $value_by_breakpoint['slidesPerView'] ?? 1);
3077 $breakpoint_atts[ $breakpoint_values[ $breakpoint ] ] = $value_by_breakpoint;
3078 }
3079 endforeach;
3080
3081 if ( $breakpoint_atts ) {
3082 // Replace global params.
3083 $first_breakpoint = array_values($breakpoint_atts)[0];
3084 $dataset['slidesPerView'] = $first_breakpoint['slidesPerView'];
3085 if ($dataset['slidesPerView'] > 1 && ($first_breakpoint['slidesPerGroup'] ?? 0)) {
3086 $dataset['slidesPerGroup'] = $first_breakpoint['slidesPerGroup'];
3087 }
3088 if ( $first_breakpoint['spaceBetween'] ) {
3089 $dataset['spaceBetween'] = $first_breakpoint['spaceBetween'];
3090 }
3091
3092 // Set the breakpoints.
3093 $dataset['breakpoints'] = $breakpoint_atts;
3094 }
3095 } else {
3096 if ( $carousel_settings['oneSlidePerViewInMobile'] ?? false ) {
3097 $dataset['slidesPerView'] = 1;
3098 if ( $slidesPerGroup ) {
3099 $dataset['slidesPerGroup'] = 1;
3100 }
3101
3102 $dataset['breakpoints'] = [
3103 $breakpoint_values['sm'] => [
3104 'slidesPerView' => 1,
3105 'slidesPerGroup' => 1,
3106 ],
3107
3108 $breakpoint_values['md'] => [
3109 'slidesPerView' => $slidesPerView,
3110 'slidesPerGroup' => $slidesPerGroup ? $slidesPerGroup : 1,
3111 ],
3112 ];
3113 }
3114 }
3115 }
3116
3117 // centeredSlides.
3118 $dataset['centeredSlides'] = $carousel_settings['centeredSlides'] ?? false;
3119 if ($dataset['centeredSlides'] && $effect === 'slide' && ($carousel_settings['centeredSlidesSettings']['enable'] ?? '')) {
3120 $dataset['centeredSlidesSettings'] = $carousel_settings['centeredSlidesSettings'];
3121 }
3122
3123 // Pagination.
3124 $pagination = $carousel_settings['pagination'] ?? [];
3125 $dataset['pagination'] = $pagination['enable'] ?? false;
3126 if ( $dataset['pagination'] ) {
3127 $dataset['paginationSettings'] = $pagination;
3128 }
3129
3130 // Navigation.
3131 $navigation = $carousel_settings['navigation'] ?? [];
3132 $dataset['navigation'] = $navigation['enable'] ?? false;
3133 if ( $dataset['navigation'] ) {
3134 $dataset['navigationSettings'] = $navigation;
3135 }
3136
3137 // Scrollbar.
3138 $scrollbar = $carousel_settings['scrollbar'] ?? [];
3139 $dataset['scrollbar'] = $scrollbar['enable'] ?? false;
3140 if ( $dataset['scrollbar'] ) {
3141 $dataset['scrollbarSettings'] = $scrollbar;
3142 }
3143
3144 // Equal height?.
3145 $equalHeight = $carousel_settings['equalHeight'] ?? false;
3146 if ( $equalHeight ) {
3147 // Only add the property when it is true to prevent from invalid content.
3148 $dataset['equalHeight'] = $equalHeight;
3149 }
3150
3151 // Sync carousels.
3152 $thumbs = $carousel_settings['thumbs'] ?? [];
3153 if ( $thumbs['enable'] ?? false ) {
3154 $dataset['thumbsSettings'] = $thumbs;
3155 }
3156
3157 // Exclude li role.
3158 if ( $exclude_li_role ) {
3159 $dataset['a11y'] = ['slideRole' => '', 'slideLabelMessage' => ''];
3160 }
3161
3162 return wp_json_encode( $dataset );
3163 // phpcs:enable
3164 }
3165
3166 /**
3167 * Refine the slides per view
3168 *
3169 * @param mixed $raw_value
3170 * @return void
3171 */
3172 private function carousel_refine_slides_per_view( $raw_value ) {
3173 $value = $raw_value;
3174 if ( is_array( $raw_value ) ) {
3175 $value = $raw_value['auto'] ?? false
3176 ? 'auto'
3177 : floatval( $raw_value['value'] ?? 1 );
3178 }
3179
3180 return $value ? $value : 1;
3181 }
3182
3183 /**
3184 * Get nested post template block for core/query
3185 *
3186 * @param WP_Block $block_instance
3187 * @return boolean|WP_Block
3188 */
3189 private function get_nested_post_template( $block_instance ) {
3190 return $this->find_nested_block( $block_instance, 'core/post-template' );
3191 }
3192
3193 /**
3194 * Find a specific nested block from a parent block
3195 *
3196 * @param WP_Block $block
3197 * @param string $block_name
3198 * @return mixed
3199 */
3200 public function find_nested_block( $block, $block_name ) {
3201 if ( $block->name === $block_name ) {
3202 return $block;
3203 }
3204
3205 foreach ( $block->inner_blocks as $inner_block ) {
3206 $result = $this->find_nested_block( $inner_block, $block_name );
3207
3208 if ( $result ) {
3209 return $result;
3210 }
3211 }
3212
3213 return false;
3214 }
3215
3216 /**
3217 * Build responsive style
3218 *
3219 * @param array $args
3220 * @param array &$style_array
3221 * @param array &$responsive_style_array
3222 * @return string
3223 */
3224 private function build_query_loop_responsive_style( $args ) {
3225 $func_build_responsive_style = $args['func_build_responsive_style'] ?? '';
3226 if ( ! \is_callable( $func_build_responsive_style ) ) {
3227 return '';
3228 }
3229
3230 $setting_value = $args['setting_value'] ?? [];
3231 if ( empty( $setting_value ) || ! \is_array( $setting_value ) ) {
3232 return '';
3233 }
3234
3235 $breakpoints = $args['breakpoints'];
3236 $selector = $args['selector'];
3237
3238 $responsive_styles = [];
3239 foreach ( $setting_value as $breakpoint => $value_by_breakpoint ) {
3240 $value = null;
3241 if ( $this->is_valid_value( $value_by_breakpoint['value'] ?? null ) ) {
3242 $value = $value_by_breakpoint['value'];
3243 } elseif ( isset( $value_by_breakpoint['inherit'] ) && is_string( $value_by_breakpoint['inherit'] ) ) {
3244 $value = $setting_value[ $value_by_breakpoint['inherit'] ]['value'] ?? null;
3245 }
3246
3247 if ( $this->is_valid_value( $value ) ) {
3248 $style_by_breakpoint = $func_build_responsive_style( array_merge( $args, [ 'value' => $value ] ) );
3249 if ( $style_by_breakpoint ) {
3250 $responsive_styles[ $breakpoint ] = $style_by_breakpoint;
3251 }
3252 }
3253 }
3254
3255 $this->sort_styles( $responsive_styles );
3256
3257 $dependent_style = '';
3258 $func_build_dependent_style = $args['func_build_dependent_style'] ?? '';
3259 if ( \is_callable( $func_build_dependent_style ) ) {
3260 $dependent_style = $func_build_dependent_style( array_merge( $args, [ 'responsive_styles' => $responsive_styles ] ) );
3261 }
3262
3263 $style = '';
3264 $last_responsive_style = '';
3265 foreach ( $responsive_styles as $breakpoint => $style_by_breakpoint ) {
3266 if ( ! $style_by_breakpoint || ! is_array( $style_by_breakpoint ) ) {
3267 continue;
3268 }
3269
3270 $responsive_style = '';
3271 foreach ( $style_by_breakpoint as $attr_key => $attr_value ) {
3272 $responsive_style .= "{$attr_key}:{$attr_value};";
3273 }
3274
3275 if ( $responsive_style !== $last_responsive_style ) {
3276 $style_with_selector = "{$selector}{{$responsive_style}}";
3277 $media_query = $breakpoints[ $breakpoint ]['minQuery'] ?? '';
3278 if ( $media_query ) {
3279 $style .= \str_replace( '##CONTENT##', $style_with_selector, $media_query );
3280 } else {
3281 $style .= $style_with_selector;
3282 }
3283 $last_responsive_style = $responsive_style;
3284 }
3285 }
3286
3287 return $style . $dependent_style;
3288 }
3289
3290 /**
3291 * Build the breakpoints
3292 *
3293 * @return array
3294 */
3295 private function get_breakpoints() {
3296 if ( ! $this->breakpoints ) {
3297 $breakpoints = get_option( 'cbb_breakpoints' );
3298 if ( empty( $breakpoints ) ) {
3299 $breakpoints = [
3300 [
3301 'breakpoint' => 576,
3302 'prefix' => 'sm',
3303 ],
3304 [
3305 'breakpoint' => 768,
3306 'prefix' => 'md',
3307 ],
3308 [
3309 'breakpoint' => 1024,
3310 'prefix' => 'lg',
3311 ],
3312 ];
3313 }
3314
3315 $breakpoints = array_filter(
3316 array_map(
3317 function ( $item ) {
3318 if ( empty( $item['breakpoint'] ) || empty( $item['prefix'] ) ) {
3319 return false;
3320 }
3321
3322 $breakpoint = absint( $item['breakpoint'] );
3323
3324 if ( $breakpoint < 10 ) {
3325 return false;
3326 }
3327
3328 $max_breakpoint = $breakpoint - 1;
3329 $item['breakpointMax'] = $max_breakpoint;
3330
3331 if ( $item['prefix'] !== 'sm' ) {
3332 $item['minQuery'] = "@media (min-width: {$breakpoint}px){##CONTENT##}";
3333 $item['maxQuery'] = "@media (max-width: {$max_breakpoint}px){##MAX_CONTENT##}";
3334 } else {
3335 $item['minQuery'] = '';
3336 $item['maxQuery'] = "@media (max-width: {$max_breakpoint}px){##MAX_CONTENT##}";
3337 }
3338
3339 return $item;
3340 },
3341 $breakpoints
3342 )
3343 );
3344
3345 $this->breakpoints = array_column( $breakpoints, null, 'prefix' );
3346 }
3347
3348 return $this->breakpoints;
3349 }
3350
3351 /**
3352 * Generate block selector.
3353 *
3354 * @param string $block_name
3355 * @param WP_Block $block_instance
3356 * @return string
3357 */
3358 private function generate_selector( $block_name, $block_instance ) {
3359 $selector_prefix = 'cbb';
3360
3361 if ( '' !== $this->style_contexts['loop'] ) {
3362 $post_id = $block_instance->context['postId'] ?? 0;
3363 if ( ! $this->style_contexts['post'] || $this->style_contexts['post'] !== $post_id ) {
3364 $this->style_contexts['post'] = $post_id;
3365 $this->style_contexts['counter'] = 0;
3366 $this->style_contexts['content_counter'] = 0;
3367 }
3368
3369 if ( $this->style_contexts['content'] ) {
3370 // The content context selector.
3371 $content_counter = ++$this->style_contexts['content_counter'];
3372 return "{$selector_prefix}-p{$this->style_contexts['post']}-{$content_counter}";
3373 }
3374
3375 $counter = ++$this->style_contexts['counter'];
3376 // The selector inside the loop.
3377 return "{$selector_prefix}-{$this->style_contexts['loop']}-{$counter}";
3378 } else {
3379 // The global selector.
3380 return wp_unique_id( "{$selector_prefix}-g-" );
3381 }
3382 }
3383
3384 /**
3385 * Update loop id when having a post template block
3386 *
3387 * @param array $context
3388 * @param array $block
3389 * @return array
3390 */
3391 public function update_loop_id( $context, $block ) {
3392 if ( $this->is_loop_temlate_block( $block['blockName'] ?? '' ) ) {
3393 if ( isset( $context['queryId'] ) ) {
3394 $query_id = $context['queryId'];
3395 if ( in_array( $query_id, $this->style_contexts['loops'], true ) ) {
3396 $count = count(
3397 array_filter(
3398 $this->style_contexts['loops'],
3399 function ( $value ) use ( $query_id ) {
3400 return $value === $query_id;
3401 }
3402 )
3403 );
3404
3405 $this->style_contexts['loop'] = "{$query_id}i{$count}";
3406 } else {
3407 $this->style_contexts['loop'] = $query_id;
3408 }
3409
3410 $this->style_contexts['loops'][] = $query_id;
3411 }
3412 }
3413
3414 // Post content inside a loop.
3415 if ( $this->style_contexts['loop'] && 'core/post-content' === ( $block['blockName'] ?? '' ) ) {
3416 $this->style_contexts['content'] = true;
3417 }
3418
3419 return $context;
3420 }
3421
3422 /**
3423 * Reset loop id when the post template is rendered
3424 *
3425 * @param string $content
3426 * @param array $block
3427 * @return string
3428 */
3429 public function reset_loop_id( $content, $block ) {
3430 if ( $this->is_loop_temlate_block( $block['blockName'] ?? '' ) ) {
3431 $this->style_contexts['loop'] = '';
3432 $this->style_contexts['post'] = 0;
3433 }
3434
3435 // Post content inside a loop.
3436 if ( 'core/post-content' === ( $block['blockName'] ?? '' ) ) {
3437 $this->style_contexts['content'] = false;
3438 }
3439
3440 return $content;
3441 }
3442
3443 /**
3444 * Render sticky attributes for core template part blocks
3445 *
3446 * @param string $block_content
3447 * @param array $block
3448 * @param WP_Block $block_instance
3449 * @return string
3450 */
3451 public function render_template_part_sticky( $block_content, $block, $block_instance ) {
3452 // Ignore admin side.
3453 if ( is_admin() ) {
3454 return $block_content;
3455 }
3456
3457 // If this block has sticky data.
3458 $sticky_data = $block['attrs']['boldblocks']['sticky'] ?? false;
3459 if ( $sticky_data && ( $sticky_data['enable'] ?? false ) ) {
3460 $sticky_classes = [ 'is-sticky-block' ];
3461 $sticky_classes[] = 'is-stick-to-' . ( $sticky_data['stickTo'] ?? 'top' );
3462
3463 if ( $sticky_data['isStickOnScrollingUp'] ?? false ) {
3464 $sticky_classes[] = 'is-sticky-on-scrollup';
3465 } elseif ( $sticky_data['isFixed'] ?? false ) {
3466 $sticky_classes[] = 'is-fixed';
3467 if ( $sticky_data['isInFlow'] ?? false ) {
3468 $sticky_classes[] = 'is-in-flow';
3469 }
3470 if ( $sticky_data['isDetectingScroll'] ?? false ) {
3471 $sticky_classes[] = 'is-detecting-scroll';
3472 }
3473 } elseif ( $sticky_data['isDetectingStuck'] ?? false ) {
3474 $sticky_classes[] = 'is-detecting-stuck';
3475 }
3476
3477 if ( in_array( $block['attrs']['area'] ?? '', [ 'header', 'footer' ], true ) ) {
3478 $sticky_classes[] = 'is-sticky-' . $block['attrs']['area'];
3479 }
3480
3481 $block_content = $this->add_class_to_block( $block_content, implode( ' ', $sticky_classes ) );
3482 }
3483
3484 return $block_content;
3485 }
3486
3487 /**
3488 * Render settings for carousel layout blocks
3489 *
3490 * @param string $block_content
3491 * @param array $block
3492 * @param WP_Block $block_instance
3493 * @return string
3494 */
3495 public function render_carousel_settings( $block_content, $block, $block_instance ) {
3496 // Ignore admin side.
3497 if ( is_admin() ) {
3498 return $block_content;
3499 }
3500
3501 // There is no carousel layout.
3502 if ( 'carousel' !== ( $block_instance->block_type->supports['layoutType'] ?? '' ) ) {
3503 return $block_content;
3504 }
3505
3506 // Carousel settings.
3507 $carousel_settings = $block['attrs']['boldblocks']['carousel'] ?? [];
3508
3509 // Dataset attribute.
3510 $carousel_dataset = $this->build_carousel_settings( $carousel_settings );
3511 if ( $carousel_dataset ) {
3512 $block_content = $this->add_data_to_block( $block_content, 'data-carousel-settings', esc_attr( $carousel_dataset ) );
3513 }
3514
3515 // Preload class.
3516 $preloader_class = 'cbb-carousel-preload';
3517
3518 // Has preloader?
3519 $has_preloader = $carousel_settings['displayLoader'] ?? false;
3520 if ( $has_preloader ) {
3521 $preloader_class .= ' has-preloader';
3522 if ( 'coverflow' === ( $carousel_settings['effect'] ?? '' ) ) {
3523 $preloader_class .= ' is-fading-loader';
3524 }
3525
3526 // Add the loader to the markup.
3527 $block_content = $this->add_inner_content_to_block( $block_content, $this->get_carousel_preloader_markup( $block, $block_instance ) );
3528 }
3529
3530 // Mark it as having preloader.
3531 $block_content = $this->add_class_to_block( $block_content, $preloader_class );
3532
3533 return $block_content;
3534 }
3535
3536 /**
3537 * Get the preloader markup for carousels
3538 *
3539 * @param array $block
3540 * @param WP_Block $block_instance
3541 * @return string
3542 */
3543 private function get_carousel_preloader_markup( $block, $block_instance ) {
3544 return apply_filters( 'cbb_carousel_preloader', '<div class="cbb-loader"></div>', $block, $block_instance );
3545 }
3546
3547 /**
3548 * Migrate cbb class for old CBB blocks.
3549 *
3550 * @param string $block_content
3551 * @param array $block
3552 * @param WP_Block $block_instance
3553 * @return string
3554 */
3555 public function migrate_cbb_class( $block_content, $block, $block_instance ) {
3556 // Ignore admin side or empty content.
3557 if ( is_admin() || ! $block_content ) {
3558 return $block_content;
3559 }
3560
3561 // There is no need to migrate.
3562 $migrate_cbb_class = $block_instance->block_type->supports['migrateClass'] ?? '';
3563 if ( ! $migrate_cbb_class ) {
3564 return $block_content;
3565 }
3566
3567 // Replace the class.
3568 $block_reader = new \WP_HTML_Tag_Processor( $block_content );
3569 if ( $block_reader->next_tag( [ 'class_name' => $migrate_cbb_class ] ) ) {
3570 $new_class = str_ends_with( $migrate_cbb_class, 'custom' ) ? 'cbb-block' : 'cbb-block-parent';
3571 $block_reader->set_attribute( 'class', str_replace( $migrate_cbb_class, $new_class, $block_reader->get_attribute( 'class' ) ) );
3572
3573 // Render the updated HTML.
3574 return $block_reader->get_updated_html();
3575 }
3576
3577 return $block_content;
3578 }
3579
3580 /**
3581 * Support block style for non-cbb blocks
3582 *
3583 * @param string $is_supported
3584 * @param array $block
3585 * @return boolean
3586 */
3587 public function support_block_style( $is_supported, $block ) {
3588 if ( in_array( $block['blockName'] ?? '', $this->non_cbb_blocks, true ) ) {
3589 $is_supported = true;
3590 }
3591
3592 return $is_supported;
3593 }
3594
3595 /**
3596 * Set a valid layout for none-cbb blocks
3597 *
3598 * @param string $block_layout
3599 * @param array $block
3600 * @return string
3601 */
3602 public function get_block_layout( $block_layout, $block ) {
3603 if ( in_array( $block['blockName'] ?? '', $this->non_cbb_blocks, true ) ) {
3604 $block_layout = 'standalone';
3605 }
3606
3607 return $block_layout;
3608 }
3609
3610 /**
3611 * Check whether the block is a loop template block.
3612 *
3613 * @param string $block_name
3614 * @return array
3615 */
3616 private function is_loop_temlate_block( $block_name ) {
3617 $loop_template_blocks = apply_filters( 'cbb_get_loop_temlate_blocks', [ 'core/post-template' ] );
3618
3619 return in_array( $block_name, $loop_template_blocks, true );
3620 }
3621
3622 /**
3623 * Check whether current block has custom style or not.
3624 *
3625 * @param array $block
3626 * @param WP_Block $block_instance
3627 * @return boolean
3628 */
3629 private function has_style( $block, $block_instance ) {
3630 // There is no boldblocks value.
3631 if ( ! isset( $block['attrs']['boldblocks'] ) ) {
3632 return false;
3633 }
3634
3635 return apply_filters( 'cbb_support_block_style', isset( $block_instance->block_type->supports['cbb'] ), $block, $block_instance );
3636 }
3637
3638 /**
3639 * Order style array by breakpoints
3640 *
3641 * @param array $styles
3642 * @return void
3643 */
3644 private function sort_styles( &$styles ) {
3645 uksort(
3646 $styles,
3647 function ( $a, $b ) {
3648 if ( 'lg' === $a || 'sm' === $b ) {
3649 return 1;
3650 }
3651
3652 if ( 'sm' === $a || 'lg' === $b ) {
3653 return -1;
3654 }
3655
3656 return 0;
3657 }
3658 );
3659 }
3660
3661 /**
3662 * Build responsive style
3663 *
3664 * @param array $args
3665 * @param array &$responsive_style_array
3666 * @param array &$classes
3667 * @return string
3668 */
3669 private function build_responsive_style( $args, &$responsive_style_array, &$classes ) {
3670 $return_value = false;
3671 $func_build_responsive_style = $args['func_build_responsive_style'] ?? '';
3672 if ( ! \is_callable( $func_build_responsive_style ) ) {
3673 return $return_value;
3674 }
3675
3676 $setting_value = $args['setting_value'] ?? [];
3677 if ( empty( $setting_value ) || ! \is_array( $setting_value ) ) {
3678 return $return_value;
3679 }
3680
3681 $breakpoints = $args['breakpoints'];
3682 $selector = $args['selector'];
3683
3684 $setting_values = [];
3685 $responsive_styles = [];
3686 foreach ( $setting_value as $breakpoint => $value_by_breakpoint ) {
3687 $value = null;
3688 if ( $this->is_valid_value( $value_by_breakpoint['value'] ?? null ) ) {
3689 $value = $value_by_breakpoint['value'];
3690 } elseif ( isset( $value_by_breakpoint['inherit'] ) && is_string( $value_by_breakpoint['inherit'] ) ) {
3691 $value = $setting_value[ $value_by_breakpoint['inherit'] ]['value'] ?? null;
3692 }
3693
3694 if ( $this->is_valid_value( $value ) ) {
3695 $style_by_breakpoint = $func_build_responsive_style( array_merge( $args, [ 'value' => $value ] ) );
3696 if ( $style_by_breakpoint ) {
3697 $responsive_styles[ $breakpoint ] = $style_by_breakpoint;
3698 }
3699 }
3700
3701 $setting_values[ $breakpoint ] = $value;
3702 }
3703
3704 $this->sort_styles( $responsive_styles );
3705
3706 $dependent_style = '';
3707 $func_build_dependent_style = $args['func_build_dependent_style'] ?? '';
3708 if ( \is_callable( $func_build_dependent_style ) ) {
3709 $dependent_style = $func_build_dependent_style(
3710 array_merge(
3711 $args,
3712 [
3713 'setting_values' => $setting_values,
3714 'responsive_styles' => $responsive_styles,
3715 ]
3716 ),
3717 $classes
3718 );
3719 }
3720
3721 $keys = [];
3722 $style = '';
3723 $last_responsive_style = '';
3724 foreach ( $responsive_styles as $breakpoint => $style_by_breakpoint ) {
3725 $responsive_style = '';
3726 foreach ( $style_by_breakpoint as $attr_key => $attr_value ) {
3727 $responsive_style .= "{$attr_key}:{$attr_value};";
3728
3729 if ( ! in_array( $attr_key, $keys, true ) ) {
3730 $keys[] = $attr_key;
3731 $classes[] = $breakpoint . '-' . str_replace( '--cbb--', 'cbb-', $attr_key );
3732 }
3733 }
3734
3735 if ( $responsive_style !== $last_responsive_style ) {
3736 $style_with_selector = "{$selector}{{$responsive_style}}";
3737 $min_query = $breakpoints[ $breakpoint ]['minQuery'] ?? '';
3738 if ( $min_query ) {
3739 $style_with_selector = \str_replace( '##CONTENT##', $style_with_selector, $min_query );
3740 }
3741
3742 $style .= $style_with_selector;
3743 $last_responsive_style = $responsive_style;
3744 }
3745 }
3746
3747 // Add style to the style_array.
3748 if ( $style ) {
3749 $style .= $dependent_style;
3750 $responsive_style_array[] = $style;
3751
3752 return $style;
3753 }
3754
3755 return $return_value;
3756 }
3757
3758 /**
3759 * Check wether the block markup has a attribute or not.
3760 *
3761 * @param string $attribute
3762 * @param string $block_content
3763 * @return boolean
3764 */
3765 private function block_has_attribute( $attribute, $block_content ) {
3766 $tags = new \WP_HTML_Tag_Processor( $block_content );
3767 if ( $tags->next_tag() ) {
3768 return null !== $tags->get_attribute( $attribute );
3769 }
3770
3771 return false;
3772 }
3773
3774 /**
3775 * Add CSS class to block wrapper
3776 *
3777 * @param string $block_content
3778 * @param string $classes
3779 * @return string
3780 */
3781 public function add_class_to_block( $block_content, $classes ) {
3782 $tags = new \WP_HTML_Tag_Processor( $block_content );
3783 if ( $tags->next_tag() ) {
3784 $tags->add_class( $classes );
3785 }
3786 return $tags->get_updated_html();
3787 }
3788
3789 /**
3790 * Add data attribute to block wrapper
3791 *
3792 * @param string $block_content
3793 * @param string $value
3794 * @return string
3795 */
3796 public function add_data_to_block( $block_content, $name, $value ) {
3797 $tags = new \WP_HTML_Tag_Processor( $block_content );
3798 if ( $tags->next_tag() ) {
3799 $tags->set_attribute( $name, $value );
3800 }
3801
3802 return $tags->get_updated_html();
3803 }
3804
3805 /**
3806 * Add inner content to block markup
3807 *
3808 * @param string $block_content
3809 * @param string $inner_content
3810 * @return string
3811 */
3812 public function add_inner_content_to_block( $block_content, $inner_content ) {
3813 if ( $inner_content ) {
3814 $block_content = \preg_replace(
3815 '/' . \preg_quote( '>', '/' ) . '/',
3816 '>' . $inner_content,
3817 $block_content,
3818 1
3819 );
3820 }
3821
3822 return $block_content;
3823 }
3824
3825 /**
3826 * Detect a value is valid
3827 *
3828 * @param mixed $value
3829 * @return boolean
3830 */
3831 private function is_valid_value( $value ) {
3832 return isset( $value ) && $value !== '';
3833 }
3834
3835 /**
3836 * Detect a responsive value is valid
3837 *
3838 * @param mixed $value
3839 * @return boolean
3840 */
3841 private function is_valid_responsive_value( $value ) {
3842 return ! empty( $value['lg']['value'] ) || ! empty( $value['md']['value'] ) || ! empty( $value['sm']['value'] );
3843 }
3844
3845 /**
3846 * Get value for spacing var
3847 *
3848 * @param string $value
3849 * @return string
3850 */
3851 private function get_spacing_value( $value ) {
3852 if ( ! $this->is_valid_value( $value ) ) {
3853 return '';
3854 }
3855
3856 if ( strpos( $value, 'var:preset|spacing|' ) !== false ) {
3857 preg_match( '/var:preset\|spacing\|(.+)/', $value, $slug );
3858
3859 if ( ! $slug ) {
3860 return $value;
3861 }
3862
3863 return "var(--wp--preset--spacing--{$slug[1]})";
3864 }
3865
3866 return $value;
3867 }
3868
3869 /**
3870 * Get CSS value for a color object.
3871 *
3872 * @param array $color_array
3873 * @return string
3874 */
3875 private function get_css_color_value( $color_array ) {
3876 $value = '';
3877
3878 if ( ! empty( $color_array ) ) {
3879 if ( is_string( $color_array ) ) {
3880 $value = $color_array;
3881 } elseif ( is_array( $color_array ) ) {
3882 if ( $color_array['gradientSlug'] ?? false ) {
3883 $value = "var(--wp--preset--gradient--{$color_array['gradientSlug']}, {$color_array['gradientValue']})";
3884 } elseif ( $color_array['gradientValue'] ?? false ) {
3885 $value = $color_array['gradientValue'];
3886 } elseif ( $color_array['slug'] ?? false ) {
3887 $value = "var(--wp--preset--color--{$color_array['slug']}, {$color_array['value']})";
3888 } elseif ( $color_array['value'] ?? false ) {
3889 $value = $color_array['value'];
3890 }
3891 }
3892 }
3893
3894 return $value;
3895 }
3896
3897 /**
3898 * Refine custom JS and CSS value
3899 *
3900 * @param string $code
3901 * @param array $tokens
3902 * @param string $type
3903 * @return string
3904 */
3905 public function refine_custom_value( $code, $tokens = [], $type = 'CSS' ) {
3906 // Remove tags.
3907 // $code = preg_replace( '/(<([\w-]+)>)/mi', '', $code );
3908
3909 // Replace breakpoint tokens.
3910 $breakpoints = $this->get_breakpoints();
3911
3912 $tokens = array_merge(
3913 $tokens,
3914 [
3915 'TABLET_UP' => ( $breakpoints['md']['breakpoint'] ?? 768 ) . 'px',
3916 'TABLET_DOWN' => ( $breakpoints['md']['breakpointMax'] ?? 767 ) . 'px',
3917 'DESKTOP_UP' => ( $breakpoints['lg']['breakpoint'] ?? 1024 ) . 'px',
3918 'DESKTOP_DOWN' => ( $breakpoints['lg']['breakpointMax'] ?? 1023 ) . 'px',
3919 ]
3920 );
3921
3922 $code = str_replace( array_keys( $tokens ), array_values( $tokens ), $code );
3923
3924 // Minify it.
3925 return $this->minify( $code, $type );
3926 }
3927
3928 /**
3929 * Simple minify JS and CSS
3930 *
3931 * @param string $code
3932 * @param string $type
3933 * @return string
3934 */
3935 public function minify( $code, $type = 'CSS' ) {
3936 if ( $this->the_plugin_instance->is_debug_mode() ) {
3937 return $code;
3938 }
3939
3940 if ( 'CSS' === $type ) {
3941 $code = preg_replace( '/\s+/', ' ', $code );
3942 } elseif ( 'JS' === $type ) {
3943 // Remove comments first - https://stackoverflow.com/a/31907095/1038868.
3944 $pattern = '/(?:(?:\/\*(?:[^*]|(?:\*+[^*\/]))*\*+\/)|(?:(?<!\:|\\\|\'|\")\/\/.*))/';
3945 $code = preg_replace( $pattern, '', $code );
3946
3947 // Remove spaces, tabs, and new lines after a line if it ends with a semicolon, a bracket, or a comma, or an open parentheses.
3948 $code = preg_replace( '/([;{,\(])\s+/', '$1', $code );
3949
3950 // Remove before ...
3951 $code = preg_replace( '/\s+([}\)])/', '$1', $code );
3952
3953 // Remove spaces before a line.
3954 $code = preg_replace( '/^\s+/m', '', $code );
3955 }
3956
3957 return trim( $code );
3958 }
3959 }
3960 endif;
3961