PluginProbe
Elementor Website Builder – more than just a page builder / 3.18.2
Elementor Website Builder – more than just a page builder v3.18.2
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 / elements / container.php

container.php in Elementor Website Builder – more than just a page builder 3.18.2, at includes/elements/container.php

1,860 lines 45.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Elementor\Includes\Elements;
3
4 use Elementor\Controls_Manager;
5 use Elementor\Core\Breakpoints\Manager as Breakpoints_Manager;
6 use Elementor\Element_Base;
7 use Elementor\Embed;
8 use Elementor\Group_Control_Background;
9 use Elementor\Group_Control_Border;
10 use Elementor\Group_Control_Box_Shadow;
11 use Elementor\Group_Control_Css_Filter;
12 use Elementor\Group_Control_Flex_Container;
13 use Elementor\Group_Control_Flex_Item;
14 use Elementor\Group_Control_Grid_Container;
15 use Elementor\Plugin;
16 use Elementor\Shapes;
17 use Elementor\Utils;
18
19 if ( ! defined( 'ABSPATH' ) ) {
20 exit; // Exit if accessed directly.
21 }
22
23 class Container extends Element_Base {
24
25 /**
26 * @var \Elementor\Core\Kits\Documents\Kit
27 */
28 private $active_kit;
29 private $logical_dimensions_inline_start;
30 private $logical_dimensions_inline_end;
31
32 /**
33 * Container constructor.
34 *
35 * @param array $data
36 * @param array|null $args
37 *
38 * @return void
39 */
40 public function __construct( array $data = [], array $args = null ) {
41 parent::__construct( $data, $args );
42
43 $this->active_kit = Plugin::$instance->kits_manager->get_active_kit();
44 $this->logical_dimensions_inline_start = is_rtl() ? '{{RIGHT}}{{UNIT}}' : '{{LEFT}}{{UNIT}}';
45 $this->logical_dimensions_inline_end = is_rtl() ? '{{LEFT}}{{UNIT}}' : '{{RIGHT}}{{UNIT}}';
46 }
47
48 /**
49 * Get the element type.
50 *
51 * @return string
52 */
53 public static function get_type() {
54 return 'container';
55 }
56
57 /**
58 * Get the element name.
59 *
60 * @return string
61 */
62 public function get_name() {
63 return 'container';
64 }
65
66 /**
67 * Get the element display name.
68 *
69 * @return string
70 */
71 public function get_title() {
72 return esc_html__( 'Container', 'elementor' );
73 }
74
75 /**
76 * Get the element display icon.
77 *
78 * @return string
79 */
80 public function get_icon() {
81 return 'eicon-container';
82 }
83
84 public function get_keywords() {
85 $keywords = [ 'Container', 'Flex', 'Flexbox', 'Flexbox Container', 'Layout' ];
86
87 if ( Plugin::$instance->experiments->is_feature_active( 'container_grid' ) ) {
88 array_push( $keywords, 'Grid', 'Grid Container', 'CSS Grid' );
89 }
90
91 return $keywords;
92 }
93
94 public function get_panel_presets() {
95 return [
96 'container_grid' => [
97 'replacements' => [
98 'name' => 'container_grid',
99 'controls' => [
100 'container_type' => [ 'default' => 'grid' ],
101 ],
102 'title' => esc_html__( 'Grid', 'elementor' ),
103 'icon' => 'eicon-container-grid',
104 'custom' => [
105 'isPreset' => true,
106 'originalWidget' => $this->get_name(),
107 'presetWidget' => 'container_grid',
108 'preset_settings' => [
109 'container_type' => 'grid',
110 'presetTitle' => esc_html__( 'Grid', 'elementor' ),
111 'presetIcon' => 'eicon-container-grid',
112 ],
113 ],
114 ],
115 ],
116 ];
117 }
118
119 /**
120 * Override the render attributes to add a custom wrapper class.
121 *
122 * @return void
123 */
124 protected function add_render_attributes() {
125 parent::add_render_attributes();
126
127 $is_nested_class_name = $this->get_data( 'isInner' ) ? 'e-child' : 'e-parent';
128
129 $this->add_render_attribute( '_wrapper', [
130 'class' => [
131 'e-con',
132 $is_nested_class_name,
133 ],
134 ] );
135
136 if ( $this->get_data( 'isInner' ) ) {
137 return;
138 }
139
140 // Todo: Remove in version 3.21.0: https://elementor.atlassian.net/browse/ED-11884.
141 // Remove together with support for physical properties inside the Mega Menu & Nested Carousel widgets.
142 $this->add_render_attribute( '_wrapper', [
143 'data-core-v316-plus' => 'true',
144 ] );
145 }
146
147 /**
148 * Override the initial element config to display the Container in the panel.
149 *
150 * @return array
151 */
152 protected function get_initial_config() {
153 $config = parent::get_initial_config();
154
155 $config['controls'] = $this->get_controls();
156 $config['tabs_controls'] = $this->get_tabs_controls();
157 $config['show_in_panel'] = true;
158 $config['categories'] = [ 'layout' ];
159
160 return $config;
161 }
162
163 /**
164 * Render the element JS template.
165 *
166 * @return void
167 */
168 protected function content_template() {
169 ?>
170 <# if ( 'boxed' === settings.content_width ) { #>
171 <div class="e-con-inner">
172 <#
173 }
174 if ( settings.background_video_link ) {
175 let videoAttributes = 'autoplay muted playsinline';
176
177 if ( ! settings.background_play_once ) {
178 videoAttributes += ' loop';
179 }
180
181 view.addRenderAttribute( 'background-video-container', 'class', 'elementor-background-video-container' );
182
183 if ( ! settings.background_play_on_mobile ) {
184 view.addRenderAttribute( 'background-video-container', 'class', 'elementor-hidden-phone' );
185 }
186 #>
187 <div {{{ view.getRenderAttributeString( 'background-video-container' ) }}}>
188 <div class="elementor-background-video-embed"></div>
189 <video class="elementor-background-video-hosted elementor-html5-video" {{ videoAttributes }}></video>
190 </div>
191 <# } #>
192 <div class="elementor-shape elementor-shape-top"></div>
193 <div class="elementor-shape elementor-shape-bottom"></div>
194 <# if ( 'boxed' === settings.content_width ) { #>
195 </div>
196 <# } #>
197 <?php
198 }
199
200 /**
201 * Render the video background markup.
202 *
203 * @return void
204 */
205 protected function render_video_background() {
206 $settings = $this->get_settings_for_display();
207
208 if ( 'video' !== $settings['background_background'] ) {
209 return;
210 }
211
212 if ( ! $settings['background_video_link'] ) {
213 return;
214 }
215
216 $video_properties = Embed::get_video_properties( $settings['background_video_link'] );
217
218 $this->add_render_attribute( 'background-video-container', 'class', 'elementor-background-video-container' );
219
220 if ( ! $settings['background_play_on_mobile'] ) {
221 $this->add_render_attribute( 'background-video-container', 'class', 'elementor-hidden-phone' );
222 }
223
224 ?><div <?php $this->print_render_attribute_string( 'background-video-container' ); ?>>
225 <?php if ( $video_properties ) : ?>
226 <div class="elementor-background-video-embed"></div>
227 <?php
228 else :
229 $video_tag_attributes = 'autoplay muted playsinline';
230
231 if ( 'yes' !== $settings['background_play_once'] ) {
232 $video_tag_attributes .= ' loop';
233 }
234 ?>
235 <video class="elementor-background-video-hosted elementor-html5-video" <?php echo esc_attr( $video_tag_attributes ); ?>></video>
236 <?php endif; ?>
237 </div><?php
238 }
239
240 /**
241 * Render the Container's shape divider.
242 * TODO: Copied from `section.php`.
243 *
244 * Used to generate the shape dividers HTML.
245 *
246 * @param string $side - Shape divider side, used to set the shape key.
247 *
248 * @return void
249 */
250 protected function render_shape_divider( $side ) {
251 $settings = $this->get_active_settings();
252 $base_setting_key = "shape_divider_$side";
253 $negative = ! empty( $settings[ $base_setting_key . '_negative' ] );
254 $shape_path = Shapes::get_shape_path( $settings[ $base_setting_key ], $negative );
255
256 if ( ! is_file( $shape_path ) || ! is_readable( $shape_path ) ) {
257 return;
258 }
259 ?>
260 <div class="elementor-shape elementor-shape-<?php echo esc_attr( $side ); ?>" data-negative="<?php
261 Utils::print_unescaped_internal_string( $negative ? 'true' : 'false' );
262 ?>">
263 <?php
264 // PHPCS - The file content is being read from a strict file path structure.
265 echo Utils::file_get_contents( $shape_path ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
266 ?>
267 </div>
268 <?php
269 }
270
271 /**
272 * Print safe HTML tag for the element based on the element settings.
273 *
274 * @return void
275 */
276 protected function print_html_tag() {
277 $html_tag = $this->get_settings( 'html_tag' );
278
279 if ( empty( $html_tag ) ) {
280 $html_tag = 'div';
281 }
282
283 Utils::print_validated_html_tag( $html_tag );
284 }
285
286 /**
287 * Before rendering the container content. (Print the opening tag, etc.)
288 *
289 * @return void
290 */
291 public function before_render() {
292 $settings = $this->get_settings_for_display();
293 $link = $settings['link'];
294
295 if ( ! empty( $link['url'] ) ) {
296 $this->add_link_attributes( '_wrapper', $link );
297 }
298
299 ?><<?php $this->print_html_tag(); ?> <?php $this->print_render_attribute_string( '_wrapper' ); ?>>
300 <?php
301 if ( $this->is_boxed_container( $settings ) ) { ?>
302 <div class="e-con-inner">
303 <?php }
304
305 $this->render_video_background();
306
307 if ( ! empty( $settings['shape_divider_top'] ) ) {
308 $this->render_shape_divider( 'top' );
309 }
310
311 if ( ! empty( $settings['shape_divider_bottom'] ) ) {
312 $this->render_shape_divider( 'bottom' );
313 }
314 }
315
316 /**
317 * After rendering the Container content. (Print the closing tag, etc.)
318 *
319 * @return void
320 */
321 public function after_render() {
322 $settings = $this->get_settings_for_display();
323 if ( $this->is_boxed_container( $settings ) ) { ?>
324 </div>
325 <?php } ?>
326 </<?php $this->print_html_tag(); ?>>
327 <?php
328 }
329
330 protected function is_boxed_container( array $settings ) {
331 return ! empty( $settings['content_width'] ) && 'boxed' === $settings['content_width'];
332 }
333
334 /**
335 * Override the default child type to allow widgets & containers as children.
336 *
337 * @param array $element_data
338 *
339 * @return \Elementor\Element_Base|\Elementor\Widget_Base|null
340 */
341 protected function _get_default_child_type( array $element_data ) {
342 if ( 'container' === $element_data['elType'] ) {
343 return Plugin::$instance->elements_manager->get_element_types( 'container' );
344 }
345
346 return Plugin::$instance->widgets_manager->get_widget_types( $element_data['widgetType'] );
347 }
348
349 protected function get_flex_control_options( $is_container_grid_active ) {
350 $flex_control_options = [
351 'name' => 'flex',
352 'selector' => '{{WRAPPER}}',
353 'fields_options' => [
354 'gap' => [
355 'label' => esc_html__( 'Gaps', 'elementor' ),
356 'device_args' => [
357 Breakpoints_Manager::BREAKPOINT_KEY_DESKTOP => [
358 // Use the default gap from the kit as a placeholder.
359 'placeholder' => $this->active_kit->get_settings_for_display( 'space_between_widgets' ),
360 ],
361 ],
362 ],
363 ],
364 ];
365
366 if ( $is_container_grid_active ) {
367 $flex_control_options['condition'] = [
368 'container_type' => 'flex',
369 ];
370 }
371
372 return $flex_control_options;
373 }
374
375 protected function get_container_type_control_options( $is_container_grid_active ) {
376 if ( $is_container_grid_active ) {
377 return [
378 'label' => esc_html__( 'Container Layout', 'elementor' ),
379 'type' => Controls_Manager::SELECT,
380 'default' => 'flex',
381 'prefix_class' => 'e-',
382 'options' => [
383 'flex' => esc_html__( 'Flexbox', 'elementor' ),
384 'grid' => esc_html__( 'Grid', 'elementor' ),
385 ],
386 'selectors' => [
387 '{{WRAPPER}}' => '--display: {{VALUE}}',
388 ],
389 'separator' => 'after',
390 'frontend_available' => true,
391 ];
392 }
393
394 // TODO: This can be removed when the 'Container Grid Experiment' is merged.
395 return [
396 'label' => esc_html__( 'Container Layout', 'elementor' ),
397 'type' => Controls_Manager::HIDDEN,
398 'render_type' => 'none',
399 'default' => 'flex',
400 'prefix_class' => 'e-',
401 'selectors' => [
402 '{{WRAPPER}}' => '--display: {{VALUE}}',
403 ],
404 'separator' => 'after',
405 ];
406 }
407
408 /**
409 * Register the Container's layout controls.
410 *
411 * @return void
412 */
413 protected function register_container_layout_controls() {
414 $this->start_controls_section(
415 'section_layout_container',
416 [
417 'label' => esc_html__( 'Container', 'elementor' ),
418 'tab' => Controls_Manager::TAB_LAYOUT,
419 ]
420 );
421
422 $active_breakpoints = Plugin::$instance->breakpoints->get_active_breakpoints();
423
424 if ( array_key_exists( Breakpoints_Manager::BREAKPOINT_KEY_MOBILE_EXTRA, $active_breakpoints ) ) {
425 $min_affected_device = Breakpoints_Manager::BREAKPOINT_KEY_MOBILE_EXTRA;
426 } else {
427 $min_affected_device = Breakpoints_Manager::BREAKPOINT_KEY_TABLET;
428 }
429
430 $is_container_grid_active = Plugin::$instance->experiments->is_feature_active( 'container_grid' );
431
432 $this->add_control(
433 'container_type',
434 $this->get_container_type_control_options( $is_container_grid_active )
435 );
436
437 $this->add_control(
438 'content_width',
439 [
440 'label' => esc_html__( 'Content Width', 'elementor' ),
441 'type' => Controls_Manager::SELECT,
442 'default' => 'boxed',
443 'options' => [
444 'boxed' => esc_html__( 'Boxed', 'elementor' ),
445 'full' => esc_html__( 'Full Width', 'elementor' ),
446 ],
447 'render_type' => 'template',
448 'prefix_class' => 'e-con-',
449 'frontend_available' => true,
450 ]
451 );
452
453 $width_control_settings = [
454 'label' => esc_html__( 'Width', 'elementor' ),
455 'type' => Controls_Manager::SLIDER,
456 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
457 'range' => [
458 'px' => [
459 'min' => 500,
460 'max' => 1600,
461 ],
462 '%' => [
463 'min' => 0,
464 'max' => 100,
465 ],
466 'vw' => [
467 'min' => 0,
468 'max' => 100,
469 ],
470 ],
471 'default' => [
472 'unit' => '%',
473 ],
474 'min_affected_device' => [
475 Breakpoints_Manager::BREAKPOINT_KEY_DESKTOP => $min_affected_device,
476 Breakpoints_Manager::BREAKPOINT_KEY_LAPTOP => $min_affected_device,
477 Breakpoints_Manager::BREAKPOINT_KEY_TABLET_EXTRA => $min_affected_device,
478 Breakpoints_Manager::BREAKPOINT_KEY_TABLET => $min_affected_device,
479 Breakpoints_Manager::BREAKPOINT_KEY_MOBILE_EXTRA => $min_affected_device,
480 ],
481 'separator' => 'none',
482 ];
483
484 $this->add_responsive_control(
485 'width',
486 array_merge( $width_control_settings, [
487 'selectors' => [
488 '{{WRAPPER}}' => '--width: {{SIZE}}{{UNIT}};',
489 ],
490 'condition' => [
491 'content_width' => 'full',
492 ],
493 'device_args' => [
494 Breakpoints_Manager::BREAKPOINT_KEY_DESKTOP => [
495 'placeholder' => [
496 'size' => 100,
497 'unit' => '%',
498 ],
499 ],
500 Breakpoints_Manager::BREAKPOINT_KEY_MOBILE => [
501 // The mobile width is not inherited from the higher breakpoint width controls.
502 'placeholder' => [
503 'size' => 100,
504 'unit' => '%',
505 ],
506 ],
507 ],
508 ] )
509 );
510
511 $this->add_responsive_control(
512 'boxed_width',
513 array_merge( $width_control_settings, [
514 'selectors' => [
515 '{{WRAPPER}}' => '--content-width: {{SIZE}}{{UNIT}};',
516 ],
517 'condition' => [
518 'content_width' => 'boxed',
519 ],
520 'default' => [
521 'unit' => 'px',
522 ],
523 'device_args' => [
524 Breakpoints_Manager::BREAKPOINT_KEY_DESKTOP => [
525 // Use the default width from the kit as a placeholder.
526 'placeholder' => $this->active_kit->get_settings_for_display( 'container_width' ),
527 ],
528 Breakpoints_Manager::BREAKPOINT_KEY_MOBILE => [
529 // The mobile width is not inherited from the higher breakpoint width controls.
530 'placeholder' => [
531 'size' => 100,
532 'unit' => '%',
533 ],
534 ],
535 ],
536 ] )
537 );
538
539 $this->add_responsive_control(
540 'min_height',
541 [
542 'label' => esc_html__( 'Min Height', 'elementor' ),
543 'type' => Controls_Manager::SLIDER,
544 'size_units' => [ 'px', 'em', 'rem', 'vh', 'custom' ],
545 'range' => [
546 'px' => [
547 'min' => 0,
548 'max' => 1440,
549 ],
550 'vh' => [
551 'min' => 0,
552 'max' => 100,
553 ],
554 ],
555 'description' => sprintf(
556 esc_html__( 'To achieve full height Container use %s.', 'elementor' ),
557 '100vh'
558 ),
559 'selectors' => [
560 '{{WRAPPER}}' => '--min-height: {{SIZE}}{{UNIT}};',
561 ],
562 ]
563 );
564
565 $this->add_group_control(
566 Group_Control_Flex_Container::get_type(),
567 $this->get_flex_control_options( $is_container_grid_active )
568 );
569
570 if ( $is_container_grid_active ) {
571 $this->add_group_control(
572 Group_Control_Grid_Container::get_type(),
573 [
574 'name' => 'grid',
575 'selector' => '{{WRAPPER}}',
576 'condition' => [
577 'container_type' => [ 'grid' ],
578 ],
579 ]
580 );
581 }
582
583 $this->end_controls_section();
584 }
585
586 /**
587 * Register the Container's items layout controls.
588 *
589 * @return void
590 */
591 protected function register_items_layout_controls() {
592 $this->start_controls_section(
593 'section_layout_additional_options',
594 [
595 'label' => esc_html__( 'Additional Options', 'elementor' ),
596 'tab' => Controls_Manager::TAB_LAYOUT,
597 ]
598 );
599
600 $this->add_control(
601 'overflow',
602 [
603 'label' => esc_html__( 'Overflow', 'elementor' ),
604 'type' => Controls_Manager::SELECT,
605 'default' => '',
606 'options' => [
607 '' => esc_html__( 'Default', 'elementor' ),
608 'hidden' => esc_html__( 'Hidden', 'elementor' ),
609 'auto' => esc_html__( 'Auto', 'elementor' ),
610 ],
611 'selectors' => [
612 '{{WRAPPER}}' => '--overflow: {{VALUE}}',
613 ],
614 ]
615 );
616
617 $possible_tags = [
618 'div' => 'div',
619 'header' => 'header',
620 'footer' => 'footer',
621 'main' => 'main',
622 'article' => 'article',
623 'section' => 'section',
624 'aside' => 'aside',
625 'nav' => 'nav',
626 'a' => 'a ' . esc_html__( '(link)', 'elementor' ),
627 ];
628
629 $options = [
630 '' => esc_html__( 'Default', 'elementor' ),
631 ] + $possible_tags;
632
633 $this->add_control(
634 'html_tag',
635 [
636 'label' => esc_html__( 'HTML Tag', 'elementor' ),
637 'type' => Controls_Manager::SELECT,
638 'options' => $options,
639 ]
640 );
641
642 $this->add_control(
643 'link_note',
644 [
645 'type' => Controls_Manager::RAW_HTML,
646 'content_classes' => 'elementor-panel-alert elementor-panel-alert-warning',
647 'raw' => esc_html__( 'Don’t add links to elements nested in this container - it will break the layout.', 'elementor' ),
648 'condition' => [
649 'html_tag' => 'a',
650 ],
651 ]
652 );
653
654 $this->add_control(
655 'link',
656 [
657 'label' => esc_html__( 'Link', 'elementor' ),
658 'type' => Controls_Manager::URL,
659 'dynamic' => [
660 'active' => true,
661 ],
662 'condition' => [
663 'html_tag' => 'a',
664 ],
665 ]
666 );
667
668 $this->end_controls_section();
669 }
670
671 /**
672 * Register the Container's layout tab.
673 *
674 * @return void
675 */
676 protected function register_layout_tab() {
677 $this->register_container_layout_controls();
678
679 $this->register_items_layout_controls();
680 }
681
682 /**
683 * Register the Container's background controls.
684 *
685 * @return void
686 */
687 protected function register_background_controls() {
688 $this->start_controls_section(
689 'section_background',
690 [
691 'label' => esc_html__( 'Background', 'elementor' ),
692 'tab' => Controls_Manager::TAB_STYLE,
693 ]
694 );
695
696 $this->start_controls_tabs( 'tabs_background' );
697
698 /**
699 * Normal.
700 */
701 $this->start_controls_tab(
702 'tab_background_normal',
703 [
704 'label' => esc_html__( 'Normal', 'elementor' ),
705 ]
706 );
707
708 $this->add_group_control(
709 Group_Control_Background::get_type(),
710 [
711 'name' => 'background',
712 'types' => [ 'classic', 'gradient', 'video', 'slideshow' ],
713 'fields_options' => [
714 'background' => [
715 'frontend_available' => true,
716 ],
717 'image' => [
718 'background_lazyload' => [
719 'active' => true,
720 'keys' => [ 'background_image', 'url' ],
721 ],
722 ],
723 ],
724 ]
725 );
726
727 $this->end_controls_tab();
728
729 /**
730 * Hover.
731 */
732 $this->start_controls_tab(
733 'tab_background_hover',
734 [
735 'label' => esc_html__( 'Hover', 'elementor' ),
736 ]
737 );
738
739 $this->add_group_control(
740 Group_Control_Background::get_type(),
741 [
742 'name' => 'background_hover',
743 'selector' => '{{WRAPPER}}:hover',
744 ]
745 );
746
747 $this->add_control(
748 'background_hover_transition',
749 [
750 'label' => esc_html__( 'Transition Duration', 'elementor' ),
751 'type' => Controls_Manager::SLIDER,
752 'default' => [
753 'size' => 0.3,
754 ],
755 'render_type' => 'ui',
756 'separator' => 'before',
757 'selectors' => [
758 '{{WRAPPER}}' => '--background-transition: {{SIZE}}s;',
759 ],
760 ]
761 );
762
763 $this->end_controls_tab();
764
765 $this->end_controls_tabs();
766
767 $this->end_controls_section();
768 }
769
770 /**
771 * Register the Container's background overlay controls.
772 *
773 * @return void
774 */
775 protected function register_background_overlay_controls() {
776 $this->start_controls_section(
777 'section_background_overlay',
778 [
779 'label' => esc_html__( 'Background Overlay', 'elementor' ),
780 'tab' => Controls_Manager::TAB_STYLE,
781 ]
782 );
783
784 $this->start_controls_tabs( 'tabs_background_overlay' );
785
786 /**
787 * Normal.
788 */
789 $this->start_controls_tab(
790 'tab_background_overlay',
791 [
792 'label' => esc_html__( 'Normal', 'elementor' ),
793 ]
794 );
795
796 $background_overlay_selector = '{{WRAPPER}}::before, {{WRAPPER}} > .elementor-background-video-container::before, {{WRAPPER}} > .e-con-inner > .elementor-background-video-container::before, {{WRAPPER}} > .elementor-background-slideshow::before, {{WRAPPER}} > .e-con-inner > .elementor-background-slideshow::before, {{WRAPPER}} > .elementor-motion-effects-container > .elementor-motion-effects-layer::before';
797
798 $this->add_group_control(
799 Group_Control_Background::get_type(),
800 [
801 'name' => 'background_overlay',
802 'selector' => $background_overlay_selector,
803 'fields_options' => [
804 'background' => [
805 'selectors' => [
806 // Hack to set the `::before` content in order to render it only when there is a background overlay.
807 $background_overlay_selector => '--background-overlay: \'\';',
808 ],
809 ],
810 'image' => [
811 'background_lazyload' => [
812 'active' => true,
813 'keys' => [ 'background_overlay_image', 'url' ],
814 ],
815 ],
816 ],
817 ]
818 );
819
820 $this->add_responsive_control(
821 'background_overlay_opacity',
822 [
823 'label' => esc_html__( 'Opacity', 'elementor' ),
824 'type' => Controls_Manager::SLIDER,
825 'default' => [
826 'size' => .5,
827 ],
828 'range' => [
829 'px' => [
830 'max' => 1,
831 'step' => 0.01,
832 ],
833 ],
834 'selectors' => [
835 '{{WRAPPER}}' => '--overlay-opacity: {{SIZE}};',
836 ],
837 'condition' => [
838 'background_overlay_background' => [ 'classic', 'gradient' ],
839 ],
840 ]
841 );
842
843 $this->add_group_control(
844 Group_Control_Css_Filter::get_type(),
845 [
846 'name' => 'css_filters',
847 'selector' => '{{WRAPPER}}::before',
848 'conditions' => [
849 'relation' => 'or',
850 'terms' => [
851 [
852 'name' => 'background_overlay_image[url]',
853 'operator' => '!==',
854 'value' => '',
855 ],
856 [
857 'name' => 'background_overlay_color',
858 'operator' => '!==',
859 'value' => '',
860 ],
861 ],
862 ],
863 ]
864 );
865
866 $this->add_control(
867 'overlay_blend_mode',
868 [
869 'label' => esc_html__( 'Blend Mode', 'elementor' ),
870 'type' => Controls_Manager::SELECT,
871 'options' => [
872 '' => esc_html__( 'Normal', 'elementor' ),
873 'multiply' => esc_html__( 'Multiply', 'elementor' ),
874 'screen' => esc_html__( 'Screen', 'elementor' ),
875 'overlay' => esc_html__( 'Overlay', 'elementor' ),
876 'darken' => esc_html__( 'Darken', 'elementor' ),
877 'lighten' => esc_html__( 'Lighten', 'elementor' ),
878 'color-dodge' => esc_html__( 'Color Dodge', 'elementor' ),
879 'saturation' => esc_html__( 'Saturation', 'elementor' ),
880 'color' => esc_html__( 'Color', 'elementor' ),
881 'luminosity' => esc_html__( 'Luminosity', 'elementor' ),
882 ],
883 'selectors' => [
884 '{{WRAPPER}}' => '--overlay-mix-blend-mode: {{VALUE}}',
885 ],
886 'conditions' => [
887 'relation' => 'or',
888 'terms' => [
889 [
890 'name' => 'background_overlay_image[url]',
891 'operator' => '!==',
892 'value' => '',
893 ],
894 [
895 'name' => 'background_overlay_color',
896 'operator' => '!==',
897 'value' => '',
898 ],
899 ],
900 ],
901 ]
902 );
903
904 $this->end_controls_tab();
905
906 /**
907 * Hover.
908 */
909 $this->start_controls_tab(
910 'tab_background_overlay_hover',
911 [
912 'label' => esc_html__( 'Hover', 'elementor' ),
913 ]
914 );
915
916 $background_overlay_hover_selector = '{{WRAPPER}}:hover::before, {{WRAPPER}}:hover > .elementor-background-video-container::before, {{WRAPPER}}:hover > .e-con-inner > .elementor-background-video-container::before, {{WRAPPER}} > .elementor-background-slideshow:hover::before, {{WRAPPER}} > .e-con-inner > .elementor-background-slideshow:hover::before';
917
918 $this->add_group_control(
919 Group_Control_Background::get_type(),
920 [
921 'name' => 'background_overlay_hover',
922 'selector' => $background_overlay_hover_selector,
923 'fields_options' => [
924 'background' => [
925 'selectors' => [
926 // Hack to set the `::before` content in order to render it only when there is a background overlay.
927 $background_overlay_hover_selector => '--background-overlay: \'\';',
928 ],
929 ],
930 ],
931 ]
932 );
933
934 $this->add_responsive_control(
935 'background_overlay_hover_opacity',
936 [
937 'label' => esc_html__( 'Opacity', 'elementor' ),
938 'type' => Controls_Manager::SLIDER,
939 'default' => [
940 'size' => .5,
941 ],
942 'range' => [
943 'px' => [
944 'max' => 1,
945 'step' => 0.01,
946 ],
947 ],
948 'selectors' => [
949 '{{WRAPPER}}:hover' => '--overlay-opacity: {{SIZE}};',
950 ],
951 'condition' => [
952 'background_overlay_hover_background' => [ 'classic', 'gradient' ],
953 ],
954 ]
955 );
956
957 $this->add_group_control(
958 Group_Control_Css_Filter::get_type(),
959 [
960 'name' => 'css_filters_hover',
961 'selector' => '{{WRAPPER}}:hover::before',
962 ]
963 );
964
965 $this->add_control(
966 'background_overlay_hover_transition',
967 [
968 'label' => esc_html__( 'Transition Duration', 'elementor' ),
969 'type' => Controls_Manager::SLIDER,
970 'range' => [
971 'px' => [
972 'max' => 3,
973 'step' => 0.1,
974 ],
975 ],
976 'render_type' => 'ui',
977 'separator' => 'before',
978 'selectors' => [
979 '{{WRAPPER}}, {{WRAPPER}}::before' => '--overlay-transition: {{SIZE}}s;',
980 ],
981 ]
982 );
983
984 $this->end_controls_tab();
985
986 $this->end_controls_tabs();
987
988 $this->end_controls_section();
989 }
990
991 /**
992 * Register the Container's border controls.
993 *
994 * @return void
995 */
996 protected function register_border_controls() {
997 $this->start_controls_section(
998 'section_border',
999 [
1000 'label' => esc_html__( 'Border', 'elementor' ),
1001 'tab' => Controls_Manager::TAB_STYLE,
1002 ]
1003 );
1004
1005 $this->start_controls_tabs( 'tabs_border' );
1006
1007 /**
1008 * Normal.
1009 */
1010 $this->start_controls_tab(
1011 'tab_border',
1012 [
1013 'label' => esc_html__( 'Normal', 'elementor' ),
1014 ]
1015 );
1016
1017 $this->add_group_control(
1018 Group_Control_Border::get_type(),
1019 [
1020 'name' => 'border',
1021 'selector' => '{{WRAPPER}}',
1022 'fields_options' => [
1023 'width' => [
1024 'selectors' => [
1025 '{{SELECTOR}}' => "border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}}; --border-block-start-width: {{TOP}}{{UNIT}}; --border-inline-end-width: $this->logical_dimensions_inline_end; --border-block-end-width: {{BOTTOM}}{{UNIT}}; --border-inline-start-width: $this->logical_dimensions_inline_start;",
1026 ],
1027 ],
1028 'color' => [
1029 'selectors' => [
1030 '{{SELECTOR}}' => 'border-color: {{VALUE}}; --border-color: {{VALUE}};',
1031 ],
1032 ],
1033 'border' => [
1034 'selectors' => [
1035 '{{SELECTOR}}' => 'border-style: {{VALUE}}; --border-style: {{VALUE}};',
1036 ],
1037 ],
1038 ],
1039 ]
1040 );
1041
1042 $this->add_responsive_control(
1043 'border_radius',
1044 [
1045 'label' => esc_html__( 'Border Radius', 'elementor' ),
1046 'type' => Controls_Manager::DIMENSIONS,
1047 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ],
1048 'selectors' => [
1049 '{{WRAPPER}}' => '--border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
1050 ],
1051 ]
1052 );
1053
1054 $this->add_group_control(
1055 Group_Control_Box_Shadow::get_type(),
1056 [
1057 'name' => 'box_shadow',
1058 ]
1059 );
1060
1061 $this->end_controls_tab();
1062
1063 /**
1064 * Hover.
1065 */
1066 $this->start_controls_tab(
1067 'tab_border_hover',
1068 [
1069 'label' => esc_html__( 'Hover', 'elementor' ),
1070 ]
1071 );
1072
1073 $this->add_group_control(
1074 Group_Control_Border::get_type(),
1075 [
1076 'name' => 'border_hover',
1077 'selector' => '{{WRAPPER}}:hover',
1078 'fields_options' => [
1079 'width' => [
1080 'selectors' => [
1081 '{{SELECTOR}}' => "border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}}; --border-block-start-width: {{TOP}}{{UNIT}}; --border-inline-end-width: $this->logical_dimensions_inline_end; --border-block-end-width: {{BOTTOM}}{{UNIT}}; --border-inline-start-width: $this->logical_dimensions_inline_start;",
1082 ],
1083 ],
1084 'color' => [
1085 'selectors' => [
1086 '{{SELECTOR}}' => 'border-color: {{VALUE}}; --border-color: {{VALUE}};',
1087 ],
1088 ],
1089 ],
1090 ]
1091 );
1092
1093 $this->add_responsive_control(
1094 'border_radius_hover',
1095 [
1096 'label' => esc_html__( 'Border Radius', 'elementor' ),
1097 'type' => Controls_Manager::DIMENSIONS,
1098 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ],
1099 'selectors' => [
1100 '{{WRAPPER}}:hover' => "--border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}}; --border-top-left-radius: {{TOP}}{{UNIT}}; --border-top-right-radius: $this->logical_dimensions_inline_end; --border-bottom-right-radius: {{BOTTOM}}{{UNIT}}; --border-bottom-left-radius: $this->logical_dimensions_inline_start;",
1101 ],
1102 ]
1103 );
1104
1105 $this->add_group_control(
1106 Group_Control_Box_Shadow::get_type(),
1107 [
1108 'name' => 'box_shadow_hover',
1109 'selector' => '{{WRAPPER}}:hover',
1110 ]
1111 );
1112
1113 $this->add_control(
1114 'border_hover_transition',
1115 [
1116 'label' => esc_html__( 'Transition Duration', 'elementor' ),
1117 'type' => Controls_Manager::SLIDER,
1118 'separator' => 'before',
1119 'default' => [
1120 'size' => 0.3,
1121 ],
1122 'range' => [
1123 'px' => [
1124 'max' => 3,
1125 'step' => 0.1,
1126 ],
1127 ],
1128 'conditions' => [
1129 'relation' => 'or',
1130 'terms' => [
1131 [
1132 'name' => 'background_background',
1133 'operator' => '!==',
1134 'value' => '',
1135 ],
1136 [
1137 'name' => 'border_border',
1138 'operator' => '!==',
1139 'value' => '',
1140 ],
1141 ],
1142 ],
1143 'selectors' => [
1144 '{{WRAPPER}}, {{WRAPPER}}::before' => '--border-transition: {{SIZE}}s;',
1145 ],
1146 ]
1147 );
1148
1149 $this->end_controls_tab();
1150
1151 $this->end_controls_tabs();
1152
1153 $this->end_controls_section();
1154 }
1155
1156 /**
1157 * Register the Container's shape dividers controls.
1158 * TODO: Copied from `section.php`.
1159 *
1160 * @return void
1161 */
1162 protected function register_shape_dividers_controls() {
1163 $this->start_controls_section(
1164 'section_shape_divider',
1165 [
1166 'label' => esc_html__( 'Shape Divider', 'elementor' ),
1167 'tab' => Controls_Manager::TAB_STYLE,
1168 ]
1169 );
1170
1171 $this->start_controls_tabs( 'tabs_shape_dividers' );
1172
1173 $shapes_options = [
1174 '' => esc_html__( 'None', 'elementor' ),
1175 ];
1176
1177 foreach ( Shapes::get_shapes() as $shape_name => $shape_props ) {
1178 $shapes_options[ $shape_name ] = $shape_props['title'];
1179 }
1180
1181 foreach ( [
1182 'top' => esc_html__( 'Top', 'elementor' ),
1183 'bottom' => esc_html__( 'Bottom', 'elementor' ),
1184 ] as $side => $side_label ) {
1185 $base_control_key = "shape_divider_$side";
1186
1187 $this->start_controls_tab(
1188 "tab_$base_control_key",
1189 [
1190 'label' => $side_label,
1191 ]
1192 );
1193
1194 $this->add_control(
1195 $base_control_key,
1196 [
1197 'label' => esc_html__( 'Type', 'elementor' ),
1198 'type' => Controls_Manager::SELECT,
1199 'options' => $shapes_options,
1200 'render_type' => 'none',
1201 'frontend_available' => true,
1202 ]
1203 );
1204
1205 $this->add_control(
1206 $base_control_key . '_color',
1207 [
1208 'label' => esc_html__( 'Color', 'elementor' ),
1209 'type' => Controls_Manager::COLOR,
1210 'condition' => [
1211 "shape_divider_$side!" => '',
1212 ],
1213 'selectors' => [
1214 "{{WRAPPER}} > .elementor-shape-$side .elementor-shape-fill, {{WRAPPER}} > .e-con-inner > .elementor-shape-$side .elementor-shape-fill" => 'fill: {{UNIT}};',
1215 ],
1216 ]
1217 );
1218
1219 $this->add_responsive_control(
1220 $base_control_key . '_width',
1221 [
1222 'label' => esc_html__( 'Width', 'elementor' ),
1223 'type' => Controls_Manager::SLIDER,
1224 'default' => [
1225 'unit' => '%',
1226 ],
1227 'tablet_default' => [
1228 'unit' => '%',
1229 ],
1230 'mobile_default' => [
1231 'unit' => '%',
1232 ],
1233 'range' => [
1234 '%' => [
1235 'min' => 100,
1236 'max' => 300,
1237 ],
1238 ],
1239 'condition' => [
1240 "shape_divider_$side" => array_keys( Shapes::filter_shapes( 'height_only', Shapes::FILTER_EXCLUDE ) ),
1241 ],
1242 'selectors' => [
1243 "{{WRAPPER}} > .elementor-shape-$side svg, {{WRAPPER}} > .e-con-inner > .elementor-shape-$side svg" => 'width: calc({{SIZE}}{{UNIT}} + 1.3px)',
1244 ],
1245 ]
1246 );
1247
1248 $this->add_responsive_control(
1249 $base_control_key . '_height',
1250 [
1251 'label' => esc_html__( 'Height', 'elementor' ),
1252 'type' => Controls_Manager::SLIDER,
1253 'range' => [
1254 'px' => [
1255 'max' => 500,
1256 ],
1257 ],
1258 'condition' => [
1259 "shape_divider_$side!" => '',
1260 ],
1261 'selectors' => [
1262 "{{WRAPPER}} > .elementor-shape-$side svg, {{WRAPPER}} > .e-con-inner > .elementor-shape-$side svg" => 'height: {{SIZE}}{{UNIT}};',
1263 ],
1264 ]
1265 );
1266
1267 $this->add_control(
1268 $base_control_key . '_flip',
1269 [
1270 'label' => esc_html__( 'Flip', 'elementor' ),
1271 'type' => Controls_Manager::SWITCHER,
1272 'condition' => [
1273 "shape_divider_$side" => array_keys( Shapes::filter_shapes( 'has_flip' ) ),
1274 ],
1275 'selectors' => [
1276 "{{WRAPPER}} > .elementor-shape-$side svg, {{WRAPPER}} > .e-con-inner > .elementor-shape-$side svg" => 'transform: translateX(-50%) rotateY(180deg)',
1277 ],
1278 ]
1279 );
1280
1281 $this->add_control(
1282 $base_control_key . '_negative',
1283 [
1284 'label' => esc_html__( 'Invert', 'elementor' ),
1285 'type' => Controls_Manager::SWITCHER,
1286 'frontend_available' => true,
1287 'condition' => [
1288 "shape_divider_$side" => array_keys( Shapes::filter_shapes( 'has_negative' ) ),
1289 ],
1290 'render_type' => 'none',
1291 ]
1292 );
1293
1294 $this->add_control(
1295 $base_control_key . '_above_content',
1296 [
1297 'label' => esc_html__( 'Bring to Front', 'elementor' ),
1298 'type' => Controls_Manager::SWITCHER,
1299 'selectors' => [
1300 "{{WRAPPER}} > .elementor-shape-$side, {{WRAPPER}} > .e-con-inner > .elementor-shape-$side" => 'z-index: 2; pointer-events: none',
1301 ],
1302 'condition' => [
1303 "shape_divider_$side!" => '',
1304 ],
1305 ]
1306 );
1307
1308 $this->end_controls_tab();
1309 }
1310
1311 $this->end_controls_tabs();
1312
1313 $this->end_controls_section();
1314 }
1315 /**
1316 * Register the Container's style tab.
1317 *
1318 * @return void
1319 */
1320 protected function register_style_tab() {
1321 $this->register_background_controls();
1322
1323 $this->register_background_overlay_controls();
1324
1325 $this->register_border_controls();
1326
1327 $this->register_shape_dividers_controls();
1328 }
1329
1330 /**
1331 * Register the Container's advanced style controls.
1332 *
1333 * @return void
1334 */
1335 protected function register_advanced_controls() {
1336 $this->start_controls_section(
1337 'section_layout',
1338 [
1339 'label' => esc_html__( 'Layout', 'elementor' ),
1340 'tab' => Controls_Manager::TAB_ADVANCED,
1341 ]
1342 );
1343
1344 $this->add_responsive_control(
1345 'margin',
1346 [
1347 'label' => esc_html__( 'Margin', 'elementor' ),
1348 'type' => Controls_Manager::DIMENSIONS,
1349 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
1350 'selectors' => [
1351 '{{WRAPPER}}' => "--margin-block-start: {{TOP}}{{UNIT}}; --margin-block-end: {{BOTTOM}}{{UNIT}}; --margin-inline-start: $this->logical_dimensions_inline_start; --margin-inline-end: $this->logical_dimensions_inline_end;",
1352 ],
1353 ]
1354 );
1355
1356 $this->add_responsive_control(
1357 'padding',
1358 [
1359 'label' => esc_html__( 'Padding', 'elementor' ),
1360 'type' => Controls_Manager::DIMENSIONS,
1361 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
1362 'selectors' => [
1363 '{{WRAPPER}}' => "--padding-block-start: {{TOP}}{{UNIT}}; --padding-block-end: {{BOTTOM}}{{UNIT}}; --padding-inline-start: $this->logical_dimensions_inline_start; --padding-inline-end: $this->logical_dimensions_inline_end;",
1364 ],
1365 ]
1366 );
1367
1368 $this->add_group_control(
1369 Group_Control_Flex_Item::get_type(),
1370 [
1371 'name' => '_flex',
1372 'include' => [
1373 'align_self',
1374 'order',
1375 'order_custom',
1376 'size',
1377 'grow',
1378 'shrink',
1379 ],
1380 'selector' => '{{WRAPPER}}.e-con', // Hack to increase specificity.
1381 'separator' => 'before',
1382 ]
1383 );
1384
1385 $this->add_control(
1386 'position_description',
1387 [
1388 'raw' => '<strong>' . esc_html__( 'Please note!', 'elementor' ) . '</strong> ' . esc_html__( 'Custom positioning is not considered best practice for responsive web design and should not be used too frequently.', 'elementor' ),
1389 'type' => Controls_Manager::RAW_HTML,
1390 'content_classes' => 'elementor-panel-alert elementor-panel-alert-warning',
1391 'render_type' => 'ui',
1392 'condition' => [
1393 'position!' => '',
1394 ],
1395 ]
1396 );
1397
1398 // TODO: Copied from `common.php` - Extract to group control.
1399 $this->add_control(
1400 'position',
1401 [
1402 'label' => esc_html__( 'Position', 'elementor' ),
1403 'type' => Controls_Manager::SELECT,
1404 'default' => '',
1405 'options' => [
1406 '' => esc_html__( 'Default', 'elementor' ),
1407 'absolute' => esc_html__( 'Absolute', 'elementor' ),
1408 'fixed' => esc_html__( 'Fixed', 'elementor' ),
1409 ],
1410 'selectors' => [
1411 '{{WRAPPER}}' => '--position: {{VALUE}};',
1412 ],
1413 'frontend_available' => true,
1414 'separator' => 'before',
1415 ]
1416 );
1417
1418 $left = esc_html__( 'Left', 'elementor' );
1419 $right = esc_html__( 'Right', 'elementor' );
1420
1421 $start = is_rtl() ? $right : $left;
1422 $end = ! is_rtl() ? $right : $left;
1423
1424 $this->add_control(
1425 '_offset_orientation_h',
1426 [
1427 'label' => esc_html__( 'Horizontal Orientation', 'elementor' ),
1428 'type' => Controls_Manager::CHOOSE,
1429 'toggle' => false,
1430 'default' => 'start',
1431 'options' => [
1432 'start' => [
1433 'title' => $start,
1434 'icon' => 'eicon-h-align-left',
1435 ],
1436 'end' => [
1437 'title' => $end,
1438 'icon' => 'eicon-h-align-right',
1439 ],
1440 ],
1441 'classes' => 'elementor-control-start-end',
1442 'render_type' => 'ui',
1443 'condition' => [
1444 'position!' => '',
1445 ],
1446 ]
1447 );
1448
1449 $this->add_responsive_control(
1450 '_offset_x',
1451 [
1452 'label' => esc_html__( 'Offset', 'elementor' ),
1453 'type' => Controls_Manager::SLIDER,
1454 'range' => [
1455 'px' => [
1456 'min' => -1000,
1457 'max' => 1000,
1458 'step' => 1,
1459 ],
1460 '%' => [
1461 'min' => -200,
1462 'max' => 200,
1463 ],
1464 'vw' => [
1465 'min' => -200,
1466 'max' => 200,
1467 ],
1468 'vh' => [
1469 'min' => -200,
1470 'max' => 200,
1471 ],
1472 ],
1473 'default' => [
1474 'size' => '0',
1475 ],
1476 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'vh', 'custom' ],
1477 'selectors' => [
1478 'body:not(.rtl) {{WRAPPER}}' => 'left: {{SIZE}}{{UNIT}}',
1479 'body.rtl {{WRAPPER}}' => 'right: {{SIZE}}{{UNIT}}',
1480 ],
1481 'condition' => [
1482 '_offset_orientation_h!' => 'end',
1483 'position!' => '',
1484 ],
1485 ]
1486 );
1487
1488 $this->add_responsive_control(
1489 '_offset_x_end',
1490 [
1491 'label' => esc_html__( 'Offset', 'elementor' ),
1492 'type' => Controls_Manager::SLIDER,
1493 'range' => [
1494 'px' => [
1495 'min' => -1000,
1496 'max' => 1000,
1497 'step' => 0.1,
1498 ],
1499 '%' => [
1500 'min' => -200,
1501 'max' => 200,
1502 ],
1503 'vw' => [
1504 'min' => -200,
1505 'max' => 200,
1506 ],
1507 'vh' => [
1508 'min' => -200,
1509 'max' => 200,
1510 ],
1511 ],
1512 'default' => [
1513 'size' => '0',
1514 ],
1515 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'vh', 'custom' ],
1516 'selectors' => [
1517 'body:not(.rtl) {{WRAPPER}}' => 'right: {{SIZE}}{{UNIT}}',
1518 'body.rtl {{WRAPPER}}' => 'left: {{SIZE}}{{UNIT}}',
1519 ],
1520 'condition' => [
1521 '_offset_orientation_h' => 'end',
1522 'position!' => '',
1523 ],
1524 ]
1525 );
1526
1527 $this->add_control(
1528 '_offset_orientation_v',
1529 [
1530 'label' => esc_html__( 'Vertical Orientation', 'elementor' ),
1531 'type' => Controls_Manager::CHOOSE,
1532 'toggle' => false,
1533 'default' => 'start',
1534 'options' => [
1535 'start' => [
1536 'title' => esc_html__( 'Top', 'elementor' ),
1537 'icon' => 'eicon-v-align-top',
1538 ],
1539 'end' => [
1540 'title' => esc_html__( 'Bottom', 'elementor' ),
1541 'icon' => 'eicon-v-align-bottom',
1542 ],
1543 ],
1544 'render_type' => 'ui',
1545 'condition' => [
1546 'position!' => '',
1547 ],
1548 ]
1549 );
1550
1551 $this->add_responsive_control(
1552 '_offset_y',
1553 [
1554 'label' => esc_html__( 'Offset', 'elementor' ),
1555 'type' => Controls_Manager::SLIDER,
1556 'range' => [
1557 'px' => [
1558 'min' => -1000,
1559 'max' => 1000,
1560 'step' => 1,
1561 ],
1562 '%' => [
1563 'min' => -200,
1564 'max' => 200,
1565 ],
1566 'vh' => [
1567 'min' => -200,
1568 'max' => 200,
1569 ],
1570 'vw' => [
1571 'min' => -200,
1572 'max' => 200,
1573 ],
1574 ],
1575 'size_units' => [ 'px', '%', 'em', 'rem', 'vh', 'vw', 'custom' ],
1576 'default' => [
1577 'size' => '0',
1578 ],
1579 'selectors' => [
1580 '{{WRAPPER}}' => 'top: {{SIZE}}{{UNIT}}',
1581 ],
1582 'condition' => [
1583 '_offset_orientation_v!' => 'end',
1584 'position!' => '',
1585 ],
1586 ]
1587 );
1588
1589 $this->add_responsive_control(
1590 '_offset_y_end',
1591 [
1592 'label' => esc_html__( 'Offset', 'elementor' ),
1593 'type' => Controls_Manager::SLIDER,
1594 'range' => [
1595 'px' => [
1596 'min' => -1000,
1597 'max' => 1000,
1598 'step' => 1,
1599 ],
1600 '%' => [
1601 'min' => -200,
1602 'max' => 200,
1603 ],
1604 'vh' => [
1605 'min' => -200,
1606 'max' => 200,
1607 ],
1608 'vw' => [
1609 'min' => -200,
1610 'max' => 200,
1611 ],
1612 ],
1613 'size_units' => [ 'px', '%', 'em', 'rem', 'vh', 'vw', 'custom' ],
1614 'default' => [
1615 'size' => '0',
1616 ],
1617 'selectors' => [
1618 '{{WRAPPER}}' => 'bottom: {{SIZE}}{{UNIT}}',
1619 ],
1620 'condition' => [
1621 '_offset_orientation_v' => 'end',
1622 'position!' => '',
1623 ],
1624 ]
1625 );
1626
1627 $this->add_responsive_control(
1628 'z_index',
1629 [
1630 'label' => esc_html__( 'Z-Index', 'elementor' ),
1631 'type' => Controls_Manager::NUMBER,
1632 'min' => 0,
1633 'selectors' => [
1634 '{{WRAPPER}}' => '--z-index: {{VALUE}};',
1635 ],
1636 ]
1637 );
1638
1639 $this->add_control(
1640 '_element_id',
1641 [
1642 'label' => esc_html__( 'CSS ID', 'elementor' ),
1643 'type' => Controls_Manager::TEXT,
1644 'default' => '',
1645 'ai' => [
1646 'active' => false,
1647 ],
1648 'dynamic' => [
1649 'active' => true,
1650 ],
1651 'title' => esc_html__( 'Add your custom id WITHOUT the Pound key. e.g: my-id', 'elementor' ),
1652 'style_transfer' => false,
1653 'classes' => 'elementor-control-direction-ltr',
1654 ]
1655 );
1656
1657 $this->add_control(
1658 'css_classes',
1659 [
1660 'label' => esc_html__( 'CSS Classes', 'elementor' ),
1661 'type' => Controls_Manager::TEXT,
1662 'default' => '',
1663 'ai' => [
1664 'active' => false,
1665 ],
1666 'dynamic' => [
1667 'active' => true,
1668 ],
1669 'prefix_class' => '',
1670 'title' => esc_html__( 'Add your custom class WITHOUT the dot. e.g: my-class', 'elementor' ),
1671 'classes' => 'elementor-control-direction-ltr',
1672 ]
1673 );
1674
1675 $this->end_controls_section();
1676 }
1677
1678 /**
1679 * Register the Container's motion effects controls.
1680 *
1681 * @return void
1682 */
1683 protected function register_motion_effects_controls() {
1684 $this->start_controls_section(
1685 'section_effects',
1686 [
1687 'label' => esc_html__( 'Motion Effects', 'elementor' ),
1688 'tab' => Controls_Manager::TAB_ADVANCED,
1689 ]
1690 );
1691
1692 $this->add_responsive_control(
1693 'animation',
1694 [
1695 'label' => esc_html__( 'Entrance Animation', 'elementor' ),
1696 'type' => Controls_Manager::ANIMATION,
1697 'frontend_available' => true,
1698 ]
1699 );
1700
1701 $this->add_control(
1702 'animation_duration',
1703 [
1704 'label' => esc_html__( 'Animation Duration', 'elementor' ),
1705 'type' => Controls_Manager::SELECT,
1706 'default' => '',
1707 'options' => [
1708 'slow' => esc_html__( 'Slow', 'elementor' ),
1709 '' => esc_html__( 'Normal', 'elementor' ),
1710 'fast' => esc_html__( 'Fast', 'elementor' ),
1711 ],
1712 'prefix_class' => 'animated-',
1713 'condition' => [
1714 'animation!' => '',
1715 ],
1716 ]
1717 );
1718
1719 $this->add_control(
1720 'animation_delay',
1721 [
1722 'label' => esc_html__( 'Animation Delay', 'elementor' ) . ' (ms)',
1723 'type' => Controls_Manager::NUMBER,
1724 'default' => '',
1725 'min' => 0,
1726 'step' => 100,
1727 'condition' => [
1728 'animation!' => '',
1729 ],
1730 'render_type' => 'none',
1731 'frontend_available' => true,
1732 ]
1733 );
1734
1735 $this->end_controls_section();
1736 }
1737
1738 /**
1739 * Register the Container's responsive controls.
1740 *
1741 * @return void
1742 */
1743 protected function register_responsive_controls() {
1744 $this->start_controls_section(
1745 '_section_responsive',
1746 [
1747 'label' => esc_html__( 'Responsive', 'elementor' ),
1748 'tab' => Controls_Manager::TAB_ADVANCED,
1749 ]
1750 );
1751
1752 $this->add_control(
1753 'heading_visibility',
1754 [
1755 'label' => esc_html__( 'Visibility', 'elementor' ),
1756 'type' => Controls_Manager::HEADING,
1757 ]
1758 );
1759
1760 $this->add_control(
1761 'responsive_description',
1762 [
1763 'raw' => sprintf(
1764 /* translators: 1: Link open tag, 2: Link close tag. */
1765 esc_html__( 'Responsive visibility will take effect only on %1$s preview mode %2$s or live page, and not while editing in Elementor.', 'elementor' ),
1766 '<a href="javascript: $e.run( \'panel/close\' )">',
1767 '</a>'
1768 ),
1769 'type' => Controls_Manager::RAW_HTML,
1770 'content_classes' => 'elementor-descriptor',
1771 ]
1772 );
1773
1774 $this->add_hidden_device_controls();
1775
1776 $this->end_controls_section();
1777 }
1778
1779 /**
1780 * Register the Container's advanced tab.
1781 *
1782 * @return void
1783 */
1784 protected function register_advanced_tab() {
1785 $this->register_advanced_controls();
1786
1787 $this->register_motion_effects_controls();
1788
1789 $this->hook_sticky_notice_into_transform_section();
1790
1791 $this->register_transform_section( 'con' );
1792
1793 $this->register_responsive_controls();
1794
1795 Plugin::$instance->controls_manager->add_custom_attributes_controls( $this );
1796
1797 Plugin::$instance->controls_manager->add_custom_css_controls( $this );
1798 }
1799
1800 protected function hook_sticky_notice_into_transform_section() {
1801 add_action( 'elementor/element/container/_section_transform/after_section_start', function( Container $container ) {
1802 if ( ! empty( $container->get_controls( 'transform_sticky_notice' ) ) ) {
1803 return;
1804 }
1805
1806 $container->add_control(
1807 'transform_sticky_notice',
1808 [
1809 'type' => Controls_Manager::RAW_HTML,
1810 'raw' => esc_html__( 'Note: Avoid applying transform properties on sticky containers. Doing so might cause unexpected results.', 'elementor' ),
1811 'content_classes' => 'elementor-panel-alert elementor-panel-alert-warning',
1812 ]
1813 );
1814 } );
1815 }
1816
1817 /**
1818 * Register the Container's controls.
1819 *
1820 * @return void
1821 */
1822 protected function register_controls() {
1823 $this->register_layout_tab();
1824 $this->register_style_tab();
1825 $this->register_advanced_tab();
1826 }
1827
1828 public function on_import( $element ) {
1829 return self::slider_to_gaps_converter( $element );
1830 }
1831
1832 /**
1833 * convert slider to gaps control for the 3.16 upgrade script
1834 *
1835 * @param $element
1836 * @return array
1837 */
1838 public static function slider_to_gaps_converter( $element ) {
1839 $breakpoints = array_keys( (array) Plugin::$instance->breakpoints->get_breakpoints() );
1840 $breakpoints[] = 'desktop';
1841 $control_name = 'flex_gap';
1842
1843 foreach ( $breakpoints as $breakpoint ) {
1844 $control = 'desktop' !== $breakpoint
1845 ? $control_name . '_' . $breakpoint
1846 : $control_name;
1847
1848 if ( isset( $element['settings'][ $control ] ) ) {
1849 $old_size = strval( $element['settings'][ $control ]['size'] );
1850
1851 $element['settings'][ $control ]['column'] = $old_size;
1852 $element['settings'][ $control ]['row'] = $old_size;
1853 $element['settings'][ $control ]['isLinked'] = true;
1854 }
1855 }
1856
1857 return $element;
1858 }
1859 }
1860