PluginProbe
Elementor Website Builder – more than just a page builder / 3.16.0-dev1
Elementor Website Builder – more than just a page builder v3.16.0-dev1
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.16.0-dev1, at includes/widgets/image-carousel.php

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