PluginProbe
Slider Block by Sliderberg – WordPress Slider & Carousel Plugin for Gutenberg / 1.2.3
Slider Block by Sliderberg – WordPress Slider & Carousel Plugin for Gutenberg v1.2.3
1.2.3 1.2.2 1.2.0 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.7 1.0.8 1.0.9
sliderberg / includes / slider-renderer.php

slider-renderer.php in Slider Block by Sliderberg – WordPress Slider & Carousel Plugin for Gutenberg 1.2.3, at includes/slider-renderer.php

415 lines 16.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Slider block server-side renderer.
4 * Outputs Swiper-compatible HTML with config in data-swiper-data attribute.
5 *
6 * @package Sliderberg
7 */
8
9 if ( ! defined( 'WPINC' ) ) {
10 die;
11 }
12
13 /**
14 * Check whether a transition effect can run in carousel mode.
15 *
16 * @param string $transition_effect Selected transition effect.
17 * @return bool
18 */
19 function sliderberg_is_transition_effect_allowed_in_carousel( $transition_effect ) {
20 return in_array( $transition_effect, array( 'slide', 'coverflow' ), true );
21 }
22
23 /**
24 * Resolve the effective layout mode from block attributes.
25 *
26 * Falls back to the legacy `isCarouselMode` boolean for content saved before
27 * `mode` replaced it. WordPress's WP_Block_Type::prepare_attributes_for_render()
28 * leaves attribute keys it doesn't recognize untouched, so `isCarouselMode`
29 * still reaches this function for old posts even though it's no longer
30 * declared in block.json.
31 *
32 * Note `isCarouselMode` is only present when it was explicitly `true` —
33 * WordPress omits attributes that equal their schema default (`false`) from
34 * saved markup, so the far more common "always was Slider mode" case has
35 * neither `mode` nor `isCarouselMode` at all. `$content` (the already-
36 * rendered inner slide blocks) is what tells that apart from a genuinely
37 * fresh, never-picked chooser block, which has no inner blocks yet.
38 *
39 * @param array $attrs Block attributes.
40 * @param string $content Rendered inner block content, if available.
41 * @return string 'slider', 'carousel', or '' if no layout has been picked.
42 */
43 function sliderberg_resolve_mode( $attrs, $content = '' ) {
44 if ( ! empty( $attrs['mode'] ) ) {
45 return $attrs['mode'];
46 }
47
48 if ( isset( $attrs['isCarouselMode'] ) ) {
49 return $attrs['isCarouselMode'] ? 'carousel' : 'slider';
50 }
51
52 if ( '' !== trim( $content ) ) {
53 return 'slider';
54 }
55
56 return '';
57 }
58
59 /**
60 * Register the slider block with PHP rendering.
61 */
62 function sliderberg_register_slider_block() {
63 register_block_type_from_metadata(
64 SLIDERBERG_PLUGIN_DIR . 'build/blocks/slider',
65 array(
66 'render_callback' => 'render_sliderberg_slider_block',
67 )
68 );
69 }
70
71 /**
72 * Build the Swiper configuration array from block attributes.
73 *
74 * @param string $transition_effect Selected transition effect.
75 * @param bool $is_carousel Whether carousel mode is enabled.
76 * @return array<string, mixed>
77 */
78 function sliderberg_get_transition_effect_config( $transition_effect, $is_carousel = false ) {
79 if ( $is_carousel && ! sliderberg_is_transition_effect_allowed_in_carousel( $transition_effect ) ) {
80 return array(
81 'effect' => 'slide',
82 );
83 }
84
85 if ( 'fade' === $transition_effect ) {
86 return array(
87 'effect' => 'fade',
88 'fadeEffect' => array(
89 'crossFade' => true,
90 ),
91 );
92 }
93
94 if ( 'zoom' === $transition_effect ) {
95 return array(
96 'effect' => 'creative',
97 'creativeEffect' => array(
98 'prev' => array(
99 'opacity' => 0,
100 'scale' => 0.95,
101 ),
102 'next' => array(
103 'opacity' => 0,
104 'scale' => 1.05,
105 ),
106 ),
107 );
108 }
109
110 if ( 'coverflow' === $transition_effect ) {
111 return array(
112 'centeredSlides' => $is_carousel,
113 'effect' => 'coverflow',
114 'coverflowEffect' => array(
115 'depth' => 100,
116 'modifier' => 1,
117 'rotate' => 50,
118 'scale' => 1,
119 'slideShadows' => true,
120 'stretch' => 0,
121 ),
122 'watchSlidesProgress' => $is_carousel,
123 );
124 }
125
126 if ( 'flip' === $transition_effect ) {
127 return array(
128 'effect' => 'flip',
129 'flipEffect' => array(
130 'limitRotation' => true,
131 'slideShadows' => true,
132 ),
133 );
134 }
135
136 if ( 'cube' === $transition_effect ) {
137 return array(
138 'effect' => 'cube',
139 'cubeEffect' => array(
140 'shadow' => true,
141 'shadowOffset' => 20,
142 'shadowScale' => 0.94,
143 'slideShadows' => true,
144 ),
145 );
146 }
147
148 if ( 'parallax' === $transition_effect ) {
149 return array(
150 'effect' => 'slide',
151 'parallax' => true,
152 );
153 }
154
155 return array(
156 'effect' => 'slide',
157 );
158 }
159
160 /**
161 * Build the Swiper configuration array from block attributes.
162 *
163 * @param array $attrs Block attributes.
164 * @param string $content Rendered inner block content, if available (see
165 * sliderberg_resolve_mode() for why this matters).
166 * @return array Swiper config.
167 */
168 function sliderberg_build_swiper_config( $attrs, $content = '' ) {
169 $attrs = apply_filters( 'sliderberg_slider_attributes', $attrs );
170
171 $transition_effect = isset( $attrs['transitionEffect'] ) ? $attrs['transitionEffect'] : 'slide';
172 $is_carousel = 'carousel' === sliderberg_resolve_mode( $attrs, $content );
173 $slides_to_show = isset( $attrs['slidesToShow'] ) ? absint( $attrs['slidesToShow'] ) : 3;
174 $slides_to_scroll = isset( $attrs['slidesToScroll'] ) ? absint( $attrs['slidesToScroll'] ) : 1;
175 $slide_spacing = isset( $attrs['slideSpacing'] ) ? absint( $attrs['slideSpacing'] ) : 20;
176
177 $config = array_merge(
178 array(
179 'speed' => isset( $attrs['transitionDuration'] ) ? absint( $attrs['transitionDuration'] ) : 500,
180 'loop' => isset( $attrs['infiniteLoop'] ) ? (bool) $attrs['infiniteLoop'] : true,
181 'slidesPerView' => $is_carousel ? $slides_to_show : 1,
182 'slidesPerGroup' => $is_carousel ? $slides_to_scroll : 1,
183 'spaceBetween' => $is_carousel ? $slide_spacing : 0,
184 'keyboard' => array( 'enabled' => true ),
185 'a11y' => array(
186 'prevSlideMessage' => __( 'Previous slide', 'sliderberg' ),
187 'nextSlideMessage' => __( 'Next slide', 'sliderberg' ),
188 ),
189 ),
190 sliderberg_get_transition_effect_config( $transition_effect, $is_carousel )
191 );
192
193 // Autoplay
194 if ( ! empty( $attrs['autoplay'] ) ) {
195 $config['autoplay'] = array(
196 'delay' => isset( $attrs['autoplaySpeed'] ) ? absint( $attrs['autoplaySpeed'] ) : 5000,
197 'disableOnInteraction' => false,
198 'pauseOnMouseEnter' => isset( $attrs['pauseOnHover'] ) ? (bool) $attrs['pauseOnHover'] : true,
199 );
200 }
201
202 // Navigation
203 $hide_nav = ! empty( $attrs['hideNavigation'] );
204 if ( ! $hide_nav ) {
205 $config['navigation'] = array(
206 'nextEl' => '.swiper-button-next',
207 'prevEl' => '.swiper-button-prev',
208 );
209 }
210
211 // Pagination
212 $hide_dots = ! empty( $attrs['hideDots'] );
213 if ( ! $hide_dots ) {
214 $config['pagination'] = array(
215 'el' => '.swiper-pagination',
216 'clickable' => true,
217 );
218 }
219
220 // Responsive breakpoints (carousel mode only)
221 if ( $is_carousel ) {
222 $config['breakpoints'] = array(
223 0 => array(
224 'slidesPerView' => isset( $attrs['mobileSlidesToShow'] ) ? absint( $attrs['mobileSlidesToShow'] ) : 1,
225 'slidesPerGroup' => isset( $attrs['mobileSlidesToScroll'] ) ? absint( $attrs['mobileSlidesToScroll'] ) : 1,
226 'spaceBetween' => isset( $attrs['mobileSlideSpacing'] ) ? absint( $attrs['mobileSlideSpacing'] ) : 10,
227 ),
228 768 => array(
229 'slidesPerView' => isset( $attrs['tabletSlidesToShow'] ) ? absint( $attrs['tabletSlidesToShow'] ) : 2,
230 'slidesPerGroup' => isset( $attrs['tabletSlidesToScroll'] ) ? absint( $attrs['tabletSlidesToScroll'] ) : 1,
231 'spaceBetween' => isset( $attrs['tabletSlideSpacing'] ) ? absint( $attrs['tabletSlideSpacing'] ) : 15,
232 ),
233 1024 => array(
234 'slidesPerView' => $slides_to_show,
235 'slidesPerGroup' => $slides_to_scroll,
236 'spaceBetween' => $slide_spacing,
237 ),
238 );
239 }
240
241 // Slider direction
242 $direction = isset( $attrs['sliderDirection'] ) ? $attrs['sliderDirection'] : 'horizontal';
243 if ( 'vertical' === $direction ) {
244 $config['direction'] = 'vertical';
245 }
246
247 return apply_filters( 'sliderberg_swiper_config', $config, $attrs );
248 }
249
250 /**
251 * Render the slider block on the frontend.
252 *
253 * @param array $attributes Block attributes.
254 * @param string $content Inner block content (rendered slides).
255 * @return string HTML output.
256 */
257 function render_sliderberg_slider_block( $attributes, $content ) {
258 $attributes = apply_filters( 'sliderberg_slider_attributes', $attributes );
259
260 // No layout picked yet — a genuinely fresh, never-picked chooser block
261 // has no inner slides at all, so nothing to render. (Legacy content
262 // saved before `mode` existed always has real slide content and is
263 // handled inside sliderberg_resolve_mode().)
264 if ( '' === sliderberg_resolve_mode( $attributes, $content ) ) {
265 return '';
266 }
267
268 sliderberg_enqueue_view_assets();
269
270 $hide_nav = ! empty( $attributes['hideNavigation'] );
271 $hide_dots = ! empty( $attributes['hideDots'] );
272 $transition_effect = isset( $attributes['transitionEffect'] ) ? sanitize_html_class( $attributes['transitionEffect'] ) : 'slide';
273
274 // Navigation style attributes
275 $nav_color = isset( $attributes['navigationColor'] ) ? esc_attr( $attributes['navigationColor'] ) : '#ffffff';
276 $nav_bg_color = isset( $attributes['navigationBgColor'] ) ? esc_attr( $attributes['navigationBgColor'] ) : 'rgba(0, 0, 0, 0.5)';
277 $nav_opacity = isset( $attributes['navigationOpacity'] ) ? floatval( $attributes['navigationOpacity'] ) : 1;
278 $dot_color = isset( $attributes['dotColor'] ) ? esc_attr( $attributes['dotColor'] ) : 'rgba(0, 0, 0, 0.4)';
279 $dot_active = isset( $attributes['dotActiveColor'] ) ? esc_attr( $attributes['dotActiveColor'] ) : '#000000';
280 $min_height = isset( $attributes['minHeight'] ) ? absint( $attributes['minHeight'] ) : 400;
281
282 // Navigation shape and size
283 $nav_shape = isset( $attributes['navigationShape'] ) ? sanitize_html_class( $attributes['navigationShape'] ) : 'circle';
284 $nav_size = isset( $attributes['navigationSize'] ) ? sanitize_html_class( $attributes['navigationSize'] ) : 'medium';
285 $nav_size_value = isset( $attributes['navigationSizeValue']['all'] ) ? $attributes['navigationSizeValue']['all'] : '';
286 $easing = isset( $attributes['transitionEasing'] ) ? esc_attr( $attributes['transitionEasing'] ) : 'ease';
287
288 // Navigation position attributes
289 $nav_horizontal = isset( $attributes['navHorizontal'] ) ? sanitize_html_class( $attributes['navHorizontal'] ) : 'space-between';
290 $nav_vertical = isset( $attributes['navVertical'] ) ? sanitize_html_class( $attributes['navVertical'] ) : 'center';
291 $dots_h = isset( $attributes['dotsHorizontal'] ) ? sanitize_html_class( $attributes['dotsHorizontal'] ) : 'center';
292 $dots_v = isset( $attributes['dotsVertical'] ) ? sanitize_html_class( $attributes['dotsVertical'] ) : 'bottom';
293 $nav_outside = ! empty( $attributes['navOutside'] );
294 $nav_free = ! empty( $attributes['navFreePosition'] );
295 $prev_free_x = isset( $attributes['prevFreeX'] ) ? absint( $attributes['prevFreeX'] ) : 3;
296 $prev_free_y = isset( $attributes['prevFreeY'] ) ? absint( $attributes['prevFreeY'] ) : 50;
297 $next_free_x = isset( $attributes['nextFreeX'] ) ? absint( $attributes['nextFreeX'] ) : 97;
298 $next_free_y = isset( $attributes['nextFreeY'] ) ? absint( $attributes['nextFreeY'] ) : 50;
299 $dots_free_x = isset( $attributes['dotsFreeX'] ) ? absint( $attributes['dotsFreeX'] ) : 50;
300 $dots_free_y = isset( $attributes['dotsFreeY'] ) ? absint( $attributes['dotsFreeY'] ) : 90;
301
302 // Width handling
303 $width_preset = isset( $attributes['widthPreset'] ) ? $attributes['widthPreset'] : 'default';
304 $width_style = '';
305 if ( 'custom' === $width_preset ) {
306 $custom_width = isset( $attributes['customWidth'] ) ? floatval( $attributes['customWidth'] ) : 0;
307 $width_unit = isset( $attributes['widthUnit'] ) ? esc_attr( $attributes['widthUnit'] ) : 'px';
308 if ( $custom_width > 0 ) {
309 $width_style = sprintf( 'max-width:%s%s;margin-left:auto;margin-right:auto;', $custom_width, $width_unit );
310 }
311 }
312
313 // CSS custom properties for theming
314 $css_vars = sprintf(
315 '--swiper-navigation-color:%s;--swiper-navigation-background-color:%s;--sliderberg-nav-opacity:%s;--swiper-pagination-color:%s;--swiper-pagination-bullet-inactive-color:%s;--swiper-pagination-bullet-inactive-opacity:1;--sliderberg-min-height:%dpx;--sliderberg-easing:%s;',
316 $nav_color,
317 $nav_bg_color,
318 $nav_opacity,
319 $dot_active,
320 $dot_color,
321 $min_height,
322 $easing
323 );
324
325 // Custom navigation size CSS variable
326 if ( ! empty( $nav_size_value ) ) {
327 // Convert WP preset format "var:preset|spacing|XX" to CSS var
328 if ( 0 === strpos( $nav_size_value, 'var:' ) ) {
329 $parts = explode( '|', $nav_size_value );
330 $preset_slug = end( $parts );
331 $nav_size_css = sprintf( 'var(--wp--preset--spacing--%s)', sanitize_html_class( $preset_slug ) );
332 } else {
333 $nav_size_css = esc_attr( $nav_size_value );
334 }
335 $css_vars .= sprintf( '--sliderberg-nav-btn-size:%s;', $nav_size_css );
336 }
337
338 // Free position CSS variables (per-element)
339 if ( $nav_free ) {
340 $css_vars .= sprintf(
341 '--sliderberg-prev-free-x:%d%%;--sliderberg-prev-free-y:%d%%;--sliderberg-next-free-x:%d%%;--sliderberg-next-free-y:%d%%;--sliderberg-dots-free-x:%d%%;--sliderberg-dots-free-y:%d%%;',
342 $prev_free_x, $prev_free_y,
343 $next_free_x, $next_free_y,
344 $dots_free_x, $dots_free_y
345 );
346 }
347
348 $css_vars = apply_filters( 'sliderberg_slider_css_vars', $css_vars, $attributes );
349
350 $inline_style = $css_vars . $width_style;
351
352 // Block wrapper classes
353 $size_class = ! empty( $nav_size_value ) ? 'sliderberg-nav-custom-size' : 'sliderberg-nav-' . $nav_size;
354 $extra_classes = 'sliderberg-nav-' . $nav_shape . ' ' . $size_class . ' sliderberg-effect-' . $transition_effect;
355
356 // Vertical direction class
357 $direction = isset( $attributes['sliderDirection'] ) ? sanitize_html_class( $attributes['sliderDirection'] ) : 'horizontal';
358 if ( 'vertical' === $direction ) {
359 $extra_classes .= ' sliderberg-vertical';
360 }
361
362 if ( 'vertical' !== $direction ) {
363 if ( $nav_free ) {
364 $extra_classes .= ' sliderberg-nav-free';
365 } else {
366 $extra_classes .= ' sliderberg-nav-h-' . $nav_horizontal . ' sliderberg-nav-v-' . $nav_vertical;
367 $extra_classes .= ' sliderberg-dots-h-' . $dots_h . ' sliderberg-dots-v-' . $dots_v;
368 if ( $nav_outside ) {
369 $extra_classes .= ' sliderberg-nav-outside';
370 }
371 }
372 }
373
374 $extra_classes = apply_filters( 'sliderberg_slider_classes', $extra_classes, $attributes );
375
376 $wrapper_attributes = get_block_wrapper_attributes( array(
377 'class' => $extra_classes,
378 'style' => $inline_style,
379 ) );
380
381 // Build Swiper config JSON
382 $swiper_config = sliderberg_build_swiper_config( $attributes, $content );
383 $swiper_json = wp_json_encode( $swiper_config );
384
385 // Navigation buttons
386 $nav_html = '';
387 if ( ! $hide_nav ) {
388 $nav_html = '<button class="swiper-button-prev" aria-label="' . esc_attr__( 'Previous slide', 'sliderberg' ) . '"></button>'
389 . '<button class="swiper-button-next" aria-label="' . esc_attr__( 'Next slide', 'sliderberg' ) . '"></button>';
390 }
391
392 // Pagination
393 $pagination_html = '';
394 if ( ! $hide_dots ) {
395 $pagination_html = '<div class="swiper-pagination"></div>';
396 }
397
398 return sprintf(
399 '<div %1$s>'
400 . '<div class="sliderberg-swiper-wrap">'
401 . '<div class="swiper sliderberg-swiper" data-swiper-data=\'%2$s\'>'
402 . '<div class="swiper-wrapper">%3$s</div>'
403 . '</div>'
404 . '%4$s'
405 . '%5$s'
406 . '</div>'
407 . '</div>',
408 $wrapper_attributes,
409 esc_attr( $swiper_json ),
410 $content,
411 $nav_html,
412 $pagination_html
413 );
414 }
415