PluginProbe
Content Blocks Builder – Create blocks, repeater blocks with carousel, grid, popup layouts / 2.8.4
Content Blocks Builder – Create blocks, repeater blocks with carousel, grid, popup layouts v2.8.4
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.4, at includes/custom-style.php

3,960 lines 130.2 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 }
1994
1995 return $block_content;
1996 }
1997
1998 /**
1999 * Get all predefined animations
2000 *
2001 * @return array
2002 */
2003 private function get_animation_names() {
2004 $animation_names = [
2005 'bounce' => false,
2006 'flash' => false,
2007 'pulse' => [ 'timingFunction' => 'ease-in-out' ],
2008 'rubberBand' => false,
2009 'shakeX' => false,
2010 'shakeY' => false,
2011 'headShake' => [ 'timingFunction' => 'ease-in-out' ],
2012 'swing' => false,
2013 'tada' => false,
2014 'wobble' => false,
2015 'jello' => false,
2016 'heartBeat' => [ 'timingFunction' => 'ease-in-out' ],
2017
2018 'backInDown' => false,
2019 'backInLeft' => false,
2020 'backInRight' => false,
2021 'backInUp' => false,
2022
2023 'bounceIn' => false,
2024 'bounceInDown' => false,
2025 'bounceInLeft' => false,
2026 'bounceInRight' => false,
2027 'bounceInUp' => false,
2028
2029 'fadeIn' => false,
2030 'fadeInDown' => [ 'name' => 'fadeInXY' ],
2031 'fadeInDownBig' => [ 'name' => 'fadeInXY' ],
2032 'fadeInLeft' => [ 'name' => 'fadeInXY' ],
2033 'fadeInLeftBig' => [ 'name' => 'fadeInXY' ],
2034 'fadeInRight' => [ 'name' => 'fadeInXY' ],
2035 'fadeInRightBig' => [ 'name' => 'fadeInXY' ],
2036 'fadeInUp' => [ 'name' => 'fadeInXY' ],
2037 'fadeInUpBig' => [ 'name' => 'fadeInXY' ],
2038 'fadeInTopLeft' => [ 'name' => 'fadeInXY' ],
2039 'fadeInTopRight' => [ 'name' => 'fadeInXY' ],
2040 'fadeInBottomLeft' => [ 'name' => 'fadeInXY' ],
2041 'fadeInBottomRight' => [ 'name' => 'fadeInXY' ],
2042
2043 'fadeInUpSmall' => [ 'name' => 'fadeInXY' ],
2044 'fadeInRightSmall' => [ 'name' => 'fadeInXY' ],
2045 'fadeInDownSmall' => [ 'name' => 'fadeInXY' ],
2046 'fadeInLeftSmall' => [ 'name' => 'fadeInXY' ],
2047
2048 'flip' => false,
2049 'flipInX' => false,
2050 'flipInY' => false,
2051
2052 'lightSpeedInRight' => [ 'timingFunction' => 'ease-out' ],
2053 'lightSpeedInLeft' => [ 'timingFunction' => 'ease-out' ],
2054
2055 'rotateIn' => [ 'name' => 'rotateInZ' ],
2056 'rotateInDownLeft' => [ 'name' => 'rotateInZ' ],
2057 'rotateInDownRight' => [ 'name' => 'rotateInZ' ],
2058 'rotateInUpLeft' => [ 'name' => 'rotateInZ' ],
2059 'rotateInUpRight' => [ 'name' => 'rotateInZ' ],
2060
2061 'jackInTheBox' => false,
2062 'rollIn' => false,
2063
2064 'zoomIn' => false,
2065 'zoomInDown' => [ 'name' => 'zoomInXY' ],
2066 'zoomInLeft' => [ 'name' => 'zoomInXY' ],
2067 'zoomInRight' => [ 'name' => 'zoomInXY' ],
2068 'zoomInUp' => [ 'name' => 'zoomInXY' ],
2069
2070 'slideInDown' => [ 'name' => 'slideInXY' ],
2071 'slideInLeft' => [ 'name' => 'slideInXY' ],
2072 'slideInRight' => [ 'name' => 'slideInXY' ],
2073 'slideInUp' => [ 'name' => 'slideInXY' ],
2074
2075 'slideInDownSmall' => [ 'name' => 'slideInXY' ],
2076 'slideInLeftSmall' => [ 'name' => 'slideInXY' ],
2077 'slideInRightSmall' => [ 'name' => 'slideInXY' ],
2078 'slideInUpSmall' => [ 'name' => 'slideInXY' ],
2079
2080 'spin' => false,
2081 'zoomOutIn' => false,
2082 ];
2083
2084 return $animation_names;
2085 }
2086
2087 /**
2088 * Has a animation setting value or not
2089 *
2090 * @param string $type
2091 * @param string $name
2092 * @param array $settings
2093 * @return mixed
2094 */
2095 private function has_animation_setting_value( $type, $name, $settings ) {
2096 return isset( $settings[ $type ][ $name ] ) || isset( $settings[ $name ] );
2097 }
2098
2099 /**
2100 * Get background animation setting value
2101 *
2102 * @param string $type
2103 * @param string $name
2104 * @param array $settings
2105 * @param mixed $default_val
2106 * @return mixed
2107 */
2108 private function get_animation_setting_value( $type, $name, $settings, $default_val = '' ) {
2109 if ( isset( $settings[ $type ][ $name ] ) ) {
2110 return $settings[ $type ][ $name ];
2111 } else {
2112 return $settings[ $name ] ?? $default_val;
2113 }
2114 }
2115
2116 /**
2117 * Build the video background
2118 *
2119 * @param array $background
2120 * @param string $block_content
2121 * @param array $block
2122 * @param WP_Block $block_instance
2123 * @return string
2124 */
2125 private function build_video_background( $background, $block_content, $block, $block_instance ) {
2126 $settings = $background['settings'] ?? [];
2127 $media = $background['video'] ?? [];
2128 $block_class = 'bb:has-background bb:has-background--video';
2129
2130 $background_position = round( ( $settings['focalPoint']['x'] ?? .5 ) * 100 ) . '% ' . round( ( $settings['focalPoint']['y'] ?? .5 ) * 100 ) . '%';
2131 $object_fit = $settings['objectFit'] ?? 'cover';
2132 if ( empty( $object_fit ) ) {
2133 $object_fit = 'cover';
2134 }
2135
2136 $video_style = 'object-fit:' . $object_fit . ';object-position:' . $background_position . ';';
2137 $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 );
2138
2139 if ( $media['isPausable'] ?? false ) {
2140 $block_class .= ' cbb-has-video-controls';
2141 $block_background .= '<button class="cbb-play-pause cbb-video-play-pause is-playing"></button>';
2142 }
2143
2144 if ( strpos( $block['innerHTML'] ?? '', $block_class ) === false ) {
2145 $block_content = $this->add_class_to_block( $block_content, $block_class );
2146 }
2147
2148 $block_content = $this->add_inner_content_to_block( $block_content, $block_background );
2149
2150 return $block_content;
2151 }
2152
2153 /**
2154 * Render block overlay
2155 *
2156 * @param string $block_content
2157 * @param array $block
2158 * @param WP_Block $block_instance
2159 * @return string
2160 */
2161 public function build_block_overlay( $block_content, $block, $block_instance ) {
2162 // Ignore admin side.
2163 if ( is_admin() ) {
2164 return $block_content;
2165 }
2166
2167 $block_overlay = $block['attrs']['boldblocks']['overlay'] ?? false;
2168
2169 // There is no overlay value.
2170 if ( ! ( $block_overlay['overlayColor'] ?? false ) ) {
2171 return $block_content;
2172 }
2173
2174 // Ignore legacy markup.
2175 if ( strpos( $block['innerHTML'] ?? '', 'bb:block-overlay' ) !== false ) {
2176 return $block_content;
2177 }
2178
2179 $bg_color = '';
2180 $overlay_color = $block_overlay['overlayColor'] ?? [];
2181 if ( ( $overlay_color['gradientSlug'] ?? false ) || ( $overlay_color['gradientValue'] ?? false ) ) {
2182 if ( $overlay_color['gradientSlug'] ?? false ) {
2183 $bg_color = sprintf( 'var(--wp--preset--gradient--%1$s, %2$s)', $overlay_color['gradientSlug'], $overlay_color['gradientValue'] ?? '' );
2184 } else {
2185 $bg_color = $overlay_color['gradientValue'];
2186 }
2187 } elseif ( ( $overlay_color['slug'] ?? false ) || ( $overlay_color['value'] ?? false ) ) {
2188 if ( $overlay_color['slug'] ?? false ) {
2189 $bg_color = sprintf( 'var(--wp--preset--color--%1$s, %2$s)', $overlay_color['slug'], $overlay_color['value'] ?? '' );
2190 } else {
2191 $bg_color = $overlay_color['value'];
2192 }
2193 }
2194
2195 if ( empty( $bg_color ) ) {
2196 return $block_content;
2197 }
2198
2199 $opacity = absint( $block_overlay['opacity'] ?? 100 );
2200
2201 // Build the markup.
2202 $overlay_markup = sprintf( '<div aria-hidden="true" class="bb:block-overlay" style="background:%1$s;opacity:%2$s;"></div>', $bg_color, $opacity / 100 );
2203
2204 $block_class = 'bb:has-overlay';
2205 if ( strpos( $block['innerHTML'] ?? '', $block_class ) === false ) {
2206 $block_content = $this->add_class_to_block( $block_content, $block_class );
2207 }
2208
2209 $block_content = $this->add_inner_content_to_block( $block_content, $overlay_markup );
2210
2211 return $block_content;
2212 }
2213
2214 /**
2215 * Render accordions block data
2216 *
2217 * @param array $parsed_block
2218 * @return array
2219 */
2220 public function render_accordion_block_data( $parsed_block ) {
2221 $block_type = \WP_Block_Type_Registry::get_instance()->get_registered( $parsed_block['blockName'] );
2222
2223 if ( ! $block_type ) {
2224 return $parsed_block;
2225 }
2226
2227 $layout_type = $block_type->supports['layoutType'] ?? '';
2228 if ( ! in_array( $layout_type, [ 'accordion', 'accordionItem' ], true ) ) {
2229 return $parsed_block;
2230 }
2231
2232 static $accordion_counter = 0;
2233 static $accordion_id = '';
2234 static $item_index = [];
2235
2236 if ( 'accordion' === $layout_type ) {
2237 ++$accordion_counter;
2238 $processor = new \WP_HTML_Tag_Processor( $parsed_block['innerHTML'] ?? '' );
2239
2240 $accordion_id = '';
2241 if ( $processor->next_tag() ) {
2242 $accordion_id = $processor->get_attribute( 'id' );
2243 }
2244
2245 if ( ! $accordion_id ) {
2246 $accordion_id = 'accordion-' . $accordion_counter;
2247 }
2248 } elseif ( 'accordionItem' === $layout_type ) {
2249 if ( ! isset( $item_index[ $accordion_id ] ) ) {
2250 $item_index[ $accordion_id ] = 1;
2251 } else {
2252 ++$item_index[ $accordion_id ];
2253 }
2254
2255 $parsed_block['attrs']['boldblocks']['accordionItem']['block_id'] = $accordion_id . '-item-' . $item_index[ $accordion_id ];
2256 }
2257
2258 return $parsed_block;
2259 }
2260
2261 /**
2262 * Render accordion item blocks
2263 *
2264 * @param string $block_content
2265 * @param array $block
2266 * @param WP_Block $block_instance
2267 * @return string
2268 */
2269 public function render_accordion_item_block( $block_content, $block, $block_instance ) {
2270 // Ignore admin side.
2271 if ( is_admin() ) {
2272 return $block_content;
2273 }
2274
2275 // Bail if it is not an accordion item block.
2276 if ( 'accordionItem' !== ( $block_instance->block_type->supports['layoutType'] ?? '' ) ) {
2277 return $block_content;
2278 }
2279
2280 $block_id = $block['attrs']['boldblocks']['accordionItem']['block_id'] ?? '';
2281 $block_id_selector = '#' . $block_id;
2282
2283 if ( $block_id ) {
2284 $processor = new \WP_HTML_Tag_Processor( $block_content );
2285
2286 while ( $processor->next_tag() ) {
2287 $tag_class = $processor->get_attribute( 'class' );
2288 if ( ! $tag_class ) {
2289 continue;
2290 }
2291
2292 if ( strpos( $tag_class, 'accordion-header' ) !== false ) {
2293 $processor->set_attribute( 'data-toggle-target', $block_id_selector );
2294 $processor->set_attribute( 'aria-controls', $block_id );
2295 } elseif ( strpos( $tag_class, 'accordion-link' ) !== false ) {
2296 $processor->set_attribute( 'href', $block_id_selector );
2297 } elseif ( strpos( $tag_class, 'accordion-collapse' ) !== false ) {
2298 $processor->set_attribute( 'id', $block_id );
2299 $processor->set_attribute( 'role', 'region' );
2300 break;
2301 }
2302 }
2303
2304 $block_content = $processor->get_updated_html();
2305 }
2306
2307 return $block_content;
2308 }
2309
2310 /**
2311 * Render grid style for the query loop's grid layout
2312 *
2313 * @param string $block_content
2314 * @param array $block
2315 * @param WP_Block $block_instance
2316 * @return string
2317 */
2318 public function render_query_loop_grid_style( $block_content, $block, $block_instance ) {
2319 // Ignore admin side.
2320 if ( is_admin() ) {
2321 return $block_content;
2322 }
2323
2324 // If this is a core/query block that has grid layout.
2325 $grid_data = $this->get_query_loop_grid_data( $block, $block_instance );
2326 if ( ! $grid_data ) {
2327 return $block_content;
2328 }
2329
2330 $display_layout = $grid_data['displayType'] ?? '';
2331 if ( 'responsiveGrid' === $display_layout && ( $grid_data['grid'] ?? false ) ) {
2332 // Buil selector.
2333 $selector = 'cbb-query-' . $this->get_query_loop_id( $block );
2334
2335 // Get responsive settings.
2336 $breakpoints = $this->get_breakpoints();
2337
2338 // Get custom style.
2339 $block_style = $this->get_query_grid_style(
2340 [
2341 'selector' => ".{$selector} > ul",
2342 'block' => $block,
2343 'data' => $grid_data['grid'],
2344 'breakpoints' => $breakpoints,
2345 ]
2346 );
2347
2348 if ( empty( $block_style ) ) {
2349 return $block_content;
2350 }
2351
2352 $handle = $selector;
2353 // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
2354 wp_register_style( $handle, '' );
2355 wp_add_inline_style( $handle, $block_style );
2356 wp_enqueue_style( $handle );
2357
2358 // Add selector to block wrapper element.
2359 $block_content = $this->add_class_to_block( $block_content, $selector );
2360 }
2361
2362 return $block_content;
2363 }
2364
2365 /**
2366 * Build an unique ID for a query loop
2367 *
2368 * @param array $block
2369 * @return string
2370 */
2371 private function get_query_loop_id( $block ) {
2372 $query_id = $block['attrs']['queryId'] ?? 1;
2373 $key = "id_{$query_id}";
2374 if ( ! isset( $this->query_ids[ $key ] ) ) {
2375 $this->query_ids[ $key ] = 1;
2376 } else {
2377 ++$this->query_ids[ $key ];
2378 }
2379
2380 return $query_id . '-' . $this->query_ids[ $key ];
2381 }
2382
2383 /**
2384 * Get grid layout data
2385 *
2386 * @param array $block
2387 * @param WP_Block $block_instance
2388 * @return array
2389 */
2390 private function get_query_loop_grid_data( $block, $block_instance ) {
2391 $data = [];
2392 $old_layout_type = $block['attrs']['displayLayout']['type'] ?? '';
2393 if ( 'grid' === $old_layout_type ) {
2394 // Convert to the new name.
2395 $old_layout_type = 'responsiveGrid';
2396 }
2397 $data['displayType'] = $old_layout_type;
2398 $data['grid'] = $block['attrs']['boldblocks']['grid'] ?? [];
2399
2400 if ( $block_instance->block_type->api_version >= 3 ) {
2401 $post_template = $this->get_nested_post_template( $block_instance );
2402
2403 if ( $post_template ) {
2404 if ( 'responsiveGrid' === ( $post_template->attributes['boldblocks']['layout']['type'] ?? '' ) ) {
2405 $data['displayType'] = 'responsiveGrid';
2406 $data['grid'] = $post_template->attributes['boldblocks']['grid'] ?? [];
2407 }
2408 }
2409 }
2410
2411 return $data;
2412 }
2413
2414 /**
2415 * Build grid style
2416 *
2417 * @param array $args
2418 * @return string
2419 */
2420 private function get_query_grid_style( $args ) {
2421 $selector = $args['selector'];
2422 $data = $args['data'];
2423
2424 // None-responsive style.
2425 $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;}";
2426
2427 // Columns style.
2428 $style .= $this->build_query_loop_responsive_style(
2429 array_merge(
2430 $args,
2431 [
2432 'setting_value' => $data['columns'] ?? null,
2433 'func_build_responsive_style' => function ( $args ) {
2434 $style_array = [];
2435 $value = $args['value'];
2436 if ( $value ) {
2437 $style_array = [
2438 '--cbb--grid--columns' => $value,
2439 ];
2440 }
2441
2442 return $style_array;
2443 },
2444 'func_build_dependent_style' => function ( $args ) {
2445 $responsive_styles = $args['responsive_styles'] ?? [];
2446 $selector = $args['selector'];
2447 $breakpoints = $args['breakpoints'] ?? [];
2448 $style = "{$selector}{grid-template-columns:var(--cbb--grid--columns);}";
2449
2450 foreach ( $responsive_styles as $breakpoint => $attribute_array ) {
2451 $media_query = $breakpoints[ $breakpoint ]['minQuery'] ?? '';
2452 if ( $media_query ) {
2453 $style = \str_replace( '##CONTENT##', $style, $media_query );
2454 }
2455
2456 break;
2457 }
2458
2459 return $style;
2460 },
2461 ]
2462 )
2463 );
2464
2465 // Rows style.
2466 $style .= $this->build_query_loop_responsive_style(
2467 array_merge(
2468 $args,
2469 [
2470 'setting_value' => $data['rows'] ?? null,
2471 'func_build_responsive_style' => function ( $args ) {
2472 $style_array = [];
2473 $value = $args['value'];
2474 if ( $value ) {
2475 $style_array = [
2476 '--cbb--grid--rows' => $value,
2477 ];
2478 }
2479
2480 return $style_array;
2481 },
2482 'func_build_dependent_style' => function ( $args ) {
2483 $responsive_styles = $args['responsive_styles'] ?? [];
2484 $selector = $args['selector'];
2485 $breakpoints = $args['breakpoints'] ?? [];
2486 $style = "{$selector}{grid-template-rows:var(--cbb--grid--rows);}";
2487
2488 foreach ( $responsive_styles as $breakpoint => $attribute_array ) {
2489 $media_query = $breakpoints[ $breakpoint ]['minQuery'] ?? '';
2490 if ( $media_query ) {
2491 $style = \str_replace( '##CONTENT##', $style, $media_query );
2492 }
2493
2494 break;
2495 }
2496
2497 return $style;
2498 },
2499 ]
2500 )
2501 );
2502
2503 // Auto rows style.
2504 $style .= $this->build_query_loop_responsive_style(
2505 array_merge(
2506 $args,
2507 [
2508 'setting_value' => $data['autoRows'] ?? null,
2509 'func_build_responsive_style' => function ( $args ) {
2510 $style_array = [];
2511 $value = $args['value'];
2512 if ( $value ) {
2513 $style_array = [
2514 '--cbb--grid--auto-rows' => $value,
2515 ];
2516 }
2517
2518 return $style_array;
2519 },
2520 'func_build_dependent_style' => function ( $args ) {
2521 $responsive_styles = $args['responsive_styles'] ?? [];
2522 $selector = $args['selector'];
2523 $breakpoints = $args['breakpoints'] ?? [];
2524 $style = "{$selector}{grid-auto-rows:var(--cbb--grid--auto-rows);}";
2525
2526 foreach ( $responsive_styles as $breakpoint => $attribute_array ) {
2527 $media_query = $breakpoints[ $breakpoint ]['minQuery'] ?? '';
2528 if ( $media_query ) {
2529 $style = \str_replace( '##CONTENT##', $style, $media_query );
2530 }
2531
2532 break;
2533 }
2534
2535 return $style;
2536 },
2537 ]
2538 )
2539 );
2540
2541 // Gap style.
2542 $style .= $this->build_query_loop_responsive_style(
2543 array_merge(
2544 $args,
2545 [
2546 'setting_value' => $data['gap'] ?? null,
2547 'func_build_responsive_style' => function ( $args ) {
2548 $style_array = [];
2549 $value = $args['value'];
2550 if ( $value && is_array( $value ) ) {
2551 $row = $value['row'] ?? null;
2552
2553 if ( $this->is_valid_value( $row ) ) {
2554 $style_array['--cbb--grid--gap--row'] = $row;
2555 }
2556
2557 $column = $value['column'] ?? null;
2558
2559 if ( $this->is_valid_value( $column ) ) {
2560 $style_array['--cbb--grid--gap--column'] = $column;
2561 }
2562 }
2563
2564 return $style_array;
2565 },
2566 'func_build_dependent_style' => function ( $args ) {
2567 $responsive_styles = $args['responsive_styles'] ?? [];
2568 $selector = $args['selector'];
2569 $breakpoints = $args['breakpoints'] ?? [];
2570 $style = '';
2571
2572 foreach ( $responsive_styles as $breakpoint => $style_array ) {
2573 if ( strpos( $style, 'row-gap' ) !== false && strpos( $style, 'column-gap' ) !== false ) {
2574 break;
2575 }
2576
2577 $media_query = $breakpoints[ $breakpoint ]['minQuery'] ?? '';
2578
2579 if ( strpos( $style, 'row-gap' ) === false && isset( $style_array['--cbb--grid--gap--row'] ) ) {
2580 $row_style = "{$selector}{row-gap:var(--cbb--grid--gap--row);}";
2581 if ( $media_query ) {
2582 $style .= \str_replace( '##CONTENT##', $row_style, $media_query );
2583 } else {
2584 $style .= $row_style;
2585 }
2586 }
2587 if ( strpos( $style, 'column-gap' ) === false && isset( $style_array['--cbb--grid--gap--column'] ) ) {
2588 $column_style = "{$selector}{column-gap:var(--cbb--grid--gap--column);}";
2589 if ( $media_query ) {
2590 $style .= \str_replace( '##CONTENT##', $column_style, $media_query );
2591 } else {
2592 $style .= $column_style;
2593 }
2594 }
2595 }
2596
2597 return $style;
2598 },
2599 ]
2600 )
2601 );
2602
2603 $items = $data['items'] ?? [];
2604 if ( $items ) {
2605 foreach ( $items as $item_key => $item_value ) {
2606 $item_index = str_replace( 'item', '', $item_key );
2607
2608 // Column style.
2609 $style .= $this->build_query_loop_responsive_style(
2610 array_merge(
2611 $args,
2612 [
2613 'setting_value' => $item_value['columnSpan'] ?? [],
2614 'setting_name' => 'columnSpan',
2615 'selector' => "{$selector} > li:nth-of-type({$item_index})",
2616 'func_build_responsive_style' => [ $this, 'build_grid_item_responsive_style' ],
2617 'func_build_dependent_style' => [ $this, 'build_grid_item_dependent_style' ],
2618 ]
2619 )
2620 );
2621
2622 // Row style.
2623 $style .= $this->build_query_loop_responsive_style(
2624 array_merge(
2625 $args,
2626 [
2627 'setting_value' => $item_value['rowSpan'] ?? [],
2628 'setting_name' => 'rowSpan',
2629 'selector' => "{$selector} > li:nth-of-type({$item_index})",
2630 'func_build_responsive_style' => [ $this, 'build_grid_item_responsive_style' ],
2631 'func_build_dependent_style' => [ $this, 'build_grid_item_dependent_style' ],
2632 ]
2633 )
2634 );
2635 }
2636 }
2637
2638 return $style;
2639 }
2640
2641 /**
2642 * Build responsive style for grid item
2643 *
2644 * @param array $args
2645 * @return array
2646 */
2647 private function build_grid_item_responsive_style( $args ) {
2648 $setting_name = $args['setting_name'] ?? 'columnSpan';
2649 $variable_name = 'columnSpan' === $setting_name ? '--cbb--grid-item--column' : '--cbb--grid-item--row';
2650 $style_array = [];
2651 $value = $args['value'];
2652 if ( $value && is_array( $value ) ) {
2653 $span = $value['span'] ?? null;
2654 $start = $value['start'] ?? null;
2655
2656 if ( $this->is_valid_value( $start ) || $this->is_valid_value( $span ) ) {
2657 if ( ! $this->is_valid_value( $start ) ) {
2658 $start = 'auto';
2659 }
2660
2661 if ( ! $this->is_valid_value( $span ) ) {
2662 $span = 'auto';
2663 }
2664 if ( $span !== 'auto' && $span > 0 ) {
2665 $span = "span {$span}";
2666 }
2667
2668 $style_array[ $variable_name ] = "{$start} / {$span}";
2669 }
2670
2671 if ( 'columnSpan' === $setting_name ) {
2672 $order = $value['order'] ?? null;
2673 if ( is_numeric( $order ) ) {
2674 $style_array['--cbb--grid-item--order'] = $order;
2675 }
2676 }
2677 }
2678
2679 return $style_array;
2680 }
2681
2682 /**
2683 * Build dependent style for grid item
2684 *
2685 * @param array $args
2686 * @return array
2687 */
2688 private function build_grid_item_dependent_style( $args ) {
2689 $setting_name = $args['setting_name'] ?? 'columnSpan';
2690 $variable_name = 'columnSpan' === $setting_name ? '--cbb--grid-item--column' : '--cbb--grid-item--row';
2691 $css_name = 'columnSpan' === $setting_name ? 'grid-column' : 'grid-row';
2692
2693 $responsive_styles = $args['responsive_styles'] ?? [];
2694 $selector = $args['selector'];
2695 $breakpoints = $args['breakpoints'] ?? [];
2696 $style = '';
2697
2698 foreach ( $responsive_styles as $breakpoint => $style_array ) {
2699 if ( 'columnSpan' === $setting_name ) {
2700 if ( strpos( $style, $css_name ) !== false && strpos( $style, 'order' ) !== false ) {
2701 break;
2702 }
2703 } elseif ( strpos( $style, $css_name ) !== false ) {
2704 break;
2705 }
2706
2707 $media_query = $breakpoints[ $breakpoint ]['minQuery'] ?? '';
2708
2709 if ( strpos( $style, $css_name ) === false && isset( $style_array[ $variable_name ] ) ) {
2710 $column_style = "{$selector}{{$css_name}:var($variable_name);}";
2711 if ( $media_query ) {
2712 $style .= \str_replace( '##CONTENT##', $column_style, $media_query );
2713 } else {
2714 $style .= $column_style;
2715 }
2716 }
2717 if ( 'columnSpan' === $setting_name && strpos( $style, 'order' ) === false && isset( $style_array['--cbb--grid-item--order'] ) ) {
2718 $order_style = "{$selector}{order:var(--cbb--grid-item--order);}";
2719 if ( $media_query ) {
2720 $style .= \str_replace( '##CONTENT##', $order_style, $media_query );
2721 } else {
2722 $style .= $order_style;
2723 }
2724 }
2725 }
2726
2727 return $style;
2728 }
2729
2730 /**
2731 * Render carousel layout for query loop blocks
2732 *
2733 * @param string $block_content
2734 * @param array $block
2735 * @param WP_Block $block_instance
2736 * @return string
2737 */
2738 public function render_query_loop_carousel_layout( $block_content, $block, $block_instance ) {
2739 // Ignore admin side.
2740 if ( is_admin() ) {
2741 return $block_content;
2742 }
2743
2744 $post_template = $this->get_nested_post_template( $block_instance );
2745 if ( $post_template ) {
2746 // If this has carousel layout.
2747 if ( 'carousel' === ( $post_template->attributes['boldblocks']['layout']['type'] ?? '' ) ) {
2748 // Build carousel settings.
2749 $carousel_settings = $post_template->attributes['boldblocks']['carousel'] ?? [];
2750 if ( $carousel_settings ) {
2751 if ( ! $this->block_has_attribute( 'data-carousel-settings', $block_content ) ) {
2752 $carousel_attr = $this->build_carousel_settings( $carousel_settings, true );
2753
2754 if ( $carousel_attr ) {
2755 $block_content = $this->add_data_to_block( $block_content, 'data-carousel-settings', esc_attr( $carousel_attr ) );
2756 }
2757 }
2758
2759 // Buil selector.
2760 $selector = 'cbb-query-' . $this->get_query_loop_id( $block );
2761
2762 // Get responsive settings.
2763 $breakpoints = $this->get_breakpoints();
2764
2765 // Get custom style.
2766 $block_style = $this->get_carousel_preload_style(
2767 '',
2768 [
2769 'selector' => ".{$selector}",
2770 'settings' => [ 'carousel' => $carousel_settings ],
2771 'block_layout' => 'carousel',
2772 'breakpoints' => $breakpoints,
2773 ]
2774 );
2775
2776 $carousel_class = 'js-carousel-layout cbb-carousel-preload';
2777
2778 if ( $block_style ) {
2779 $handle = $selector;
2780 // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
2781 wp_register_style( $handle, '' );
2782 wp_add_inline_style( $handle, $block_style );
2783 wp_enqueue_style( $handle );
2784
2785 $carousel_class .= ' ' . $selector;
2786 }
2787
2788 if ( $carousel_settings['displayLoader'] ?? false ) {
2789 $carousel_class .= ' has-preloader';
2790
2791 if ( 'coverflow' === ( $carousel_settings['effect'] ?? '' ) ) {
2792 $carousel_class .= ' is-fading-loader';
2793 }
2794
2795 // Add preloader.
2796 $block_content = $this->add_inner_content_to_block( $block_content, $this->get_carousel_preloader_markup( $block, $block_instance ) );
2797 }
2798
2799 // Add selector to block wrapper element.
2800 $block_content = $this->add_class_to_block( $block_content, $carousel_class );
2801 }
2802 }
2803 }
2804
2805 return $block_content;
2806 }
2807
2808 /**
2809 * Add preload style for carousels
2810 *
2811 * @param string $style
2812 * @param array $args
2813 */
2814 public function get_carousel_preload_style( $style, $args ) {
2815 if ( 'carousel' === ( $args['block_layout'] ?? '' ) ) {
2816 $preload_style = '';
2817 $value = $args['settings']['carousel'] ?? false;
2818 $selector = $args['selector'];
2819 $breakpoints = $args['breakpoints'];
2820 if ( $value && is_array( $value ) ) {
2821 if ( $value['enableResponsive'] ?? false ) {
2822 $setting_value = $value['breakpoints'] ?? [];
2823 $responsive_styles = [];
2824 foreach ( $setting_value as $breakpoint => $value_by_breakpoint ) {
2825 $value = null;
2826 if ( $this->is_valid_value( $value_by_breakpoint['value'] ?? null ) ) {
2827 $value = $value_by_breakpoint['value'];
2828 } elseif ( isset( $value_by_breakpoint['inherit'] ) && is_string( $value_by_breakpoint['inherit'] ) ) {
2829 $value = $setting_value[ $value_by_breakpoint['inherit'] ]['value'] ?? null;
2830 }
2831
2832 if ( $this->is_valid_value( $value ) ) {
2833 $style_by_breakpoint = $this->build_carousel_preload_style( $value );
2834 if ( $style_by_breakpoint ) {
2835 $responsive_styles[ $breakpoint ] = $style_by_breakpoint;
2836 }
2837 }
2838 }
2839
2840 $this->sort_styles( $responsive_styles );
2841
2842 $last_responsive_style = '';
2843 foreach ( $responsive_styles as $breakpoint => $style_by_breakpoint ) {
2844 if ( ! $style_by_breakpoint || ! is_array( $style_by_breakpoint ) ) {
2845 continue;
2846 }
2847
2848 $responsive_style = '';
2849 foreach ( $style_by_breakpoint as $attr_key => $attr_value ) {
2850 $responsive_style .= "{$attr_key}:{$attr_value};";
2851 }
2852
2853 if ( $responsive_style !== $last_responsive_style ) {
2854 $style_with_selector = "{$selector}{{$responsive_style}}";
2855 $media_query = $breakpoints[ $breakpoint ]['minQuery'] ?? '';
2856 if ( $media_query ) {
2857 $preload_style .= \str_replace( '##CONTENT##', $style_with_selector, $media_query );
2858 } else {
2859 $preload_style .= $style_with_selector;
2860 }
2861 $last_responsive_style = $responsive_style;
2862 }
2863 }
2864 } else {
2865 $style_array = $this->build_carousel_preload_style( $value );
2866 $attr_style = '';
2867 foreach ( $style_array as $attr_key => $attr_value ) {
2868 $attr_style .= "{$attr_key}:{$attr_value};";
2869 }
2870
2871 if ( $attr_style ) {
2872 $preload_style .= "{$selector}{{$attr_style}}";
2873 }
2874 }
2875 }
2876
2877 if ( $preload_style ) {
2878 $style .= $preload_style;
2879 }
2880 }
2881
2882 return $style;
2883 }
2884
2885 /**
2886 * Build preload style for carousels
2887 *
2888 * @param array $value
2889 * @return void
2890 */
2891 private function build_carousel_preload_style( $value ) {
2892 $style_array = [];
2893 $slides_perview = $value['slidesPerView'] ?? 1;
2894 if ( is_array( $slides_perview ) ) {
2895 if ( $slides_perview['auto'] ?? false ) {
2896 $slides_perview = 'auto';
2897 } else {
2898 $slides_perview = $slides_perview['value'] ?? 1;
2899 }
2900 }
2901
2902 if ( $slides_perview !== 'auto' ) {
2903 $gap_count = absint( ceil( $slides_perview ) ) - 1;
2904 if ( $slides_perview <= 0 ) {
2905 $slides_perview = 1;
2906 }
2907 } else {
2908 $gap_count = 0;
2909 }
2910
2911 $space_between = $value['spaceBetween'] ?? 0;
2912 if ( $space_between < 0 ) {
2913 $space_between = 0;
2914 }
2915
2916 $total_gap = $gap_count * $space_between;
2917
2918 $style_array['--cbb-slide-width'] = $slides_perview === 'auto' ? 'auto' : "calc((100% - {$total_gap}px ) / {$slides_perview})";
2919 $style_array['--cbb-carousel-gap'] = "{$space_between}px";
2920
2921 return $style_array;
2922 }
2923
2924 /**
2925 * Build a json string of carousel settings
2926 *
2927 * @param array $carousel_settings
2928 * @param boolean $exclude_li_role
2929 * @return string
2930 */
2931 private function build_carousel_settings( $carousel_settings, $exclude_li_role = false ) {
2932 // phpcs:disable
2933 $dataset = [];
2934
2935 // Speed.
2936 if ( $carousel_settings['speed'] ?? false ) {
2937 $dataset['speed'] = $carousel_settings['speed'];
2938 }
2939
2940 // Loop and rewind.
2941 $dataset['loop'] = false;
2942 $dataset['rewind'] = false;
2943
2944 $loop_type = $carousel_settings['loopType'] ?? '';
2945 if ( 'infinite' === $loop_type ) {
2946 $dataset['loop'] = true;
2947 } elseif ( 'rewind' === $loop_type ) {
2948 $dataset['rewind'] = true;
2949 }
2950
2951 // Autoplay.
2952 $dataset['autoplay'] = false;
2953 $autoplay = $carousel_settings['autoplay'] ?? false;
2954 if ( $autoplay && is_array( $autoplay ) && ( $autoplay['enable'] ?? false ) ) {
2955 unset( $autoplay['enable'] );
2956
2957 if ( !$dataset['loop'] && !$dataset['rewind']) {
2958 $autoplay['stopOnLastSlide'] = true;
2959 }
2960
2961 $dataset['autoplay'] = $autoplay;
2962 }
2963
2964 // Play/pause.
2965 if ( $carousel_settings['playPause']['enable'] ?? false ) {
2966 $dataset['playPause'] = $carousel_settings['playPause'];
2967 }
2968
2969 // Direction.
2970 if ( $carousel_settings['direction'] ?? false ) {
2971 $dataset['direction'] = $carousel_settings['direction'];
2972 }
2973
2974 // Effect.
2975 $effect = $carousel_settings['effect'] ?? 'slide';
2976 // Handle wrong default data.
2977 if ( is_array( $effect ) ) {
2978 $effect = 'slide';
2979 }
2980
2981 if ( $effect ) {
2982 $dataset['effect'] = $effect;
2983 $effectSettings = $carousel_settings['effectSettings'] ?? [];
2984 $settings = $effectSettings[ $effect . 'Effect' ] ?? false;
2985
2986 // Fade.
2987 if ( 'fade' === $effect ) {
2988 $dataset['fadeEffect'] = [ 'crossFade' => true ];
2989 } elseif ( 'coverflow' === $effect ) {
2990 // Coverflow.
2991 if ( is_array( $settings ) ) {
2992 $dataset['coverflowEffect'] = $settings;
2993 } else {
2994 $dataset['coverflowEffect'] = [ 'slideShadows' => false ];
2995 }
2996 } elseif ( 'creative' === $effect ) {
2997 // Creative.
2998 if ( is_array( $settings ) ) {
2999 $dataset['creativeEffect'] = $settings;
3000 } else {
3001 $dataset['creativeEffect'] = [
3002 'prev' => [
3003 'translate' => [ '-20%', 0, -1 ],
3004 ],
3005 'next' => [
3006 'translate' => [ '100%', 0, 0 ],
3007 ],
3008 ];
3009 }
3010
3011 $dataset['direction'] = "horizontal";
3012 }
3013 }
3014
3015 // slidesPerView.
3016 $slidesPerViewDependencies = [
3017 'fade',
3018 'flip',
3019 'cube',
3020 'cards',
3021 'creative',
3022 ];
3023
3024 if ( in_array($effect, $slidesPerViewDependencies, true ) ) {
3025 $dataset['slidesPerView'] = 1;
3026 } else {
3027 $slidesPerView = $this->carousel_refine_slides_per_view( $carousel_settings['slidesPerView'] ?? 1 );
3028 $slidesPerGroup = absint( $carousel_settings['slidesPerGroup'] ?? 0 );
3029
3030 if ( $slidesPerView ) {
3031 $dataset['slidesPerView'] = $slidesPerView;
3032
3033 if ( $slidesPerView >= 2 && $slidesPerGroup ) {
3034 $dataset['slidesPerGroup'] = $slidesPerGroup;
3035 }
3036 }
3037
3038 // spaceBetween.
3039 $spaceBetween = absint( $carousel_settings['spaceBetween'] ?? 0 );
3040 if ( $spaceBetween ) {
3041 $dataset['spaceBetween'] = $spaceBetween;
3042 }
3043
3044 // Breakpoints.
3045 $breakpoint_settings = $this->get_breakpoints();
3046 $breakpoint_values = [
3047 'sm' => $breakpoint_settings['sm']['breakpoint'] ?? 576,
3048 'md' => $breakpoint_settings['md']['breakpoint'] ?? 768,
3049 'lg' => $breakpoint_settings['lg']['breakpoint'] ?? 1024,
3050 ];
3051
3052 $breakpoints = $carousel_settings['breakpoints'] ?? [];
3053 if ( ($carousel_settings['enableResponsive'] ?? false) && is_array( $breakpoints ) ) {
3054 // Mobile first.
3055 $this->sort_styles( $breakpoints );
3056
3057 $breakpoint_atts = [];
3058 foreach ( array_keys( $breakpoints ) as $breakpoint ) :
3059 $value = $breakpoints[ $breakpoint ]['value'] ?? '';
3060 $inherit = $breakpoints[ $breakpoint ]['inherit'] ?? '';
3061
3062 $value_by_breakpoint = false;
3063 if ( $value && is_array( $value ) ) {
3064 $value_by_breakpoint = $value;
3065 }
3066
3067 if ( ! $value_by_breakpoint && is_string( $inherit ) ) {
3068 $inheritValue = $breakpoints[ $inherit ] ?? '';
3069 if ( $inheritValue && is_array( $inheritValue ) ) {
3070 $value_by_breakpoint = $inheritValue;
3071 }
3072 }
3073
3074 if ( is_array( $value_by_breakpoint ) ) {
3075 $value_by_breakpoint['slidesPerView'] = $this->carousel_refine_slides_per_view( $value_by_breakpoint['slidesPerView'] ?? 1);
3076 $breakpoint_atts[ $breakpoint_values[ $breakpoint ] ] = $value_by_breakpoint;
3077 }
3078 endforeach;
3079
3080 if ( $breakpoint_atts ) {
3081 // Replace global params.
3082 $first_breakpoint = array_values($breakpoint_atts)[0];
3083 $dataset['slidesPerView'] = $first_breakpoint['slidesPerView'];
3084 if ($dataset['slidesPerView'] > 1 && ($first_breakpoint['slidesPerGroup'] ?? 0)) {
3085 $dataset['slidesPerGroup'] = $first_breakpoint['slidesPerGroup'];
3086 }
3087 if ( $first_breakpoint['spaceBetween'] ) {
3088 $dataset['spaceBetween'] = $first_breakpoint['spaceBetween'];
3089 }
3090
3091 // Set the breakpoints.
3092 $dataset['breakpoints'] = $breakpoint_atts;
3093 }
3094 } else {
3095 if ( $carousel_settings['oneSlidePerViewInMobile'] ?? false ) {
3096 $dataset['slidesPerView'] = 1;
3097 if ( $slidesPerGroup ) {
3098 $dataset['slidesPerGroup'] = 1;
3099 }
3100
3101 $dataset['breakpoints'] = [
3102 $breakpoint_values['sm'] => [
3103 'slidesPerView' => 1,
3104 'slidesPerGroup' => 1,
3105 ],
3106
3107 $breakpoint_values['md'] => [
3108 'slidesPerView' => $slidesPerView,
3109 'slidesPerGroup' => $slidesPerGroup ? $slidesPerGroup : 1,
3110 ],
3111 ];
3112 }
3113 }
3114 }
3115
3116 // centeredSlides.
3117 $dataset['centeredSlides'] = $carousel_settings['centeredSlides'] ?? false;
3118 if ($dataset['centeredSlides'] && $effect === 'slide' && ($carousel_settings['centeredSlidesSettings']['enable'] ?? '')) {
3119 $dataset['centeredSlidesSettings'] = $carousel_settings['centeredSlidesSettings'];
3120 }
3121
3122 // Pagination.
3123 $pagination = $carousel_settings['pagination'] ?? [];
3124 $dataset['pagination'] = $pagination['enable'] ?? false;
3125 if ( $dataset['pagination'] ) {
3126 $dataset['paginationSettings'] = $pagination;
3127 }
3128
3129 // Navigation.
3130 $navigation = $carousel_settings['navigation'] ?? [];
3131 $dataset['navigation'] = $navigation['enable'] ?? false;
3132 if ( $dataset['navigation'] ) {
3133 $dataset['navigationSettings'] = $navigation;
3134 }
3135
3136 // Scrollbar.
3137 $scrollbar = $carousel_settings['scrollbar'] ?? [];
3138 $dataset['scrollbar'] = $scrollbar['enable'] ?? false;
3139 if ( $dataset['scrollbar'] ) {
3140 $dataset['scrollbarSettings'] = $scrollbar;
3141 }
3142
3143 // Equal height?.
3144 $equalHeight = $carousel_settings['equalHeight'] ?? false;
3145 if ( $equalHeight ) {
3146 // Only add the property when it is true to prevent from invalid content.
3147 $dataset['equalHeight'] = $equalHeight;
3148 }
3149
3150 // Sync carousels.
3151 $thumbs = $carousel_settings['thumbs'] ?? [];
3152 if ( $thumbs['enable'] ?? false ) {
3153 $dataset['thumbsSettings'] = $thumbs;
3154 }
3155
3156 // Exclude li role.
3157 if ( $exclude_li_role ) {
3158 $dataset['a11y'] = ['slideRole' => '', 'slideLabelMessage' => ''];
3159 }
3160
3161 return wp_json_encode( $dataset );
3162 // phpcs:enable
3163 }
3164
3165 /**
3166 * Refine the slides per view
3167 *
3168 * @param mixed $raw_value
3169 * @return void
3170 */
3171 private function carousel_refine_slides_per_view( $raw_value ) {
3172 $value = $raw_value;
3173 if ( is_array( $raw_value ) ) {
3174 $value = $raw_value['auto'] ?? false
3175 ? 'auto'
3176 : floatval( $raw_value['value'] ?? 1 );
3177 }
3178
3179 return $value ? $value : 1;
3180 }
3181
3182 /**
3183 * Get nested post template block for core/query
3184 *
3185 * @param WP_Block $block_instance
3186 * @return boolean|WP_Block
3187 */
3188 private function get_nested_post_template( $block_instance ) {
3189 return $this->find_nested_block( $block_instance, 'core/post-template' );
3190 }
3191
3192 /**
3193 * Find a specific nested block from a parent block
3194 *
3195 * @param WP_Block $block
3196 * @param string $block_name
3197 * @return mixed
3198 */
3199 public function find_nested_block( $block, $block_name ) {
3200 if ( $block->name === $block_name ) {
3201 return $block;
3202 }
3203
3204 foreach ( $block->inner_blocks as $inner_block ) {
3205 $result = $this->find_nested_block( $inner_block, $block_name );
3206
3207 if ( $result ) {
3208 return $result;
3209 }
3210 }
3211
3212 return false;
3213 }
3214
3215 /**
3216 * Build responsive style
3217 *
3218 * @param array $args
3219 * @param array &$style_array
3220 * @param array &$responsive_style_array
3221 * @return string
3222 */
3223 private function build_query_loop_responsive_style( $args ) {
3224 $func_build_responsive_style = $args['func_build_responsive_style'] ?? '';
3225 if ( ! \is_callable( $func_build_responsive_style ) ) {
3226 return '';
3227 }
3228
3229 $setting_value = $args['setting_value'] ?? [];
3230 if ( empty( $setting_value ) || ! \is_array( $setting_value ) ) {
3231 return '';
3232 }
3233
3234 $breakpoints = $args['breakpoints'];
3235 $selector = $args['selector'];
3236
3237 $responsive_styles = [];
3238 foreach ( $setting_value as $breakpoint => $value_by_breakpoint ) {
3239 $value = null;
3240 if ( $this->is_valid_value( $value_by_breakpoint['value'] ?? null ) ) {
3241 $value = $value_by_breakpoint['value'];
3242 } elseif ( isset( $value_by_breakpoint['inherit'] ) && is_string( $value_by_breakpoint['inherit'] ) ) {
3243 $value = $setting_value[ $value_by_breakpoint['inherit'] ]['value'] ?? null;
3244 }
3245
3246 if ( $this->is_valid_value( $value ) ) {
3247 $style_by_breakpoint = $func_build_responsive_style( array_merge( $args, [ 'value' => $value ] ) );
3248 if ( $style_by_breakpoint ) {
3249 $responsive_styles[ $breakpoint ] = $style_by_breakpoint;
3250 }
3251 }
3252 }
3253
3254 $this->sort_styles( $responsive_styles );
3255
3256 $dependent_style = '';
3257 $func_build_dependent_style = $args['func_build_dependent_style'] ?? '';
3258 if ( \is_callable( $func_build_dependent_style ) ) {
3259 $dependent_style = $func_build_dependent_style( array_merge( $args, [ 'responsive_styles' => $responsive_styles ] ) );
3260 }
3261
3262 $style = '';
3263 $last_responsive_style = '';
3264 foreach ( $responsive_styles as $breakpoint => $style_by_breakpoint ) {
3265 if ( ! $style_by_breakpoint || ! is_array( $style_by_breakpoint ) ) {
3266 continue;
3267 }
3268
3269 $responsive_style = '';
3270 foreach ( $style_by_breakpoint as $attr_key => $attr_value ) {
3271 $responsive_style .= "{$attr_key}:{$attr_value};";
3272 }
3273
3274 if ( $responsive_style !== $last_responsive_style ) {
3275 $style_with_selector = "{$selector}{{$responsive_style}}";
3276 $media_query = $breakpoints[ $breakpoint ]['minQuery'] ?? '';
3277 if ( $media_query ) {
3278 $style .= \str_replace( '##CONTENT##', $style_with_selector, $media_query );
3279 } else {
3280 $style .= $style_with_selector;
3281 }
3282 $last_responsive_style = $responsive_style;
3283 }
3284 }
3285
3286 return $style . $dependent_style;
3287 }
3288
3289 /**
3290 * Build the breakpoints
3291 *
3292 * @return array
3293 */
3294 private function get_breakpoints() {
3295 if ( ! $this->breakpoints ) {
3296 $breakpoints = get_option( 'cbb_breakpoints' );
3297 if ( empty( $breakpoints ) ) {
3298 $breakpoints = [
3299 [
3300 'breakpoint' => 576,
3301 'prefix' => 'sm',
3302 ],
3303 [
3304 'breakpoint' => 768,
3305 'prefix' => 'md',
3306 ],
3307 [
3308 'breakpoint' => 1024,
3309 'prefix' => 'lg',
3310 ],
3311 ];
3312 }
3313
3314 $breakpoints = array_filter(
3315 array_map(
3316 function ( $item ) {
3317 if ( empty( $item['breakpoint'] ) || empty( $item['prefix'] ) ) {
3318 return false;
3319 }
3320
3321 $breakpoint = absint( $item['breakpoint'] );
3322
3323 if ( $breakpoint < 10 ) {
3324 return false;
3325 }
3326
3327 $max_breakpoint = $breakpoint - 1;
3328 $item['breakpointMax'] = $max_breakpoint;
3329
3330 if ( $item['prefix'] !== 'sm' ) {
3331 $item['minQuery'] = "@media (min-width: {$breakpoint}px){##CONTENT##}";
3332 $item['maxQuery'] = "@media (max-width: {$max_breakpoint}px){##MAX_CONTENT##}";
3333 } else {
3334 $item['minQuery'] = '';
3335 $item['maxQuery'] = "@media (max-width: {$max_breakpoint}px){##MAX_CONTENT##}";
3336 }
3337
3338 return $item;
3339 },
3340 $breakpoints
3341 )
3342 );
3343
3344 $this->breakpoints = array_column( $breakpoints, null, 'prefix' );
3345 }
3346
3347 return $this->breakpoints;
3348 }
3349
3350 /**
3351 * Generate block selector.
3352 *
3353 * @param string $block_name
3354 * @param WP_Block $block_instance
3355 * @return string
3356 */
3357 private function generate_selector( $block_name, $block_instance ) {
3358 $selector_prefix = 'cbb';
3359
3360 if ( '' !== $this->style_contexts['loop'] ) {
3361 $post_id = $block_instance->context['postId'] ?? 0;
3362 if ( ! $this->style_contexts['post'] || $this->style_contexts['post'] !== $post_id ) {
3363 $this->style_contexts['post'] = $post_id;
3364 $this->style_contexts['counter'] = 0;
3365 $this->style_contexts['content_counter'] = 0;
3366 }
3367
3368 if ( $this->style_contexts['content'] ) {
3369 // The content context selector.
3370 $content_counter = ++$this->style_contexts['content_counter'];
3371 return "{$selector_prefix}-p{$this->style_contexts['post']}-{$content_counter}";
3372 }
3373
3374 $counter = ++$this->style_contexts['counter'];
3375 // The selector inside the loop.
3376 return "{$selector_prefix}-{$this->style_contexts['loop']}-{$counter}";
3377 } else {
3378 // The global selector.
3379 return wp_unique_id( "{$selector_prefix}-g-" );
3380 }
3381 }
3382
3383 /**
3384 * Update loop id when having a post template block
3385 *
3386 * @param array $context
3387 * @param array $block
3388 * @return array
3389 */
3390 public function update_loop_id( $context, $block ) {
3391 if ( $this->is_loop_temlate_block( $block['blockName'] ?? '' ) ) {
3392 if ( isset( $context['queryId'] ) ) {
3393 $query_id = $context['queryId'];
3394 if ( in_array( $query_id, $this->style_contexts['loops'], true ) ) {
3395 $count = count(
3396 array_filter(
3397 $this->style_contexts['loops'],
3398 function ( $value ) use ( $query_id ) {
3399 return $value === $query_id;
3400 }
3401 )
3402 );
3403
3404 $this->style_contexts['loop'] = "{$query_id}i{$count}";
3405 } else {
3406 $this->style_contexts['loop'] = $query_id;
3407 }
3408
3409 $this->style_contexts['loops'][] = $query_id;
3410 }
3411 }
3412
3413 // Post content inside a loop.
3414 if ( $this->style_contexts['loop'] && 'core/post-content' === ( $block['blockName'] ?? '' ) ) {
3415 $this->style_contexts['content'] = true;
3416 }
3417
3418 return $context;
3419 }
3420
3421 /**
3422 * Reset loop id when the post template is rendered
3423 *
3424 * @param string $content
3425 * @param array $block
3426 * @return string
3427 */
3428 public function reset_loop_id( $content, $block ) {
3429 if ( $this->is_loop_temlate_block( $block['blockName'] ?? '' ) ) {
3430 $this->style_contexts['loop'] = '';
3431 $this->style_contexts['post'] = 0;
3432 }
3433
3434 // Post content inside a loop.
3435 if ( 'core/post-content' === ( $block['blockName'] ?? '' ) ) {
3436 $this->style_contexts['content'] = false;
3437 }
3438
3439 return $content;
3440 }
3441
3442 /**
3443 * Render sticky attributes for core template part blocks
3444 *
3445 * @param string $block_content
3446 * @param array $block
3447 * @param WP_Block $block_instance
3448 * @return string
3449 */
3450 public function render_template_part_sticky( $block_content, $block, $block_instance ) {
3451 // Ignore admin side.
3452 if ( is_admin() ) {
3453 return $block_content;
3454 }
3455
3456 // If this block has sticky data.
3457 $sticky_data = $block['attrs']['boldblocks']['sticky'] ?? false;
3458 if ( $sticky_data && ( $sticky_data['enable'] ?? false ) ) {
3459 $sticky_classes = [ 'is-sticky-block' ];
3460 $sticky_classes[] = 'is-stick-to-' . ( $sticky_data['stickTo'] ?? 'top' );
3461
3462 if ( $sticky_data['isStickOnScrollingUp'] ?? false ) {
3463 $sticky_classes[] = 'is-sticky-on-scrollup';
3464 } elseif ( $sticky_data['isFixed'] ?? false ) {
3465 $sticky_classes[] = 'is-fixed';
3466 if ( $sticky_data['isInFlow'] ?? false ) {
3467 $sticky_classes[] = 'is-in-flow';
3468 }
3469 if ( $sticky_data['isDetectingScroll'] ?? false ) {
3470 $sticky_classes[] = 'is-detecting-scroll';
3471 }
3472 } elseif ( $sticky_data['isDetectingStuck'] ?? false ) {
3473 $sticky_classes[] = 'is-detecting-stuck';
3474 }
3475
3476 if ( in_array( $block['attrs']['area'] ?? '', [ 'header', 'footer' ], true ) ) {
3477 $sticky_classes[] = 'is-sticky-' . $block['attrs']['area'];
3478 }
3479
3480 $block_content = $this->add_class_to_block( $block_content, implode( ' ', $sticky_classes ) );
3481 }
3482
3483 return $block_content;
3484 }
3485
3486 /**
3487 * Render settings for carousel layout blocks
3488 *
3489 * @param string $block_content
3490 * @param array $block
3491 * @param WP_Block $block_instance
3492 * @return string
3493 */
3494 public function render_carousel_settings( $block_content, $block, $block_instance ) {
3495 // Ignore admin side.
3496 if ( is_admin() ) {
3497 return $block_content;
3498 }
3499
3500 // There is no carousel layout.
3501 if ( 'carousel' !== ( $block_instance->block_type->supports['layoutType'] ?? '' ) ) {
3502 return $block_content;
3503 }
3504
3505 // Carousel settings.
3506 $carousel_settings = $block['attrs']['boldblocks']['carousel'] ?? [];
3507
3508 // Dataset attribute.
3509 $carousel_dataset = $this->build_carousel_settings( $carousel_settings );
3510 if ( $carousel_dataset ) {
3511 $block_content = $this->add_data_to_block( $block_content, 'data-carousel-settings', esc_attr( $carousel_dataset ) );
3512 }
3513
3514 // Preload class.
3515 $preloader_class = 'cbb-carousel-preload';
3516
3517 // Has preloader?
3518 $has_preloader = $carousel_settings['displayLoader'] ?? false;
3519 if ( $has_preloader ) {
3520 $preloader_class .= ' has-preloader';
3521 if ( 'coverflow' === ( $carousel_settings['effect'] ?? '' ) ) {
3522 $preloader_class .= ' is-fading-loader';
3523 }
3524
3525 // Add the loader to the markup.
3526 $block_content = $this->add_inner_content_to_block( $block_content, $this->get_carousel_preloader_markup( $block, $block_instance ) );
3527 }
3528
3529 // Mark it as having preloader.
3530 $block_content = $this->add_class_to_block( $block_content, $preloader_class );
3531
3532 return $block_content;
3533 }
3534
3535 /**
3536 * Get the preloader markup for carousels
3537 *
3538 * @param array $block
3539 * @param WP_Block $block_instance
3540 * @return string
3541 */
3542 private function get_carousel_preloader_markup( $block, $block_instance ) {
3543 return apply_filters( 'cbb_carousel_preloader', '<div class="cbb-loader"></div>', $block, $block_instance );
3544 }
3545
3546 /**
3547 * Migrate cbb class for old CBB blocks.
3548 *
3549 * @param string $block_content
3550 * @param array $block
3551 * @param WP_Block $block_instance
3552 * @return string
3553 */
3554 public function migrate_cbb_class( $block_content, $block, $block_instance ) {
3555 // Ignore admin side or empty content.
3556 if ( is_admin() || ! $block_content ) {
3557 return $block_content;
3558 }
3559
3560 // There is no need to migrate.
3561 $migrate_cbb_class = $block_instance->block_type->supports['migrateClass'] ?? '';
3562 if ( ! $migrate_cbb_class ) {
3563 return $block_content;
3564 }
3565
3566 // Replace the class.
3567 $block_reader = new \WP_HTML_Tag_Processor( $block_content );
3568 if ( $block_reader->next_tag( [ 'class_name' => $migrate_cbb_class ] ) ) {
3569 $new_class = str_ends_with( $migrate_cbb_class, 'custom' ) ? 'cbb-block' : 'cbb-block-parent';
3570 $block_reader->set_attribute( 'class', str_replace( $migrate_cbb_class, $new_class, $block_reader->get_attribute( 'class' ) ) );
3571
3572 // Render the updated HTML.
3573 return $block_reader->get_updated_html();
3574 }
3575
3576 return $block_content;
3577 }
3578
3579 /**
3580 * Support block style for non-cbb blocks
3581 *
3582 * @param string $is_supported
3583 * @param array $block
3584 * @return boolean
3585 */
3586 public function support_block_style( $is_supported, $block ) {
3587 if ( in_array( $block['blockName'] ?? '', $this->non_cbb_blocks, true ) ) {
3588 $is_supported = true;
3589 }
3590
3591 return $is_supported;
3592 }
3593
3594 /**
3595 * Set a valid layout for none-cbb blocks
3596 *
3597 * @param string $block_layout
3598 * @param array $block
3599 * @return string
3600 */
3601 public function get_block_layout( $block_layout, $block ) {
3602 if ( in_array( $block['blockName'] ?? '', $this->non_cbb_blocks, true ) ) {
3603 $block_layout = 'standalone';
3604 }
3605
3606 return $block_layout;
3607 }
3608
3609 /**
3610 * Check whether the block is a loop template block.
3611 *
3612 * @param string $block_name
3613 * @return array
3614 */
3615 private function is_loop_temlate_block( $block_name ) {
3616 $loop_template_blocks = apply_filters( 'cbb_get_loop_temlate_blocks', [ 'core/post-template' ] );
3617
3618 return in_array( $block_name, $loop_template_blocks, true );
3619 }
3620
3621 /**
3622 * Check whether current block has custom style or not.
3623 *
3624 * @param array $block
3625 * @param WP_Block $block_instance
3626 * @return boolean
3627 */
3628 private function has_style( $block, $block_instance ) {
3629 // There is no boldblocks value.
3630 if ( ! isset( $block['attrs']['boldblocks'] ) ) {
3631 return false;
3632 }
3633
3634 return apply_filters( 'cbb_support_block_style', isset( $block_instance->block_type->supports['cbb'] ), $block, $block_instance );
3635 }
3636
3637 /**
3638 * Order style array by breakpoints
3639 *
3640 * @param array $styles
3641 * @return void
3642 */
3643 private function sort_styles( &$styles ) {
3644 uksort(
3645 $styles,
3646 function ( $a, $b ) {
3647 if ( 'lg' === $a || 'sm' === $b ) {
3648 return 1;
3649 }
3650
3651 if ( 'sm' === $a || 'lg' === $b ) {
3652 return -1;
3653 }
3654
3655 return 0;
3656 }
3657 );
3658 }
3659
3660 /**
3661 * Build responsive style
3662 *
3663 * @param array $args
3664 * @param array &$responsive_style_array
3665 * @param array &$classes
3666 * @return string
3667 */
3668 private function build_responsive_style( $args, &$responsive_style_array, &$classes ) {
3669 $return_value = false;
3670 $func_build_responsive_style = $args['func_build_responsive_style'] ?? '';
3671 if ( ! \is_callable( $func_build_responsive_style ) ) {
3672 return $return_value;
3673 }
3674
3675 $setting_value = $args['setting_value'] ?? [];
3676 if ( empty( $setting_value ) || ! \is_array( $setting_value ) ) {
3677 return $return_value;
3678 }
3679
3680 $breakpoints = $args['breakpoints'];
3681 $selector = $args['selector'];
3682
3683 $setting_values = [];
3684 $responsive_styles = [];
3685 foreach ( $setting_value as $breakpoint => $value_by_breakpoint ) {
3686 $value = null;
3687 if ( $this->is_valid_value( $value_by_breakpoint['value'] ?? null ) ) {
3688 $value = $value_by_breakpoint['value'];
3689 } elseif ( isset( $value_by_breakpoint['inherit'] ) && is_string( $value_by_breakpoint['inherit'] ) ) {
3690 $value = $setting_value[ $value_by_breakpoint['inherit'] ]['value'] ?? null;
3691 }
3692
3693 if ( $this->is_valid_value( $value ) ) {
3694 $style_by_breakpoint = $func_build_responsive_style( array_merge( $args, [ 'value' => $value ] ) );
3695 if ( $style_by_breakpoint ) {
3696 $responsive_styles[ $breakpoint ] = $style_by_breakpoint;
3697 }
3698 }
3699
3700 $setting_values[ $breakpoint ] = $value;
3701 }
3702
3703 $this->sort_styles( $responsive_styles );
3704
3705 $dependent_style = '';
3706 $func_build_dependent_style = $args['func_build_dependent_style'] ?? '';
3707 if ( \is_callable( $func_build_dependent_style ) ) {
3708 $dependent_style = $func_build_dependent_style(
3709 array_merge(
3710 $args,
3711 [
3712 'setting_values' => $setting_values,
3713 'responsive_styles' => $responsive_styles,
3714 ]
3715 ),
3716 $classes
3717 );
3718 }
3719
3720 $keys = [];
3721 $style = '';
3722 $last_responsive_style = '';
3723 foreach ( $responsive_styles as $breakpoint => $style_by_breakpoint ) {
3724 $responsive_style = '';
3725 foreach ( $style_by_breakpoint as $attr_key => $attr_value ) {
3726 $responsive_style .= "{$attr_key}:{$attr_value};";
3727
3728 if ( ! in_array( $attr_key, $keys, true ) ) {
3729 $keys[] = $attr_key;
3730 $classes[] = $breakpoint . '-' . str_replace( '--cbb--', 'cbb-', $attr_key );
3731 }
3732 }
3733
3734 if ( $responsive_style !== $last_responsive_style ) {
3735 $style_with_selector = "{$selector}{{$responsive_style}}";
3736 $min_query = $breakpoints[ $breakpoint ]['minQuery'] ?? '';
3737 if ( $min_query ) {
3738 $style_with_selector = \str_replace( '##CONTENT##', $style_with_selector, $min_query );
3739 }
3740
3741 $style .= $style_with_selector;
3742 $last_responsive_style = $responsive_style;
3743 }
3744 }
3745
3746 // Add style to the style_array.
3747 if ( $style ) {
3748 $style .= $dependent_style;
3749 $responsive_style_array[] = $style;
3750
3751 return $style;
3752 }
3753
3754 return $return_value;
3755 }
3756
3757 /**
3758 * Check wether the block markup has a attribute or not.
3759 *
3760 * @param string $attribute
3761 * @param string $block_content
3762 * @return boolean
3763 */
3764 private function block_has_attribute( $attribute, $block_content ) {
3765 $tags = new \WP_HTML_Tag_Processor( $block_content );
3766 if ( $tags->next_tag() ) {
3767 return null !== $tags->get_attribute( $attribute );
3768 }
3769
3770 return false;
3771 }
3772
3773 /**
3774 * Add CSS class to block wrapper
3775 *
3776 * @param string $block_content
3777 * @param string $classes
3778 * @return string
3779 */
3780 public function add_class_to_block( $block_content, $classes ) {
3781 $tags = new \WP_HTML_Tag_Processor( $block_content );
3782 if ( $tags->next_tag() ) {
3783 $tags->add_class( $classes );
3784 }
3785 return $tags->get_updated_html();
3786 }
3787
3788 /**
3789 * Add data attribute to block wrapper
3790 *
3791 * @param string $block_content
3792 * @param string $value
3793 * @return string
3794 */
3795 public function add_data_to_block( $block_content, $name, $value ) {
3796 $tags = new \WP_HTML_Tag_Processor( $block_content );
3797 if ( $tags->next_tag() ) {
3798 $tags->set_attribute( $name, $value );
3799 }
3800
3801 return $tags->get_updated_html();
3802 }
3803
3804 /**
3805 * Add inner content to block markup
3806 *
3807 * @param string $block_content
3808 * @param string $inner_content
3809 * @return string
3810 */
3811 public function add_inner_content_to_block( $block_content, $inner_content ) {
3812 if ( $inner_content ) {
3813 $block_content = \preg_replace(
3814 '/' . \preg_quote( '>', '/' ) . '/',
3815 '>' . $inner_content,
3816 $block_content,
3817 1
3818 );
3819 }
3820
3821 return $block_content;
3822 }
3823
3824 /**
3825 * Detect a value is valid
3826 *
3827 * @param mixed $value
3828 * @return boolean
3829 */
3830 private function is_valid_value( $value ) {
3831 return isset( $value ) && $value !== '';
3832 }
3833
3834 /**
3835 * Detect a responsive value is valid
3836 *
3837 * @param mixed $value
3838 * @return boolean
3839 */
3840 private function is_valid_responsive_value( $value ) {
3841 return ! empty( $value['lg']['value'] ) || ! empty( $value['md']['value'] ) || ! empty( $value['sm']['value'] );
3842 }
3843
3844 /**
3845 * Get value for spacing var
3846 *
3847 * @param string $value
3848 * @return string
3849 */
3850 private function get_spacing_value( $value ) {
3851 if ( ! $this->is_valid_value( $value ) ) {
3852 return '';
3853 }
3854
3855 if ( strpos( $value, 'var:preset|spacing|' ) !== false ) {
3856 preg_match( '/var:preset\|spacing\|(.+)/', $value, $slug );
3857
3858 if ( ! $slug ) {
3859 return $value;
3860 }
3861
3862 return "var(--wp--preset--spacing--{$slug[1]})";
3863 }
3864
3865 return $value;
3866 }
3867
3868 /**
3869 * Get CSS value for a color object.
3870 *
3871 * @param array $color_array
3872 * @return string
3873 */
3874 private function get_css_color_value( $color_array ) {
3875 $value = '';
3876
3877 if ( ! empty( $color_array ) ) {
3878 if ( is_string( $color_array ) ) {
3879 $value = $color_array;
3880 } elseif ( is_array( $color_array ) ) {
3881 if ( $color_array['gradientSlug'] ?? false ) {
3882 $value = "var(--wp--preset--gradient--{$color_array['gradientSlug']}, {$color_array['gradientValue']})";
3883 } elseif ( $color_array['gradientValue'] ?? false ) {
3884 $value = $color_array['gradientValue'];
3885 } elseif ( $color_array['slug'] ?? false ) {
3886 $value = "var(--wp--preset--color--{$color_array['slug']}, {$color_array['value']})";
3887 } elseif ( $color_array['value'] ?? false ) {
3888 $value = $color_array['value'];
3889 }
3890 }
3891 }
3892
3893 return $value;
3894 }
3895
3896 /**
3897 * Refine custom JS and CSS value
3898 *
3899 * @param string $code
3900 * @param array $tokens
3901 * @param string $type
3902 * @return string
3903 */
3904 public function refine_custom_value( $code, $tokens = [], $type = 'CSS' ) {
3905 // Remove tags.
3906 // $code = preg_replace( '/(<([\w-]+)>)/mi', '', $code );
3907
3908 // Replace breakpoint tokens.
3909 $breakpoints = $this->get_breakpoints();
3910
3911 $tokens = array_merge(
3912 $tokens,
3913 [
3914 'TABLET_UP' => ( $breakpoints['md']['breakpoint'] ?? 768 ) . 'px',
3915 'TABLET_DOWN' => ( $breakpoints['md']['breakpointMax'] ?? 767 ) . 'px',
3916 'DESKTOP_UP' => ( $breakpoints['lg']['breakpoint'] ?? 1024 ) . 'px',
3917 'DESKTOP_DOWN' => ( $breakpoints['lg']['breakpointMax'] ?? 1023 ) . 'px',
3918 ]
3919 );
3920
3921 $code = str_replace( array_keys( $tokens ), array_values( $tokens ), $code );
3922
3923 // Minify it.
3924 return $this->minify( $code, $type );
3925 }
3926
3927 /**
3928 * Simple minify JS and CSS
3929 *
3930 * @param string $code
3931 * @param string $type
3932 * @return string
3933 */
3934 public function minify( $code, $type = 'CSS' ) {
3935 if ( $this->the_plugin_instance->is_debug_mode() ) {
3936 return $code;
3937 }
3938
3939 if ( 'CSS' === $type ) {
3940 $code = preg_replace( '/\s+/', ' ', $code );
3941 } elseif ( 'JS' === $type ) {
3942 // Remove comments first - https://stackoverflow.com/a/31907095/1038868.
3943 $pattern = '/(?:(?:\/\*(?:[^*]|(?:\*+[^*\/]))*\*+\/)|(?:(?<!\:|\\\|\'|\")\/\/.*))/';
3944 $code = preg_replace( $pattern, '', $code );
3945
3946 // Remove spaces, tabs, and new lines after a line if it ends with a semicolon, a bracket, or a comma, or an open parentheses.
3947 $code = preg_replace( '/([;{,\(])\s+/', '$1', $code );
3948
3949 // Remove before ...
3950 $code = preg_replace( '/\s+([}\)])/', '$1', $code );
3951
3952 // Remove spaces before a line.
3953 $code = preg_replace( '/^\s+/m', '', $code );
3954 }
3955
3956 return trim( $code );
3957 }
3958 }
3959 endif;
3960