PluginProbe
Elementor Website Builder – more than just a page builder / 3.11.5
Elementor Website Builder – more than just a page builder v3.11.5
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.11.5, at includes/elements/container.php

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