PluginProbe
Elementor Website Builder – more than just a page builder / 3.22.3
Elementor Website Builder – more than just a page builder v3.22.3
4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 4.1.0-beta1 4.1.0-dev1 4.0.7 All 451 releases
elementor / includes / widgets / image-carousel.php

image-carousel.php in Elementor Website Builder – more than just a page builder 3.22.3, at includes/widgets/image-carousel.php

1,073 lines 26.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Elementor;
3
4 if ( ! defined( 'ABSPATH' ) ) {
5 exit; // Exit if accessed directly.
6 }
7
8 use Elementor\Core\Kits\Documents\Tabs\Global_Colors;
9
10 /**
11 * Elementor image carousel widget.
12 *
13 * Elementor widget that displays a set of images in a rotating carousel or
14 * slider.
15 *
16 * @since 1.0.0
17 */
18 class Widget_Image_Carousel extends Widget_Base {
19
20 /**
21 * Get widget name.
22 *
23 * Retrieve image carousel widget name.
24 *
25 * @since 1.0.0
26 * @access public
27 *
28 * @return string Widget name.
29 */
30 public function get_name() {
31 return 'image-carousel';
32 }
33
34 /**
35 * Get widget title.
36 *
37 * Retrieve image carousel widget title.
38 *
39 * @since 1.0.0
40 * @access public
41 *
42 * @return string Widget title.
43 */
44 public function get_title() {
45 return esc_html__( 'Image Carousel', 'elementor' );
46 }
47
48 /**
49 * Get widget icon.
50 *
51 * Retrieve image carousel widget icon.
52 *
53 * @since 1.0.0
54 * @access public
55 *
56 * @return string Widget icon.
57 */
58 public function get_icon() {
59 return 'eicon-slider-push';
60 }
61
62 /**
63 * Get widget keywords.
64 *
65 * Retrieve the list of keywords the widget belongs to.
66 *
67 * @since 2.1.0
68 * @access public
69 *
70 * @return array Widget keywords.
71 */
72 public function get_keywords() {
73 return [ 'image', 'photo', 'visual', 'carousel', 'slider' ];
74 }
75
76 protected function is_dynamic_content(): bool {
77 return false;
78 }
79
80 /**
81 * Get widget upsale data.
82 *
83 * Retrieve the widget promotion data.
84 *
85 * @since 3.18.0
86 * @access protected
87 *
88 * @return array Widget promotion data.
89 */
90 protected function get_upsale_data() {
91 return [
92 'condition' => ! Utils::has_pro(),
93 'image' => esc_url( ELEMENTOR_ASSETS_URL . 'images/go-pro.svg' ),
94 'image_alt' => esc_attr__( 'Upgrade', 'elementor' ),
95 'description' => esc_html__( 'Gain complete freedom to design every slide with Elementor"s Pro Carousel.', 'elementor' ),
96 'upgrade_url' => esc_url( 'https://go.elementor.com/go-pro-image-carousel-widget/' ),
97 'upgrade_text' => esc_html__( 'Upgrade Now', 'elementor' ),
98 ];
99 }
100
101 /**
102 * Register image carousel widget controls.
103 *
104 * Adds different input fields to allow the user to change and customize the widget settings.
105 *
106 * @since 3.1.0
107 * @access protected
108 */
109 protected function register_controls() {
110 $this->start_controls_section(
111 'section_image_carousel',
112 [
113 'label' => esc_html__( 'Image Carousel', 'elementor' ),
114 ]
115 );
116
117 $this->add_control(
118 'carousel',
119 [
120 'label' => esc_html__( 'Add Images', 'elementor' ),
121 'type' => Controls_Manager::GALLERY,
122 'default' => [],
123 'show_label' => false,
124 'dynamic' => [
125 'active' => true,
126 ],
127 ]
128 );
129
130 $this->add_group_control(
131 Group_Control_Image_Size::get_type(),
132 [
133 'name' => 'thumbnail', // Usage: `{name}_size` and `{name}_custom_dimension`, in this case `thumbnail_size` and `thumbnail_custom_dimension`.
134 ]
135 );
136
137 $slides_to_show = range( 1, 10 );
138 $slides_to_show = array_combine( $slides_to_show, $slides_to_show );
139
140 $this->add_responsive_control(
141 'slides_to_show',
142 [
143 'label' => esc_html__( 'Slides to Show', 'elementor' ),
144 'type' => Controls_Manager::SELECT,
145 'options' => [
146 '' => esc_html__( 'Default', 'elementor' ),
147 ] + $slides_to_show,
148 'frontend_available' => true,
149 'render_type' => 'template',
150 'selectors' => [
151 '{{WRAPPER}}' => '--e-image-carousel-slides-to-show: {{VALUE}}',
152 ],
153 'content_classes' => 'elementor-control-field-select-small',
154 ]
155 );
156
157 $this->add_responsive_control(
158 'slides_to_scroll',
159 [
160 'label' => esc_html__( 'Slides to Scroll', 'elementor' ),
161 'type' => Controls_Manager::SELECT,
162 'description' => esc_html__( 'Set how many slides are scrolled per swipe.', 'elementor' ),
163 'options' => [
164 '' => esc_html__( 'Default', 'elementor' ),
165 ] + $slides_to_show,
166 'condition' => [
167 'slides_to_show!' => '1',
168 ],
169 'frontend_available' => true,
170 'content_classes' => 'elementor-control-field-select-small',
171 ]
172 );
173
174 $this->add_control(
175 'image_stretch',
176 [
177 'label' => esc_html__( 'Image Stretch', 'elementor' ),
178 'type' => Controls_Manager::SELECT,
179 'default' => 'no',
180 'options' => [
181 'no' => esc_html__( 'No', 'elementor' ),
182 'yes' => esc_html__( 'Yes', 'elementor' ),
183 ],
184 ]
185 );
186
187 $this->add_control(
188 'navigation',
189 [
190 'label' => esc_html__( 'Navigation', 'elementor' ),
191 'type' => Controls_Manager::SELECT,
192 'default' => 'both',
193 'options' => [
194 'both' => esc_html__( 'Arrows and Dots', 'elementor' ),
195 'arrows' => esc_html__( 'Arrows', 'elementor' ),
196 'dots' => esc_html__( 'Dots', 'elementor' ),
197 'none' => esc_html__( 'None', 'elementor' ),
198 ],
199 'frontend_available' => true,
200 ]
201 );
202
203 $this->add_control(
204 'navigation_previous_icon',
205 [
206 'label' => esc_html__( 'Previous Arrow Icon', 'elementor' ),
207 'type' => Controls_Manager::ICONS,
208 'fa4compatibility' => 'icon',
209 'skin' => 'inline',
210 'label_block' => false,
211 'skin_settings' => [
212 'inline' => [
213 'none' => [
214 'label' => 'Default',
215 'icon' => 'eicon-chevron-left',
216 ],
217 'icon' => [
218 'icon' => 'eicon-star',
219 ],
220 ],
221 ],
222 'recommended' => [
223 'fa-regular' => [
224 'arrow-alt-circle-left',
225 'caret-square-left',
226 ],
227 'fa-solid' => [
228 'angle-double-left',
229 'angle-left',
230 'arrow-alt-circle-left',
231 'arrow-circle-left',
232 'arrow-left',
233 'caret-left',
234 'caret-square-left',
235 'chevron-circle-left',
236 'chevron-left',
237 'long-arrow-alt-left',
238 ],
239 ],
240 'conditions' => [
241 'relation' => 'or',
242 'terms' => [
243 [
244 'name' => 'navigation',
245 'operator' => '=',
246 'value' => 'both',
247 ],
248 [
249 'name' => 'navigation',
250 'operator' => '=',
251 'value' => 'arrows',
252 ],
253 ],
254 ],
255 ]
256 );
257
258 $this->add_control(
259 'navigation_next_icon',
260 [
261 'label' => esc_html__( 'Next Arrow Icon', 'elementor' ),
262 'type' => Controls_Manager::ICONS,
263 'fa4compatibility' => 'icon',
264 'skin' => 'inline',
265 'label_block' => false,
266 'skin_settings' => [
267 'inline' => [
268 'none' => [
269 'label' => 'Default',
270 'icon' => 'eicon-chevron-right',
271 ],
272 'icon' => [
273 'icon' => 'eicon-star',
274 ],
275 ],
276 ],
277 'recommended' => [
278 'fa-regular' => [
279 'arrow-alt-circle-right',
280 'caret-square-right',
281 ],
282 'fa-solid' => [
283 'angle-double-right',
284 'angle-right',
285 'arrow-alt-circle-right',
286 'arrow-circle-right',
287 'arrow-right',
288 'caret-right',
289 'caret-square-right',
290 'chevron-circle-right',
291 'chevron-right',
292 'long-arrow-alt-right',
293 ],
294 ],
295 'conditions' => [
296 'relation' => 'or',
297 'terms' => [
298 [
299 'name' => 'navigation',
300 'operator' => '=',
301 'value' => 'both',
302 ],
303 [
304 'name' => 'navigation',
305 'operator' => '=',
306 'value' => 'arrows',
307 ],
308 ],
309 ],
310 ]
311 );
312
313 $this->add_control(
314 'link_to',
315 [
316 'label' => esc_html__( 'Link', 'elementor' ),
317 'type' => Controls_Manager::SELECT,
318 'default' => 'none',
319 'options' => [
320 'none' => esc_html__( 'None', 'elementor' ),
321 'file' => esc_html__( 'Media File', 'elementor' ),
322 'custom' => esc_html__( 'Custom URL', 'elementor' ),
323 ],
324 ]
325 );
326
327 $this->add_control(
328 'link',
329 [
330 'label' => esc_html__( 'Link', 'elementor' ),
331 'type' => Controls_Manager::URL,
332 'condition' => [
333 'link_to' => 'custom',
334 ],
335 'show_label' => false,
336 'dynamic' => [
337 'active' => true,
338 ],
339 ]
340 );
341
342 $this->add_control(
343 'open_lightbox',
344 [
345 'label' => esc_html__( 'Lightbox', 'elementor' ),
346 'type' => Controls_Manager::SELECT,
347 'description' => sprintf(
348 /* translators: 1: Link open tag, 2: Link close tag. */
349 esc_html__( 'Manage your site’s lightbox settings in the %1$sLightbox panel%2$s.', 'elementor' ),
350 '<a href="javascript: $e.run( \'panel/global/open\' ).then( () => $e.route( \'panel/global/settings-lightbox\' ) )">',
351 '</a>'
352 ),
353 'default' => 'default',
354 'options' => [
355 'default' => esc_html__( 'Default', 'elementor' ),
356 'yes' => esc_html__( 'Yes', 'elementor' ),
357 'no' => esc_html__( 'No', 'elementor' ),
358 ],
359 'condition' => [
360 'link_to' => 'file',
361 ],
362 ]
363 );
364
365 $this->add_control(
366 'caption_type',
367 [
368 'label' => esc_html__( 'Caption', 'elementor' ),
369 'type' => Controls_Manager::SELECT,
370 'default' => '',
371 'options' => [
372 '' => esc_html__( 'None', 'elementor' ),
373 'title' => esc_html__( 'Title', 'elementor' ),
374 'caption' => esc_html__( 'Caption', 'elementor' ),
375 'description' => esc_html__( 'Description', 'elementor' ),
376 ],
377 ]
378 );
379
380 $this->end_controls_section();
381
382 $this->start_controls_section(
383 'section_additional_options',
384 [
385 'label' => esc_html__( 'Additional Options', 'elementor' ),
386 ]
387 );
388
389 $this->add_control(
390 'lazyload',
391 [
392 'label' => esc_html__( 'Lazyload', 'elementor' ),
393 'type' => Controls_Manager::SWITCHER,
394 'frontend_available' => true,
395 ]
396 );
397
398 $this->add_control(
399 'autoplay',
400 [
401 'label' => esc_html__( 'Autoplay', 'elementor' ),
402 'type' => Controls_Manager::SWITCHER,
403 'label_on' => esc_html__( 'Yes', 'elementor' ),
404 'label_off' => esc_html__( 'No', 'elementor' ),
405 'return_value' => 'yes',
406 'default' => 'yes',
407 'frontend_available' => true,
408 ]
409 );
410
411 $this->add_control(
412 'pause_on_hover',
413 [
414 'label' => esc_html__( 'Pause on Hover', 'elementor' ),
415 'type' => Controls_Manager::SWITCHER,
416 'label_on' => esc_html__( 'Yes', 'elementor' ),
417 'label_off' => esc_html__( 'No', 'elementor' ),
418 'return_value' => 'yes',
419 'default' => 'yes',
420 'condition' => [
421 'autoplay' => 'yes',
422 ],
423 'render_type' => 'none',
424 'frontend_available' => true,
425 ]
426 );
427
428 $this->add_control(
429 'pause_on_interaction',
430 [
431 'label' => esc_html__( 'Pause on Interaction', 'elementor' ),
432 'type' => Controls_Manager::SWITCHER,
433 'label_on' => esc_html__( 'Yes', 'elementor' ),
434 'label_off' => esc_html__( 'No', 'elementor' ),
435 'return_value' => 'yes',
436 'default' => 'yes',
437 'condition' => [
438 'autoplay' => 'yes',
439 ],
440 'frontend_available' => true,
441 ]
442 );
443
444 $this->add_control(
445 'autoplay_speed',
446 [
447 'label' => esc_html__( 'Autoplay Speed', 'elementor' ),
448 'type' => Controls_Manager::NUMBER,
449 'default' => 5000,
450 'condition' => [
451 'autoplay' => 'yes',
452 ],
453 'render_type' => 'none',
454 'frontend_available' => true,
455 ]
456 );
457
458 // Loop requires a re-render so no 'render_type = none'
459 $this->add_control(
460 'infinite',
461 [
462 'label' => esc_html__( 'Infinite Loop', 'elementor' ),
463 'type' => Controls_Manager::SWITCHER,
464 'label_on' => esc_html__( 'Yes', 'elementor' ),
465 'label_off' => esc_html__( 'No', 'elementor' ),
466 'return_value' => 'yes',
467 'default' => 'yes',
468 'frontend_available' => true,
469 ]
470 );
471
472 $this->add_control(
473 'effect',
474 [
475 'label' => esc_html__( 'Effect', 'elementor' ),
476 'type' => Controls_Manager::SELECT,
477 'default' => 'slide',
478 'options' => [
479 'slide' => esc_html__( 'Slide', 'elementor' ),
480 'fade' => esc_html__( 'Fade', 'elementor' ),
481 ],
482 'condition' => [
483 'slides_to_show' => '1',
484 ],
485 'frontend_available' => true,
486 ]
487 );
488
489 $this->add_control(
490 'speed',
491 [
492 'label' => esc_html__( 'Animation Speed', 'elementor' ),
493 'type' => Controls_Manager::NUMBER,
494 'default' => 500,
495 'render_type' => 'none',
496 'frontend_available' => true,
497 ]
498 );
499
500 $this->add_control(
501 'direction',
502 [
503 'label' => esc_html__( 'Direction', 'elementor' ),
504 'type' => Controls_Manager::SELECT,
505 'default' => 'ltr',
506 'options' => [
507 'ltr' => esc_html__( 'Left', 'elementor' ),
508 'rtl' => esc_html__( 'Right', 'elementor' ),
509 ],
510 ]
511 );
512
513 $this->end_controls_section();
514
515 $this->start_controls_section(
516 'section_style_navigation',
517 [
518 'label' => esc_html__( 'Navigation', 'elementor' ),
519 'tab' => Controls_Manager::TAB_STYLE,
520 'condition' => [
521 'navigation' => [ 'arrows', 'dots', 'both' ],
522 ],
523 ]
524 );
525
526 $this->add_control(
527 'heading_style_arrows',
528 [
529 'label' => esc_html__( 'Arrows', 'elementor' ),
530 'type' => Controls_Manager::HEADING,
531 'separator' => 'before',
532 'condition' => [
533 'navigation' => [ 'arrows', 'both' ],
534 ],
535 ]
536 );
537
538 $this->add_control(
539 'arrows_position',
540 [
541 'label' => esc_html__( 'Position', 'elementor' ),
542 'type' => Controls_Manager::SELECT,
543 'default' => 'inside',
544 'options' => [
545 'inside' => esc_html__( 'Inside', 'elementor' ),
546 'outside' => esc_html__( 'Outside', 'elementor' ),
547 ],
548 'prefix_class' => 'elementor-arrows-position-',
549 'condition' => [
550 'navigation' => [ 'arrows', 'both' ],
551 ],
552 ]
553 );
554
555 $this->add_responsive_control(
556 'arrows_size',
557 [
558 'label' => esc_html__( 'Size', 'elementor' ),
559 'type' => Controls_Manager::SLIDER,
560 'size_units' => [ 'px', 'em', 'rem', 'custom' ],
561 'range' => [
562 'px' => [
563 'max' => 100,
564 ],
565 ],
566 'selectors' => [
567 '{{WRAPPER}} .elementor-swiper-button.elementor-swiper-button-prev, {{WRAPPER}} .elementor-swiper-button.elementor-swiper-button-next' => 'font-size: {{SIZE}}{{UNIT}};',
568 ],
569 'condition' => [
570 'navigation' => [ 'arrows', 'both' ],
571 ],
572 ]
573 );
574
575 $this->add_control(
576 'arrows_color',
577 [
578 'label' => esc_html__( 'Color', 'elementor' ),
579 'type' => Controls_Manager::COLOR,
580 'selectors' => [
581 '{{WRAPPER}} .elementor-swiper-button.elementor-swiper-button-prev, {{WRAPPER}} .elementor-swiper-button.elementor-swiper-button-next' => 'color: {{VALUE}};',
582 '{{WRAPPER}} .elementor-swiper-button.elementor-swiper-button-prev svg, {{WRAPPER}} .elementor-swiper-button.elementor-swiper-button-next svg' => 'fill: {{VALUE}};',
583 ],
584 'condition' => [
585 'navigation' => [ 'arrows', 'both' ],
586 ],
587 ]
588 );
589
590 $this->add_control(
591 'heading_style_dots',
592 [
593 'label' => esc_html__( 'Pagination', 'elementor' ),
594 'type' => Controls_Manager::HEADING,
595 'separator' => 'before',
596 'condition' => [
597 'navigation' => [ 'dots', 'both' ],
598 ],
599 ]
600 );
601
602 $this->add_control(
603 'dots_position',
604 [
605 'label' => esc_html__( 'Position', 'elementor' ),
606 'type' => Controls_Manager::SELECT,
607 'default' => 'outside',
608 'options' => [
609 'outside' => esc_html__( 'Outside', 'elementor' ),
610 'inside' => esc_html__( 'Inside', 'elementor' ),
611 ],
612 'prefix_class' => 'elementor-pagination-position-',
613 'condition' => [
614 'navigation' => [ 'dots', 'both' ],
615 ],
616 ]
617 );
618
619 $this->add_responsive_control(
620 'dots_size',
621 [
622 'label' => esc_html__( 'Size', 'elementor' ),
623 'type' => Controls_Manager::SLIDER,
624 'size_units' => [ 'px', 'em', 'rem', 'custom' ],
625 'range' => [
626 'px' => [
627 'max' => 20,
628 ],
629 ],
630 'selectors' => [
631 '{{WRAPPER}} .swiper-pagination-bullet' => 'width: {{SIZE}}{{UNIT}}; height: {{SIZE}}{{UNIT}};',
632 ],
633 'condition' => [
634 'navigation' => [ 'dots', 'both' ],
635 ],
636 ]
637 );
638
639 $this->add_control(
640 'dots_inactive_color',
641 [
642 'label' => esc_html__( 'Color', 'elementor' ),
643 'type' => Controls_Manager::COLOR,
644 'selectors' => [
645 // The opacity property will override the default inactive dot color which is opacity 0.2.
646 '{{WRAPPER}} .swiper-pagination-bullet:not(.swiper-pagination-bullet-active)' => 'background: {{VALUE}}; opacity: 1',
647 ],
648 'condition' => [
649 'navigation' => [ 'dots', 'both' ],
650 ],
651 ]
652 );
653
654 $this->add_control(
655 'dots_color',
656 [
657 'label' => esc_html__( 'Active Color', 'elementor' ),
658 'type' => Controls_Manager::COLOR,
659 'selectors' => [
660 '{{WRAPPER}} .swiper-pagination-bullet' => 'background: {{VALUE}};',
661 ],
662 'condition' => [
663 'navigation' => [ 'dots', 'both' ],
664 ],
665 ]
666 );
667
668 $this->end_controls_section();
669
670 $this->start_controls_section(
671 'section_style_image',
672 [
673 'label' => esc_html__( 'Image', 'elementor' ),
674 'tab' => Controls_Manager::TAB_STYLE,
675 ]
676 );
677
678 $this->add_responsive_control(
679 'gallery_vertical_align',
680 [
681 'label' => esc_html__( 'Vertical Align', 'elementor' ),
682 'type' => Controls_Manager::CHOOSE,
683 'options' => [
684 'flex-start' => [
685 'title' => esc_html__( 'Start', 'elementor' ),
686 'icon' => 'eicon-v-align-top',
687 ],
688 'center' => [
689 'title' => esc_html__( 'Center', 'elementor' ),
690 'icon' => 'eicon-v-align-middle',
691 ],
692 'flex-end' => [
693 'title' => esc_html__( 'End', 'elementor' ),
694 'icon' => 'eicon-v-align-bottom',
695 ],
696 ],
697 'condition' => [
698 'slides_to_show!' => '1',
699 ],
700 'selectors' => [
701 '{{WRAPPER}} .swiper-wrapper' => 'display: flex; align-items: {{VALUE}};',
702 ],
703 ]
704 );
705
706 $this->add_control(
707 'image_spacing',
708 [
709 'label' => esc_html__( 'Spacing', 'elementor' ),
710 'type' => Controls_Manager::SELECT,
711 'options' => [
712 '' => esc_html__( 'Default', 'elementor' ),
713 'custom' => esc_html__( 'Custom', 'elementor' ),
714 ],
715 'default' => '',
716 'condition' => [
717 'slides_to_show!' => '1',
718 ],
719 ]
720 );
721
722 $this->add_responsive_control(
723 'image_spacing_custom',
724 [
725 'label' => esc_html__( 'Image Spacing', 'elementor' ),
726 'type' => Controls_Manager::SLIDER,
727 'size_units' => [ 'px', 'em', 'rem', 'custom' ],
728 'range' => [
729 'px' => [
730 'max' => 100,
731 ],
732 ],
733 'default' => [
734 'size' => 20,
735 ],
736 'condition' => [
737 'image_spacing' => 'custom',
738 'slides_to_show!' => '1',
739 ],
740 'frontend_available' => true,
741 'render_type' => 'none',
742 'separator' => 'after',
743 ]
744 );
745
746 $this->add_group_control(
747 Group_Control_Border::get_type(),
748 [
749 'name' => 'image_border',
750 'selector' => '{{WRAPPER}} .elementor-image-carousel-wrapper .elementor-image-carousel .swiper-slide-image',
751 ]
752 );
753
754 $this->add_responsive_control(
755 'image_border_radius',
756 [
757 'label' => esc_html__( 'Border Radius', 'elementor' ),
758 'type' => Controls_Manager::DIMENSIONS,
759 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ],
760 'selectors' => [
761 '{{WRAPPER}} .elementor-image-carousel-wrapper .elementor-image-carousel .swiper-slide-image' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
762 ],
763 ]
764 );
765
766 $this->end_controls_section();
767
768 $this->start_controls_section(
769 'section_caption',
770 [
771 'label' => esc_html__( 'Caption', 'elementor' ),
772 'tab' => Controls_Manager::TAB_STYLE,
773 'condition' => [
774 'caption_type!' => '',
775 ],
776 ]
777 );
778
779 $this->add_responsive_control(
780 'caption_align',
781 [
782 'label' => esc_html__( 'Alignment', 'elementor' ),
783 'type' => Controls_Manager::CHOOSE,
784 'options' => [
785 'left' => [
786 'title' => esc_html__( 'Left', 'elementor' ),
787 'icon' => 'eicon-text-align-left',
788 ],
789 'center' => [
790 'title' => esc_html__( 'Center', 'elementor' ),
791 'icon' => 'eicon-text-align-center',
792 ],
793 'right' => [
794 'title' => esc_html__( 'Right', 'elementor' ),
795 'icon' => 'eicon-text-align-right',
796 ],
797 'justify' => [
798 'title' => esc_html__( 'Justified', 'elementor' ),
799 'icon' => 'eicon-text-align-justify',
800 ],
801 ],
802 'default' => 'center',
803 'selectors' => [
804 '{{WRAPPER}} .elementor-image-carousel-caption' => 'text-align: {{VALUE}};',
805 ],
806 ]
807 );
808
809 $this->add_control(
810 'caption_text_color',
811 [
812 'label' => esc_html__( 'Text Color', 'elementor' ),
813 'type' => Controls_Manager::COLOR,
814 'default' => '',
815 'selectors' => [
816 '{{WRAPPER}} .elementor-image-carousel-caption' => 'color: {{VALUE}};',
817 ],
818 ]
819 );
820
821 $this->add_group_control(
822 Group_Control_Typography::get_type(),
823 [
824 'name' => 'caption_typography',
825 'global' => [
826 'default' => Global_Colors::COLOR_ACCENT,
827 ],
828 'selector' => '{{WRAPPER}} .elementor-image-carousel-caption',
829 ]
830 );
831
832 $this->add_group_control(
833 Group_Control_Text_Shadow::get_type(),
834 [
835 'name' => 'caption_shadow',
836 'selector' => '{{WRAPPER}} .elementor-image-carousel-caption',
837 ]
838 );
839
840 $this->add_responsive_control(
841 'caption_space',
842 [
843 'label' => esc_html__( 'Spacing', 'elementor' ),
844 'type' => Controls_Manager::SLIDER,
845 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ],
846 'selectors' => [
847 '{{WRAPPER}} .elementor-image-carousel-caption' => 'margin-block-start: {{SIZE}}{{UNIT}};',
848 ],
849 ]
850 );
851
852 $this->end_controls_section();
853 }
854
855 /**
856 * Render image carousel widget output on the frontend.
857 *
858 * Written in PHP and used to generate the final HTML.
859 *
860 * @since 1.0.0
861 * @access protected
862 */
863 protected function render() {
864 $settings = $this->get_settings_for_display();
865
866 $lazyload = 'yes' === $settings['lazyload'];
867
868 if ( empty( $settings['carousel'] ) ) {
869 return;
870 }
871
872 $slides = [];
873
874 foreach ( $settings['carousel'] as $index => $attachment ) {
875 $image_url = Group_Control_Image_Size::get_attachment_image_src( $attachment['id'], 'thumbnail', $settings );
876
877 if ( ! $image_url && isset( $attachment['url'] ) ) {
878 $image_url = $attachment['url'];
879 }
880
881 if ( $lazyload ) {
882 $image_html = '<img class="swiper-slide-image swiper-lazy" data-src="' . esc_attr( $image_url ) . '" alt="' . esc_attr( Control_Media::get_image_alt( $attachment ) ) . '" />';
883 } else {
884 $image_html = '<img class="swiper-slide-image" src="' . esc_attr( $image_url ) . '" alt="' . esc_attr( Control_Media::get_image_alt( $attachment ) ) . '" />';
885 }
886
887 $link_tag = '';
888
889 $link = $this->get_link_url( $attachment, $settings );
890
891 if ( $link ) {
892 $link_key = 'link_' . $index;
893
894 $this->add_lightbox_data_attributes( $link_key, $attachment['id'], $settings['open_lightbox'], $this->get_id() );
895
896 if ( Plugin::$instance->editor->is_edit_mode() ) {
897 $this->add_render_attribute( $link_key, [
898 'class' => 'elementor-clickable',
899 ] );
900 }
901
902 $this->add_link_attributes( $link_key, $link );
903
904 $link_tag = '<a ' . $this->get_render_attribute_string( $link_key ) . '>';
905 }
906
907 $image_caption = $this->get_image_caption( $attachment );
908
909 $slide_count = $index + 1;
910 $slide_setting_key = 'swiper_slide_' . $index;
911
912 $this->add_render_attribute( $slide_setting_key, [
913 'class' => 'swiper-slide',
914 'role' => 'group',
915 'aria-roledescription' => 'slide',
916 'aria-label' => sprintf(
917 /* translators: 1: Slide count, 2: Total slides count. */
918 esc_html__( '%1$s of %2$s', 'elementor' ),
919 $slide_count,
920 count( $settings['carousel'] )
921 ),
922 ] );
923
924 $slide_html = '<div ' . $this->get_render_attribute_string( $slide_setting_key ) . '>' . $link_tag . '<figure class="swiper-slide-inner">' . $image_html;
925
926 if ( $lazyload ) {
927 $slide_html .= '<div class="swiper-lazy-preloader"></div>';
928 }
929
930 if ( ! empty( $image_caption ) ) {
931 $slide_html .= '<figcaption class="elementor-image-carousel-caption">' . wp_kses_post( $image_caption ) . '</figcaption>';
932 }
933
934 $slide_html .= '</figure>';
935
936 if ( $link ) {
937 $slide_html .= '</a>';
938 }
939
940 $slide_html .= '</div>';
941
942 $slides[] = $slide_html;
943
944 }
945
946 if ( empty( $slides ) ) {
947 return;
948 }
949
950 $swiper_class = Plugin::$instance->experiments->is_feature_active( 'e_swiper_latest' ) ? 'swiper' : 'swiper-container';
951 $has_autoplay_enabled = 'yes' === $this->get_settings_for_display( 'autoplay' );
952
953 $this->add_render_attribute( [
954 'carousel' => [
955 'class' => 'elementor-image-carousel swiper-wrapper',
956 'aria-live' => $has_autoplay_enabled ? 'off' : 'polite',
957 ],
958 'carousel-wrapper' => [
959 'class' => 'elementor-image-carousel-wrapper ' . $swiper_class,
960 'dir' => $settings['direction'],
961 ],
962 ] );
963
964 $show_dots = ( in_array( $settings['navigation'], [ 'dots', 'both' ] ) );
965 $show_arrows = ( in_array( $settings['navigation'], [ 'arrows', 'both' ] ) );
966
967 if ( 'yes' === $settings['image_stretch'] ) {
968 $this->add_render_attribute( 'carousel', 'class', 'swiper-image-stretch' );
969 }
970
971 $slides_count = count( $settings['carousel'] );
972 ?>
973 <div <?php $this->print_render_attribute_string( 'carousel-wrapper' ); ?>>
974 <div <?php $this->print_render_attribute_string( 'carousel' ); ?>>
975 <?php // PHPCS - $slides contains the slides content, all the relevent content is escaped above. ?>
976 <?php echo implode( '', $slides ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
977 </div>
978 <?php if ( 1 < $slides_count ) : ?>
979 <?php if ( $show_arrows ) : ?>
980 <div class="elementor-swiper-button elementor-swiper-button-prev" role="button" tabindex="0">
981 <?php $this->render_swiper_button( 'previous' ); ?>
982 </div>
983 <div class="elementor-swiper-button elementor-swiper-button-next" role="button" tabindex="0">
984 <?php $this->render_swiper_button( 'next' ); ?>
985 </div>
986 <?php endif; ?>
987
988 <?php if ( $show_dots ) : ?>
989 <div class="swiper-pagination"></div>
990 <?php endif; ?>
991 <?php endif; ?>
992 </div>
993 <?php
994 }
995
996 /**
997 * Retrieve image carousel link URL.
998 *
999 * @since 1.0.0
1000 * @access private
1001 *
1002 * @param array $attachment
1003 * @param object $instance
1004 *
1005 * @return array|string|false An array/string containing the attachment URL, or false if no link.
1006 */
1007 private function get_link_url( $attachment, $instance ) {
1008 if ( 'none' === $instance['link_to'] ) {
1009 return false;
1010 }
1011
1012 if ( 'custom' === $instance['link_to'] ) {
1013 if ( empty( $instance['link']['url'] ) ) {
1014 return false;
1015 }
1016
1017 return $instance['link'];
1018 }
1019
1020 return [
1021 'url' => wp_get_attachment_url( $attachment['id'] ),
1022 ];
1023 }
1024
1025 /**
1026 * Retrieve image carousel caption.
1027 *
1028 * @since 1.2.0
1029 * @access private
1030 *
1031 * @param array $attachment
1032 *
1033 * @return string The caption of the image.
1034 */
1035 private function get_image_caption( $attachment ) {
1036 $caption_type = $this->get_settings_for_display( 'caption_type' );
1037
1038 if ( empty( $caption_type ) ) {
1039 return '';
1040 }
1041
1042 $attachment_post = get_post( $attachment['id'] );
1043
1044 if ( 'caption' === $caption_type ) {
1045 return $attachment_post->post_excerpt;
1046 }
1047
1048 if ( 'title' === $caption_type ) {
1049 return $attachment_post->post_title;
1050 }
1051
1052 if ( empty( $attachment_post->post_content ) ) {
1053 return '';
1054 }
1055
1056 return $attachment_post->post_content;
1057 }
1058
1059 private function render_swiper_button( $type ) {
1060 $direction = 'next' === $type ? 'right' : 'left';
1061 $icon_settings = $this->get_settings_for_display( 'navigation_' . $type . '_icon' );
1062
1063 if ( empty( $icon_settings['value'] ) ) {
1064 $icon_settings = [
1065 'library' => 'eicons',
1066 'value' => 'eicon-chevron-' . $direction,
1067 ];
1068 }
1069
1070 Icons_Manager::render_icon( $icon_settings, [ 'aria-hidden' => 'true' ] );
1071 }
1072 }
1073