PluginProbe
Elementor Website Builder – more than just a page builder / 3.27.5
Elementor Website Builder – more than just a page builder v3.27.5
4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 4.1.0-beta1 4.1.0-dev1 4.0.7 All 451 releases
elementor / includes / elements / container.php

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

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