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

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