PluginProbe
UiCore Elements – Free widgets and templates for Elementor / 1.3.17
UiCore Elements – Free widgets and templates for Elementor v1.3.17
1.3.17 1.3.16 trunk 1.0.0 1.0.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 1.0.16 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 All 41 releases
← All changes | includes/widgets/custom-carousel.php +207 -172 1.2.11.3.17 View file →
@@ -1,6 +1,8 @@
1 1 <?php
2 +
2 3 namespace UiCoreElements;
4 +
3 5 use Elementor\Plugin;
4 6 use Elementor\Controls_Manager;
5 7 use Elementor\Repeater;
6 8 use Elementor\Modules\NestedElements\Controls\Control_Nested_Repeater;
@@ -16,9 +18,10 @@
16 18 * @author Lucas Marini Falbo <lucas@uicore.co>
17 19 * @since 1.0.7
18 20 */
19 21
20 -class CustomCarousel extends UiCoreNestedWidget {
22 +class CustomCarousel extends UiCoreNestedWidget
23 +{
21 24
22 25 use Carousel_Trait;
23 26 use Animation_Trait;
24 27 use Item_Style_Component;
@@ -54,43 +57,49 @@
54 57 public function get_scripts()
55 58 {
56 59 return $this->TRAIT_get_carousel_scripts(false);
57 60 }
58 - // TODO: remove or set as false, after 3.30, when the full deprecation of widget innet wrapper is ready
59 - public function has_widget_inner_wrapper(): bool {
60 - return true;
61 - }
61 + public function has_widget_inner_wrapper(): bool
62 + {
63 + // TODO: remove after Optmized Markup experiment is merged to the core
64 + return ! \Elementor\Plugin::$instance->experiments->is_feature_active('e_optimized_markup');
65 + }
62 66
63 67 // Nested required functions
64 - protected function carousel_content_container( int $index ) {
65 - return [
66 - 'elType' => 'container',
67 - 'settings' => [
68 + protected function carousel_content_container(int $index)
69 + {
70 + return [
71 + 'elType' => 'container',
72 + 'settings' => [
68 73 /* translators: %s: Item number */
69 - '_title' => sprintf( esc_html__( 'Item #%s', 'uicore-elements' ), $index ),
70 - 'content_width' => 'full',
74 + '_title' => sprintf(esc_html__('Item #%s', 'uicore-elements'), $index),
75 + 'content_width' => 'full',
71 76 'flex_justify_content' => 'center',
72 77 'flex_align_items' => 'center',
73 - ],
74 - ];
75 - }
76 - protected function get_default_children_elements() {
77 - return [
78 - $this->carousel_content_container( 1 ),
79 - $this->carousel_content_container( 2 ),
80 - $this->carousel_content_container( 3 ),
81 - ];
82 - }
83 - protected function get_default_repeater_title_setting_key() {
84 - return 'carousel_item_title';
85 - }
86 - protected function get_default_children_title() {
78 + ],
79 + ];
80 + }
81 + protected function get_default_children_elements()
82 + {
83 + return [
84 + $this->carousel_content_container(1),
85 + $this->carousel_content_container(2),
86 + $this->carousel_content_container(3),
87 + ];
88 + }
89 + protected function get_default_repeater_title_setting_key()
90 + {
91 + return 'carousel_item_title';
92 + }
93 + protected function get_default_children_title()
94 + {
87 95 /* translators: %d: Item number */
88 - return esc_html__( 'Item #%d', 'uicore-elements' );
89 - }
90 - protected function get_default_children_placeholder_selector() {
91 - return '.swiper-wrapper';
92 - }
96 + return esc_html__('Item #%d', 'uicore-elements');
97 + }
98 + protected function get_default_children_placeholder_selector()
99 + {
100 + return '.swiper-wrapper';
101 + }
93 102
94 103 /**
95 104 * We've set a bool variable to this function because Custom Slider extends this widget and requires one extra control.
96 105 *
@@ -98,12 +107,12 @@
98 107 */
99 108 protected function register_controls(bool $is_slider = false)
100 109 {
101 110
102 - if( !Plugin::$instance->experiments->is_feature_active('nested-elements') ){
103 - $this->nesting_fallback('controls');
104 - return;
105 - }
111 + if (!Plugin::$instance->experiments->is_feature_active('nested-elements')) {
112 + $this->nesting_fallback('controls');
113 + return;
114 + }
106 115
107 116 $this->start_controls_section(
108 117 'section_content',
109 118 [
@@ -111,34 +120,34 @@
111 120 'tab' => Controls_Manager::TAB_CONTENT,
112 121 ]
113 122 );
114 123
115 - $repeater = new Repeater();
124 + $repeater = new Repeater();
116 125
117 - $repeater->add_control(
118 - 'carousel_item_title',
119 - [
120 - 'label' => __('Title', 'uicore-elements'),
121 - 'type' => Controls_Manager::TEXT,
122 - 'render_type' => 'template',
123 - 'dynamic' => [
124 - 'active' => true,
125 - ],
126 - ]
127 - );
126 + $repeater->add_control(
127 + 'carousel_item_title',
128 + [
129 + 'label' => __('Title', 'uicore-elements'),
130 + 'type' => Controls_Manager::TEXT,
131 + 'render_type' => 'template',
132 + 'dynamic' => [
133 + 'active' => true,
134 + ],
135 + ]
136 + );
128 137
129 - $this->add_control(
130 - 'carousel_items',
131 - [
132 - 'type' => Control_Nested_Repeater::CONTROL_TYPE,
133 - 'fields' => $repeater->get_controls(),
134 - 'default' => [
135 - [ 'carousel_item_title' => __( 'Item #1', 'uicore-elements' ) ],
136 - [ 'carousel_item_title' => __( 'Item #2', 'uicore-elements' ) ],
137 - [ 'carousel_item_title' => __( 'Item #3', 'uicore-elements' ) ],
138 - ]
138 + $this->add_control(
139 + 'carousel_items',
140 + [
141 + 'type' => Control_Nested_Repeater::CONTROL_TYPE,
142 + 'fields' => $repeater->get_controls(),
143 + 'default' => [
144 + ['carousel_item_title' => __('Item #1', 'uicore-elements')],
145 + ['carousel_item_title' => __('Item #2', 'uicore-elements')],
146 + ['carousel_item_title' => __('Item #3', 'uicore-elements')],
139 147 ]
140 - );
148 + ]
149 + );
141 150
142 151 $this->end_controls_section();
143 152
144 153 // Additional Carousel Controls
@@ -149,20 +158,20 @@
149 158 'tab' => Controls_Manager::TAB_CONTENT,
150 159 ]
151 160 );
152 161
153 - $this->TRAIT_register_carousel_additional_controls($is_slider); // Carousel Additionals
162 + $this->TRAIT_register_carousel_additional_controls($is_slider); // Carousel Additionals
154 163
155 164 $this->end_controls_section();
156 165
157 166 $this->start_controls_section(
158 - 'section_carousel_settings',
159 - [
160 - 'label' => __('Carousel Settings', 'uicore-elements'),
161 - ]
162 - );
167 + 'section_carousel_settings',
168 + [
169 + 'label' => __('Carousel Settings', 'uicore-elements'),
170 + ]
171 + );
163 172
164 - $this->TRAIT_register_carousel_settings_controls(); // Carousel settings
173 + $this->TRAIT_register_carousel_settings_controls(); // Carousel settings
165 174
166 175 $this->end_controls_section();
167 176
168 177 $this->TRAIT_register_navigation_controls(); // Navigation settings
@@ -167,16 +176,16 @@
167 176
168 177 $this->TRAIT_register_navigation_controls(); // Navigation settings
169 178
170 179 $this->start_controls_section(
171 - 'section_style_review_items',
172 - [
173 - 'label' => esc_html__( 'Items', 'uicore-elements' ),
174 - 'tab' => Controls_Manager::TAB_STYLE,
175 - ]
176 - );
180 + 'section_style_review_items',
181 + [
182 + 'label' => esc_html__('Items', 'uicore-elements'),
183 + 'tab' => Controls_Manager::TAB_STYLE,
184 + ]
185 + );
177 186
178 - $this->TRAIT_register_all_item_style_controls();
187 + $this->TRAIT_register_all_item_style_controls();
179 188
180 189 $this->end_controls_section();
181 190
182 191 $this->TRAIT_register_navigation_style_controls(); // Carousel Navigation Styles
@@ -196,146 +205,172 @@
196 205 // Adds description to loop
197 206 $this->update_control('loop', [
198 207 'description' => esc_html__('Loop preview is disabled here on the editor, due to compatibility issues. But you can test on the front-end, since it works outside the editor.', 'uicore-elements'),
199 208 ]);
200 -
201 209 }
202 210
203 211 public function render()
204 212 {
205 - if( Plugin::$instance->experiments->is_feature_active('nested-elements') == false ){
206 - $this->nesting_fallback();
207 - return;
208 - }
213 + if (Plugin::$instance->experiments->is_feature_active('nested-elements') == false) {
214 + $this->nesting_fallback();
215 + return;
216 + }
209 217
210 218 $items = $this->get_settings_for_display('carousel_items');
211 219 $carousel_items = '';
212 220
213 221 $total_slides = count($items);
214 - $should_duplicate = $this->TRAIT_should_duplicate_slides($total_slides);
215 - $duplicated_slides = [];
222 + $this->TRAIT_render_center_loop_warning($total_slides);
216 223
217 - foreach ( $items as $index => $item ) {
224 + foreach ($items as $index => $item) {
218 225 ob_start();
219 226 $this->render_item($index);
220 227 $current_slide = ob_get_clean();
221 228
222 - if($should_duplicate){
223 - $duplicated_slides[$index] = $current_slide;
224 - }
225 -
226 229 $carousel_items .= $current_slide;
227 230 }
228 231
229 - // Most recent swiper versions requires, if loop, at least one extra slide compared to visible slides
230 - if($should_duplicate) {
231 - $diff = $this->TRAIT_get_duplication_diff($total_slides);
232 - for($i = 0; $i <= $diff; $i++){
233 - $carousel_items .= $duplicated_slides[$i];
234 - }
235 - }
232 +?>
233 + <div class="ui-e-carousel ui-e-nested swiper">
236 234
237 - ?>
238 - <div class="ui-e-carousel swiper">
235 + <div class='swiper-wrapper'>
236 + <?php echo $carousel_items; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
237 + ?>
238 + </div>
239 239
240 - <div class='swiper-wrapper'>
241 - <?php echo $carousel_items; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
242 - </div>
240 + </div>
243 241
244 - </div>
242 + <?php $this->TRAIT_render_carousel_navigations($total_slides); ?>
243 + <?php
244 + }
245 245
246 - <?php $this->TRAIT_render_carousel_navigations(); ?>
247 - <?php
246 + public function render_item($index)
247 + {
248 + ?>
249 + <div class="ui-e-wrp swiper-slide" data-hash="<?php echo esc_html($index); ?>">
250 + <?php $this->print_child($index); ?>
251 + </div>
252 + <?php
248 253 }
249 254
250 - public function render_item( $index ) {
251 - ?>
252 - <div class="ui-e-wrp swiper-slide" data-hash="<?php echo esc_html($index);?>">
253 - <?php $this->print_child( $index ); ?>
254 - </div>
255 - <?php
256 - }
255 + public function print_child($index)
256 + {
257 + $children = $this->get_children();
258 + $child_ids = [];
257 259
258 - public function print_child( $index ) {
259 - $children = $this->get_children();
260 - $child_ids = [];
260 + if (empty($children) || ! isset($children[$index])) {
261 + return;
262 + }
261 263
262 - if( empty( $children )){
263 - return;
264 - }
264 + foreach ($children as $child) {
265 + $child_ids[] = $child->get_id();
266 + }
265 267
266 - foreach ( $children as $child ) {
267 - $child_ids[] = $child->get_id();
268 - }
268 + add_filter('elementor/frontend/container/should_render', function ($should_render, $container) use ($child_ids) {
269 + return $this->add_child_attributes($should_render, $container, $child_ids);
270 + }, 10, 3);
269 271
270 - // Add the `ui-e-item` class directly on the item container
271 - $add_attribute_to_container = function ( $should_render, $container ) use ( $child_ids ) {
272 - if ( in_array( $container->get_id(), $child_ids ) ) {
273 - $container->add_render_attribute( '_wrapper', [
274 - 'class' => 'ui-e-item',
275 - ] );
276 - }
272 + $children[$index]->print_element();
277 273
278 - return $should_render;
279 - };
274 + remove_filter('elementor/frontend/container/should_render', [$this, 'add_child_attributes']);
275 + }
280 276
281 - add_filter( 'elementor/frontend/container/should_render', $add_attribute_to_container, 10, 3 );
282 - $children[ $index ]->print_element();
283 - remove_filter( 'elementor/frontend/container/should_render', $add_attribute_to_container );
284 - }
277 + /**
278 + * Adds attributes to child elements containers.
279 + */
280 + protected function add_child_attributes($should_render, $container, $child_ids, $classnames = ['ui-e-item'])
281 + {
285 282
286 - protected function get_initial_config(): array {
287 - if ( Plugin::$instance->experiments->is_feature_active( 'e_nested_atomic_repeaters' ) ) {
288 - return array_merge( parent::get_initial_config(), [
289 - 'support_improved_repeaters' => true,
290 - 'node' => 'button',
291 - ] );
292 - }
283 + // custom cases may be added here
293 284
294 - return parent::get_initial_config();
295 - }
285 + if (in_array($container->get_id(), $child_ids)) {
286 + $container->add_render_attribute('_wrapper', [
287 + 'class' => $classnames,
288 + ]);
289 + }
296 290
297 - protected function content_template() {
291 + return $should_render;
292 + }
298 293
299 - if( Plugin::$instance->experiments->is_feature_active('nested-elements') == false ){
300 - return;
301 - }
294 + protected function get_initial_config(): array
295 + {
296 + if (Plugin::$instance->experiments->is_feature_active('e_nested_atomic_repeaters')) {
297 + return array_merge(parent::get_initial_config(), [
298 + 'support_improved_repeaters' => true,
299 + 'node' => 'button',
300 + ]);
301 + }
302 302
303 - ?>
304 - <#
305 - var carouselItems = settings.carousel_items,
306 - hideNavigation = settings.animation_style.includes('marquee'),
307 - navigationDots = settings.navigation.includes('dots') && !hideNavigation,
308 - navigationArrows = settings.navigation.includes('arrows') && !hideNavigation,
309 - navigationFraction = settings.navigation.includes('fraction') && !hideNavigation;
303 + return parent::get_initial_config();
304 + }
310 305
311 - var prev = elementor.helpers.renderIcon( view, settings.previous_arrow, { 'aria-hidden': true }, 'i' , 'object' ),
312 - next = elementor.helpers.renderIcon( view, settings.next_arrow, { 'aria-hidden': true }, 'i' , 'object' );
306 + protected function content_template()
307 + {
308 +
309 + if (Plugin::$instance->experiments->is_feature_active('nested-elements') == false) {
310 + return;
311 + }
312 +
313 + ?>
314 + <#
315 + var carouselItems=settings.carousel_items || [],
316 + hideNavigation=settings.animation_style && settings.animation_style.includes('marquee'),
317 + navigationDots=settings.navigation && settings.navigation.includes('dots') && !hideNavigation,
318 + navigationArrows=settings.navigation && settings.navigation.includes('arrows') && !hideNavigation,
319 + navigationFraction=settings.navigation && settings.navigation.includes('fraction') && !hideNavigation,
320 + totalSlides=carouselItems.length,
321 + perView=parseInt(settings.slides_per_view, 10) || 0,
322 + needsCenterLoopWarning=settings.center_slides &&
323 + settings.loop &&
324 + perView % 2===0 &&
325 + totalSlides < (perView + 2),
326 + missingSlides=(perView + 2) - totalSlides;
327 +
328 + var prev=elementor.helpers.renderIcon(view, settings.previous_arrow, { 'aria-hidden' : true }, 'i' , 'object' ),
329 + next=elementor.helpers.renderIcon(view, settings.next_arrow, { 'aria-hidden' : true }, 'i' , 'object' );
313 330 #>
314 331
315 - <div class="ui-e-carousel swiper {{ elementorFrontend.config.swiperClass }}">
316 - <div class='swiper-wrapper'> </div>
317 - </div>
332 + <# if ( needsCenterLoopWarning ) { #>
333 + <div style="background: red; mix-blend-mode: difference; padding: 15px; border-radius: 5px;">
334 + <span style="color: white; font-size: 1.2em; font-weight: bold;">
335 + <?php echo esc_html__('Warning', 'uicore-elements'); ?>
336 + </span>
318 337
319 - <# if ( navigationDots ) { #>
320 - <div class="swiper-pagination ui-e-dots ui-e-carousel-dots"></div>
338 + <p style="color: white; margin-bottom: 0px;">
339 + <?php echo esc_html__('You enabled centered slides, loop, and have an even number of slides per view', 'uicore-elements'); ?>
340 + ({{{ perView }}} <?php echo esc_html__('slides per view', 'uicore-elements'); ?>).
341 + <?php echo esc_html__('For this configuration to work properly, you need to have', 'uicore-elements'); ?>
342 + {{{ missingSlides }}}
343 + <?php echo esc_html__('more slide(s) or disable one of the mentioned features.', 'uicore-elements'); ?>
344 + </p>
345 + </div>
321 346 <# } #>
322 - <# if ( navigationArrows ) { #>
323 - <div class="ui-e-button ui-e-carousel-button ui-e-previous" role="button" aria-label="Previous slide">
324 - {{{ prev.value }}}
347 +
348 + <div class="ui-e-carousel ui-e-nested swiper {{ elementorFrontend.config.swiperClass }}">
349 + <div class='swiper-wrapper'> </div>
325 350 </div>
326 - <div class="ui-e-button ui-e-carousel-button ui-e-next" role="button" aria-label="Next slide">
327 - {{{ next.value }}}
328 - </div>
329 - <# } #>
330 - <# if ( navigationFraction ) { #>
331 - <div class="ui-e-fraction ui-e-carousel-fraction">
332 - <span class="ui-e-current"></span>
333 - /
334 - <span class="ui-e-total"></span>
335 - </div>
336 - <# } #>
337 - <?php
338 - }
339 351
340 -}
341 -\Elementor\Plugin::instance()->widgets_manager->register(new CustomCarousel());
352 + <# if ( navigationDots ) { #>
353 + <div class="swiper-pagination ui-e-dots ui-e-carousel-dots"></div>
354 + <# } #>
355 +
356 + <# if ( navigationArrows ) { #>
357 + <div class="ui-e-button ui-e-carousel-button ui-e-previous" role="button" aria-label="Previous slide">
358 + {{{ prev.value }}}
359 + </div>
360 + <div class="ui-e-button ui-e-carousel-button ui-e-next" role="button" aria-label="Next slide">
361 + {{{ next.value }}}
362 + </div>
363 + <# } #>
364 +
365 + <# if ( navigationFraction ) { #>
366 + <div class="ui-e-fraction ui-e-carousel-fraction">
367 + <span class="ui-e-current"></span>
368 + /
369 + <span class="ui-e-total"></span>
370 + </div>
371 + <# } #>
372 + <?php
373 + }
374 + }
375 +
376 + \Elementor\Plugin::instance()->widgets_manager->register(new CustomCarousel());