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

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