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

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