View file File name : nested-carousel.php Content :<?php namespace ElementorPro\Modules\NestedCarousel\Widgets; use Elementor\Controls_Manager; use Elementor\Icons_Manager; use Elementor\Modules\NestedElements\Base\Widget_Nested_Base; use Elementor\Modules\NestedElements\Controls\Control_Nested_Repeater; use Elementor\Repeater; use ElementorPro\Plugin; use ElementorPro\Base\Base_Widget_Trait; use ElementorPro\Base\Base_Carousel_Trait; use Elementor\Group_Control_Background; use Elementor\Group_Control_Border; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Nested_Carousel extends Widget_Nested_Base { use Base_Widget_Trait; use Base_Carousel_Trait; public function get_name() { return 'nested-carousel'; } public function get_title() { return esc_html__( 'Carousel', 'elementor-pro' ); } public function get_icon() { return 'eicon-nested-carousel'; } public function get_keywords() { return [ 'Carousel', 'Slides', 'Nested', 'Media', 'Gallery', 'Image' ]; } protected function get_default_children_elements() { return [ [ 'elType' => 'container', 'settings' => [ '_title' => __( 'Slide #1', 'elementor-pro' ), ], ], [ 'elType' => 'container', 'settings' => [ '_title' => __( 'Slide #2', 'elementor-pro' ), ], ], [ 'elType' => 'container', 'settings' => [ '_title' => __( 'Slide #3', 'elementor-pro' ), ], ], ]; } protected function get_default_repeater_title_setting_key() { return 'slide_title'; } protected function get_default_children_title() { return esc_html__( 'Slide #%d', 'elementor-pro' ); } protected function get_default_children_placeholder_selector() { return '.swiper-wrapper'; } protected function get_html_wrapper_class() { return 'elementor-widget-n-carousel'; } protected function register_controls() { $low_specificity_slider_container_selector = ':where( {{WRAPPER}} .swiper-slide ) > .e-con'; $this->start_controls_section( 'section_slides', [ 'label' => esc_html__( 'Slides', 'elementor-pro' ), ] ); $repeater = new Repeater(); $repeater->add_control( 'slide_title', [ 'label' => esc_html__( 'Title', 'elementor-pro' ), 'type' => Controls_Manager::TEXT, 'default' => esc_html__( 'Slide Title', 'elementor-pro' ), 'placeholder' => esc_html__( 'Slide Title', 'elementor-pro' ), 'dynamic' => [ 'active' => true, ], 'label_block' => true, ] ); $this->add_control( 'carousel_items', [ 'label' => esc_html__( 'Carousel Items', 'elementor-pro' ), 'type' => Control_Nested_Repeater::CONTROL_TYPE, 'fields' => $repeater->get_controls(), 'default' => [ [ 'slide_title' => esc_html__( 'Slide #1', 'elementor-pro' ), ], [ 'slide_title' => esc_html__( 'Slide #2', 'elementor-pro' ), ], [ 'slide_title' => esc_html__( 'Slide #3', 'elementor-pro' ), ], ], 'frontend_available' => true, 'title_field' => '{{{ slide_title }}}', ] ); $this->add_carousel_layout_controls( [ 'css_prefix' => 'e-n-carousel-', 'slides_to_show_custom_settings' => [ 'separator' => 'before', 'tablet_default' => '2', 'mobile_default' => '1', 'frontend_available' => true, 'render_type' => 'template', 'selectors' => [ '{{WRAPPER}}' => '--e-n-carousel-swiper-slides-to-display: {{VALUE}}', ], ], 'slides_to_scroll_custom_settings' => [], 'equal_height_custom_settings' => [ 'selectors' => [ '{{WRAPPER}}' => '--e-n-carousel-slide-height: auto; --e-n-carousel-slide-container-height: 100%;', ], ], 'slides_on_display' => 8, ] ); $this->end_controls_section(); $this->add_carousel_settings_controls( [ 'css_prefix' => 'e-n-carousel-', 'autoplay_custom_settings' => [ 'description' => esc_html__( 'The Autoplay is inactive while editing. Preview your page to see it in action.', 'elementor-pro' ), ], 'infinite_custom_settings' => [ 'description' => esc_html__( 'Infinite scroll is inactive while editing. Preview your page to see it in action.', 'elementor-pro' ), ], 'offset_sides_custom_settings' => [ 'description' => esc_html__( 'Offset is inactive while editing. Preview your page to see it in action.', 'elementor-pro' ), ], ] ); $this->add_carousel_navigation_controls( [ 'css_prefix' => 'e-n-carousel-', ] ); $this->add_carousel_pagination_controls( [ 'css_prefix' => 'e-n-carousel-', ] ); $this->start_controls_section( 'section_slides_style', [ 'label' => esc_html__( 'Slides', 'elementor-pro' ), 'tab' => Controls_Manager::TAB_STYLE, ] ); $this->add_responsive_control( 'image_spacing_custom', [ 'label' => esc_html__( 'Gap between slides', 'elementor-pro' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', 'em', 'rem', 'custom' ], 'range' => [ 'px' => [ 'max' => 400, ], 'em' => [ 'max' => 40, ], 'rem' => [ 'max' => 40, ], ], 'default' => [ 'size' => 10, ], 'frontend_available' => true, 'render_type' => 'none', 'selectors' => [ '{{WRAPPER}}' => '--e-n-carousel-swiper-slides-gap: {{SIZE}}{{UNIT}}', ], ] ); $this->add_group_control( Group_Control_Background::get_type(), [ 'name' => 'content_background', 'types' => [ 'classic', 'gradient' ], 'exclude' => [ 'image' ], 'selector' => $low_specificity_slider_container_selector, 'fields_options' => [ 'color' => [ 'label' => esc_html__( 'Background Color', 'elementor-pro' ), ], ], ] ); $this->add_group_control( Group_Control_Border::get_type(), [ 'name' => 'content_border', 'selector' => $low_specificity_slider_container_selector, 'fields_options' => [ 'color' => [ 'label' => esc_html__( 'Border Color', 'elementor-pro' ), ], 'width' => [ 'label' => esc_html__( 'Border Width', 'elementor-pro' ), ], ], ] ); $this->add_responsive_control( 'border_radius', [ 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), 'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], 'selectors' => [ $low_specificity_slider_container_selector => '--border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', ], ] ); // Todo: Remove in version 3.21.0: https://elementor.atlassian.net/browse/ED-11888. // Remove together with support for physical properties inside the container widget. $logical_dimensions_inline_start = is_rtl() ? '{{RIGHT}}{{UNIT}}' : '{{LEFT}}{{UNIT}}'; $logical_dimensions_inline_end = is_rtl() ? '{{LEFT}}{{UNIT}}' : '{{RIGHT}}{{UNIT}}'; $this->add_responsive_control( 'content_padding', [ 'label' => esc_html__( 'Padding', 'elementor-pro' ), 'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ $low_specificity_slider_container_selector => '--padding-top: {{TOP}}{{UNIT}}; --padding-right: {{RIGHT}}{{UNIT}}; --padding-bottom: {{BOTTOM}}{{UNIT}}; --padding-left: {{LEFT}}{{UNIT}};', // Todo: Remove in version 3.21.0: https://elementor.atlassian.net/browse/ED-11888. // Remove together with support for physical properties inside the container widget. ':where( [data-core-v316-plus="true"] .elementor-element.elementor-widget-n-carousel .swiper-slide ) > .e-con' => "--padding-block-start: {{TOP}}{{UNIT}}; --padding-inline-end: $logical_dimensions_inline_end; --padding-block-end: {{BOTTOM}}{{UNIT}}; --padding-inline-start: $logical_dimensions_inline_start;", ], 'separator' => 'before', ] ); $this->end_controls_section(); $this->add_carousel_navigation_styling_controls( [ 'css_prefix' => 'e-n-carousel-', 'navigation_styling_custom_settings' => [ 'condition' => [ 'arrows' => 'yes', ], ], ] ); $this->add_carousel_pagination_style_controls( [ 'css_prefix' => 'e-n-carousel-', ] ); } protected function render() { $settings = $this->get_settings_for_display(); $this->num_of_carousel_items = count( $settings['carousel_items'] ?? [] ); $slides = $settings['carousel_items']; $swiper_wrapper_class = Plugin::elementor()->experiments->is_feature_active( 'e_swiper_latest' ) ? 'swiper' : 'swiper-container'; $direction = $settings['direction']; $has_autoplay_enabled = 'yes' === $settings['autoplay']; $outside_wrapper_classes = [ 'e-n-carousel', $swiper_wrapper_class ]; $this->add_render_attribute( [ 'carousel-outside-wrapper' => [ 'class' => $outside_wrapper_classes, ], 'carousel-inside-wrapper' => [ 'class' => 'swiper-wrapper', 'aria-live' => $has_autoplay_enabled ? 'off' : 'polite', ], ] ); if ( ! empty( $direction ) ) { $this->add_render_attribute( 'carousel-outside-wrapper', 'dir', $direction ); } ?> <div <?php $this->print_render_attribute_string( 'carousel-outside-wrapper' ); ?>> <div <?php $this->print_render_attribute_string( 'carousel-inside-wrapper' ); ?>> <?php foreach ( $slides as $index => $slide ) { $slide_count = $index + 1; $slide_setting_key = $this->get_repeater_setting_key( 'slide_wrapper', 'slide', $index ); $this->add_render_attribute( $slide_setting_key, [ 'class' => 'swiper-slide', 'data-slide' => $slide_count, 'role' => 'group', 'aria-roledescription' => 'slide', 'aria-label' => $slide_count . ' ' . esc_html__( 'of', 'elementor-pro' ) . ' ' . count( $slides ), ] ); ?> <div <?php $this->print_render_attribute_string( $slide_setting_key ); ?>> <?php $this->print_child( $index ); ?> </div> <?php } ?> </div> </div> <?php $this->render_carousel_footer( $settings ); } protected function get_initial_config(): array { if ( Plugin::elementor()->experiments->is_feature_active( 'e_nested_atomic_repeaters' ) ) { return array_merge( parent::get_initial_config(), [ 'support_improved_repeaters' => true, 'target_container' => [ '.e-n-carousel > .swiper-wrapper' ], 'node' => 'div', ] ); } return parent::get_initial_config(); } protected function get_default_children_container_placeholder_selector() { return '.swiper-slide'; } protected function content_template_single_repeater_item() { ?> <# const elementUid = view.getIDInt().toString().substr( 0, 3 ), numOfSlides = view.collection.length + 1; const slideCount = numOfSlides, slideUid = elementUid + slideCount, slideWrapperKey = slideUid; const slideWrapperKeyItem = { 'class': 'swiper-slide', 'data-slide': slideCount, 'role': 'group', 'aria-roledescription': 'slide', 'aria-label': slideCount + ' <?php echo esc_html__( 'of', 'elementor-pro' ); ?> ' + numOfSlides, }; view.addRenderAttribute( 'single-slide', slideWrapperKeyItem, null, true ); #> <div {{{ view.getRenderAttributeString( 'single-slide' ) }}}></div> <?php } protected function content_template() { ?> <# if ( settings['carousel_items'] ) { const elementUid = view.getIDInt().toString().substr( 0, 3 ), carouselOutsideWrapperKey = 'carousel-' + elementUid, carouselInsideWrapperKey = 'carousel-inside-' + elementUid, swiperWrapperClass = elementorFrontend.config.swiperClass, hasAutoplayEnabled = 'yes' === settings['autoplay'], outsideWrapperClasses = ['e-n-carousel',swiperWrapperClass] shouldRenderPaginationAndArrows = 1 < settings['carousel_items'].length; view.addRenderAttribute( carouselOutsideWrapperKey, { 'class': outsideWrapperClasses, } ); view.addRenderAttribute( carouselInsideWrapperKey, { 'class': 'swiper-wrapper', 'aria-live': hasAutoplayEnabled ? 'off' : 'polite', } ); if ( !! settings['direction'] ) { view.addRenderAttribute( carouselOutsideWrapperKey, 'dir', settings['direction'] ); } #> <div {{{ view.getRenderAttributeString( carouselOutsideWrapperKey ) }}}> <div {{{ view.getRenderAttributeString( carouselInsideWrapperKey ) }}}> <# _.each( settings['carousel_items'], function( slide, index ) { const slideCount = index + 1, slideUid = elementUid + slideCount, slideWrapperKey = slideUid; view.addRenderAttribute( slideWrapperKey, { 'class': 'swiper-slide', 'data-slide': slideCount, 'role': 'group', 'aria-roledescription': 'slide', 'aria-label': slideCount + ' <?php echo esc_html__( 'of', 'elementor-pro' ); ?> ' + settings['carousel_items'].length, } ); #> <div {{{ view.getRenderAttributeString( slideWrapperKey ) }}}></div> <# } ); #> </div> </div> <# if ( 'yes' === settings['arrows'] && shouldRenderPaginationAndArrows ) { #> <?php $this->content_template_navigation_arrows(); ?> <# } #> <# if ( settings['pagination'] && shouldRenderPaginationAndArrows ) { #> <div class="swiper-pagination"></div> <# } #> <# } #> <?php } protected function content_template_navigation_arrows() { ?> <div class="elementor-swiper-button elementor-swiper-button-prev" role="button" tabindex="0"> <# const iconSettingsPrevious = settings['navigation_previous_icon'], iconPreviousHTML = elementor.helpers.renderIcon( view, iconSettingsPrevious, { 'aria-hidden': true }, 'i' , 'object' ); if ( 'eicon-chevron-left' === iconSettingsPrevious['value'] ) { #> <?php Icons_Manager::render_icon( [ 'library' => 'eicons', 'value' => 'eicon-chevron-left', ] ); ?> <# } else if ( !! iconSettingsPrevious['value'] ) { #> {{{ iconPreviousHTML.value }}} <# } #> </div> <div class="elementor-swiper-button elementor-swiper-button-next" role="button" tabindex="0"> <# const iconSettingsNext = settings['navigation_next_icon'], iconNextHTML = elementor.helpers.renderIcon( view, iconSettingsNext, { 'aria-hidden': true }, 'i' , 'object' ); if ( 'eicon-chevron-right' === iconSettingsNext['value'] ) { #> <?php Icons_Manager::render_icon( [ 'library' => 'eicons', 'value' => 'eicon-chevron-right', ] ); ?> <# } else if ( !! iconSettingsNext['value'] ) { #> {{{ iconNextHTML.value }}} <# } #> </div> <?php } }