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

1,874 lines 45.0 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 elementor-html5-video" {{ 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 elementor-html5-video" <?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 ],
679 ]
680 );
681
682 $this->end_controls_tab();
683
684 /**
685 * Hover.
686 */
687 $this->start_controls_tab(
688 'tab_background_hover',
689 [
690 'label' => esc_html__( 'Hover', 'elementor' ),
691 ]
692 );
693
694 $this->add_group_control(
695 Group_Control_Background::get_type(),
696 [
697 'name' => 'background_hover',
698 'selector' => '{{WRAPPER}}:hover',
699 ]
700 );
701
702 $this->add_control(
703 'background_hover_transition',
704 [
705 'label' => esc_html__( 'Transition Duration', 'elementor' ) . ' (s)',
706 'type' => Controls_Manager::SLIDER,
707 'default' => [
708 'size' => 0.3,
709 ],
710 'range' => [
711 'px' => [
712 'min' => 0,
713 'max' => 3,
714 'step' => 0.1,
715 ],
716 ],
717 'render_type' => 'ui',
718 'separator' => 'before',
719 'condition' => [
720 'background_hover_background' => [ 'classic', 'gradient' ],
721 ],
722 'selectors' => [
723 '{{WRAPPER}}' => '--background-transition: {{SIZE}}s;',
724 ],
725 ]
726 );
727
728 $this->end_controls_tab();
729
730 $this->end_controls_tabs();
731
732 $this->end_controls_section();
733 }
734
735 /**
736 * Register the Container's background overlay controls.
737 *
738 * @return void
739 */
740 protected function register_background_overlay_controls() {
741 $this->start_controls_section(
742 'section_background_overlay',
743 [
744 'label' => esc_html__( 'Background Overlay', 'elementor' ),
745 'tab' => Controls_Manager::TAB_STYLE,
746 ]
747 );
748
749 $this->start_controls_tabs( 'tabs_background_overlay' );
750
751 /**
752 * Normal.
753 */
754 $this->start_controls_tab(
755 'tab_background_overlay',
756 [
757 'label' => esc_html__( 'Normal', 'elementor' ),
758 ]
759 );
760
761 $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';
762
763 $this->add_group_control(
764 Group_Control_Background::get_type(),
765 [
766 'name' => 'background_overlay',
767 'selector' => $background_overlay_selector,
768 'fields_options' => [
769 'background' => [
770 'selectors' => [
771 // Hack to set the `::before` content in order to render it only when there is a background overlay.
772 $background_overlay_selector => '--background-overlay: \'\';',
773 ],
774 ],
775 ],
776 ]
777 );
778
779 $this->add_responsive_control(
780 'background_overlay_opacity',
781 [
782 'label' => esc_html__( 'Opacity', 'elementor' ),
783 'type' => Controls_Manager::SLIDER,
784 'default' => [
785 'size' => .5,
786 ],
787 'range' => [
788 'px' => [
789 'max' => 1,
790 'step' => 0.01,
791 ],
792 ],
793 'selectors' => [
794 '{{WRAPPER}}' => '--overlay-opacity: {{SIZE}};',
795 ],
796 'condition' => [
797 'background_overlay_background' => [ 'classic', 'gradient' ],
798 ],
799 ]
800 );
801
802 $this->add_group_control(
803 Group_Control_Css_Filter::get_type(),
804 [
805 'name' => 'css_filters',
806 'selector' => '{{WRAPPER}}::before',
807 'conditions' => [
808 'relation' => 'or',
809 'terms' => [
810 [
811 'name' => 'background_overlay_image[url]',
812 'operator' => '!==',
813 'value' => '',
814 ],
815 [
816 'name' => 'background_overlay_color',
817 'operator' => '!==',
818 'value' => '',
819 ],
820 ],
821 ],
822 ]
823 );
824
825 $this->add_control(
826 'overlay_blend_mode',
827 [
828 'label' => esc_html__( 'Blend Mode', 'elementor' ),
829 'type' => Controls_Manager::SELECT,
830 'options' => [
831 '' => esc_html__( 'Normal', 'elementor' ),
832 'multiply' => esc_html__( 'Multiply', 'elementor' ),
833 'screen' => esc_html__( 'Screen', 'elementor' ),
834 'overlay' => esc_html__( 'Overlay', 'elementor' ),
835 'darken' => esc_html__( 'Darken', 'elementor' ),
836 'lighten' => esc_html__( 'Lighten', 'elementor' ),
837 'color-dodge' => esc_html__( 'Color Dodge', 'elementor' ),
838 'saturation' => esc_html__( 'Saturation', 'elementor' ),
839 'color' => esc_html__( 'Color', 'elementor' ),
840 'luminosity' => esc_html__( 'Luminosity', 'elementor' ),
841 ],
842 'selectors' => [
843 '{{WRAPPER}}' => '--overlay-mix-blend-mode: {{VALUE}}',
844 ],
845 'conditions' => [
846 'relation' => 'or',
847 'terms' => [
848 [
849 'name' => 'background_overlay_image[url]',
850 'operator' => '!==',
851 'value' => '',
852 ],
853 [
854 'name' => 'background_overlay_color',
855 'operator' => '!==',
856 'value' => '',
857 ],
858 ],
859 ],
860 ]
861 );
862
863 $this->end_controls_tab();
864
865 /**
866 * Hover.
867 */
868 $this->start_controls_tab(
869 'tab_background_overlay_hover',
870 [
871 'label' => esc_html__( 'Hover', 'elementor' ),
872 ]
873 );
874
875 $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';
876
877 $this->add_group_control(
878 Group_Control_Background::get_type(),
879 [
880 'name' => 'background_overlay_hover',
881 'selector' => $background_overlay_hover_selector,
882 'fields_options' => [
883 'background' => [
884 'selectors' => [
885 // Hack to set the `::before` content in order to render it only when there is a background overlay.
886 $background_overlay_hover_selector => '--background-overlay: \'\';',
887 ],
888 ],
889 ],
890 ]
891 );
892
893 $this->add_responsive_control(
894 'background_overlay_hover_opacity',
895 [
896 'label' => esc_html__( 'Opacity', 'elementor' ),
897 'type' => Controls_Manager::SLIDER,
898 'default' => [
899 'size' => .5,
900 ],
901 'range' => [
902 'px' => [
903 'max' => 1,
904 'step' => 0.01,
905 ],
906 ],
907 'selectors' => [
908 '{{WRAPPER}}:hover' => '--overlay-opacity: {{SIZE}};',
909 ],
910 'condition' => [
911 'background_overlay_hover_background' => [ 'classic', 'gradient' ],
912 ],
913 ]
914 );
915
916 $this->add_control(
917 'background_overlay_hover_transition',
918 [
919 'label' => esc_html__( 'Transition Duration', 'elementor' ) . ' (s)',
920 'type' => Controls_Manager::SLIDER,
921 'range' => [
922 'px' => [
923 'min' => 0,
924 'max' => 3,
925 'step' => 0.1,
926 ],
927 ],
928 'render_type' => 'ui',
929 'separator' => 'before',
930 'condition' => [
931 'background_overlay_hover_background' => [ 'classic', 'gradient' ],
932 ],
933 'selectors' => [
934 '{{WRAPPER}}, {{WRAPPER}}::before' => '--overlay-transition: {{SIZE}}s;',
935 ],
936 ]
937 );
938
939 $this->add_group_control(
940 Group_Control_Css_Filter::get_type(),
941 [
942 'name' => 'css_filters_hover',
943 'selector' => '{{WRAPPER}}:hover::before',
944 ]
945 );
946
947 $this->end_controls_tab();
948
949 $this->end_controls_tabs();
950
951 $this->end_controls_section();
952 }
953
954 /**
955 * Register the Container's border controls.
956 *
957 * @return void
958 */
959 protected function register_border_controls() {
960 $this->start_controls_section(
961 'section_border',
962 [
963 'label' => esc_html__( 'Border', 'elementor' ),
964 'tab' => Controls_Manager::TAB_STYLE,
965 ]
966 );
967
968 $this->start_controls_tabs( 'tabs_border' );
969
970 /**
971 * Normal.
972 */
973 $this->start_controls_tab(
974 'tab_border',
975 [
976 'label' => esc_html__( 'Normal', 'elementor' ),
977 ]
978 );
979
980 $this->add_group_control(
981 Group_Control_Border::get_type(),
982 [
983 'name' => 'border',
984 'selector' => '{{WRAPPER}}',
985 'fields_options' => [
986 'width' => [
987 'selectors' => [
988 '{{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}};',
989 ],
990 ],
991 'color' => [
992 'selectors' => [
993 '{{SELECTOR}}' => 'border-color: {{VALUE}}; --border-color: {{VALUE}};',
994 ],
995 ],
996 'border' => [
997 'selectors' => [
998 '{{SELECTOR}}' => 'border-style: {{VALUE}}; --border-style: {{VALUE}};',
999 ],
1000 ],
1001 ],
1002 ]
1003 );
1004
1005 $this->add_responsive_control(
1006 'border_radius',
1007 [
1008 'label' => esc_html__( 'Border Radius', 'elementor' ),
1009 'type' => Controls_Manager::DIMENSIONS,
1010 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ],
1011 'selectors' => [
1012 '{{WRAPPER}}' => '--border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
1013 ],
1014 ]
1015 );
1016
1017 $this->add_group_control(
1018 Group_Control_Box_Shadow::get_type(),
1019 [
1020 'name' => 'box_shadow',
1021 ]
1022 );
1023
1024 $this->end_controls_tab();
1025
1026 /**
1027 * Hover.
1028 */
1029 $this->start_controls_tab(
1030 'tab_border_hover',
1031 [
1032 'label' => esc_html__( 'Hover', 'elementor' ),
1033 ]
1034 );
1035
1036 $this->add_group_control(
1037 Group_Control_Border::get_type(),
1038 [
1039 'name' => 'border_hover',
1040 'selector' => '{{WRAPPER}}:hover',
1041 'fields_options' => [
1042 'width' => [
1043 'selectors' => [
1044 '{{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}};',
1045 ],
1046 ],
1047 'color' => [
1048 'selectors' => [
1049 '{{SELECTOR}}' => 'border-color: {{VALUE}}; --border-color: {{VALUE}};',
1050 ],
1051 ],
1052 ],
1053 ]
1054 );
1055
1056 $this->add_responsive_control(
1057 'border_radius_hover',
1058 [
1059 'label' => esc_html__( 'Border Radius', 'elementor' ),
1060 'type' => Controls_Manager::DIMENSIONS,
1061 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ],
1062 'selectors' => [
1063 '{{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}};',
1064 ],
1065 ]
1066 );
1067
1068 $this->add_group_control(
1069 Group_Control_Box_Shadow::get_type(),
1070 [
1071 'name' => 'box_shadow_hover',
1072 'selector' => '{{WRAPPER}}:hover',
1073 ]
1074 );
1075
1076 $this->add_control(
1077 'border_hover_transition',
1078 [
1079 'label' => esc_html__( 'Transition Duration', 'elementor' ) . ' (s)',
1080 'type' => Controls_Manager::SLIDER,
1081 'separator' => 'before',
1082 'default' => [
1083 'size' => 0.3,
1084 ],
1085 'range' => [
1086 'px' => [
1087 'min' => 0,
1088 'max' => 3,
1089 'step' => 0.1,
1090 ],
1091 ],
1092 'conditions' => [
1093 'relation' => 'or',
1094 'terms' => [
1095 [
1096 'name' => 'border_hover_border',
1097 'operator' => '!==',
1098 'value' => '',
1099 ],
1100 [
1101 'name' => 'border_radius_hover[top]',
1102 'operator' => '!==',
1103 'value' => '',
1104 ],
1105 [
1106 'name' => 'border_radius_hover[right]',
1107 'operator' => '!==',
1108 'value' => '',
1109 ],
1110 [
1111 'name' => 'border_radius_hover[bottom]',
1112 'operator' => '!==',
1113 'value' => '',
1114 ],
1115 [
1116 'name' => 'border_radius_hover[left]',
1117 'operator' => '!==',
1118 'value' => '',
1119 ],
1120 ],
1121 ],
1122 'selectors' => [
1123 '{{WRAPPER}}, {{WRAPPER}}::before' => '--border-transition: {{SIZE}}s;',
1124 ],
1125 ]
1126 );
1127
1128 $this->end_controls_tab();
1129
1130 $this->end_controls_tabs();
1131
1132 $this->end_controls_section();
1133 }
1134
1135 /**
1136 * Register the Container's shape dividers controls.
1137 * TODO: Copied from `section.php`.
1138 *
1139 * @return void
1140 */
1141 protected function register_shape_dividers_controls() {
1142 $this->start_controls_section(
1143 'section_shape_divider',
1144 [
1145 'label' => esc_html__( 'Shape Divider', 'elementor' ),
1146 'tab' => Controls_Manager::TAB_STYLE,
1147 ]
1148 );
1149
1150 $this->start_controls_tabs( 'tabs_shape_dividers' );
1151
1152 $shapes_options = [
1153 '' => esc_html__( 'None', 'elementor' ),
1154 ];
1155
1156 foreach ( Shapes::get_shapes() as $shape_name => $shape_props ) {
1157 $shapes_options[ $shape_name ] = $shape_props['title'];
1158 }
1159
1160 foreach ( [
1161 'top' => esc_html__( 'Top', 'elementor' ),
1162 'bottom' => esc_html__( 'Bottom', 'elementor' ),
1163 ] as $side => $side_label ) {
1164 $base_control_key = "shape_divider_$side";
1165
1166 $this->start_controls_tab(
1167 "tab_$base_control_key",
1168 [
1169 'label' => $side_label,
1170 ]
1171 );
1172
1173 $this->add_control(
1174 $base_control_key,
1175 [
1176 'label' => esc_html__( 'Type', 'elementor' ),
1177 'type' => Controls_Manager::SELECT,
1178 'options' => $shapes_options,
1179 'render_type' => 'none',
1180 'frontend_available' => true,
1181 'assets' => [
1182 'styles' => [
1183 [
1184 'name' => 'e-shapes',
1185 'conditions' => [
1186 'terms' => [
1187 [
1188 'name' => $base_control_key,
1189 'operator' => '!==',
1190 'value' => '',
1191 ],
1192 ],
1193 ],
1194 ],
1195 ],
1196 ],
1197 ]
1198 );
1199
1200 $this->add_control(
1201 $base_control_key . '_color',
1202 [
1203 'label' => esc_html__( 'Color', 'elementor' ),
1204 'type' => Controls_Manager::COLOR,
1205 'condition' => [
1206 "shape_divider_$side!" => '',
1207 ],
1208 'selectors' => [
1209 "{{WRAPPER}} > .elementor-shape-$side .elementor-shape-fill, {{WRAPPER}} > .e-con-inner > .elementor-shape-$side .elementor-shape-fill" => 'fill: {{UNIT}};',
1210 ],
1211 ]
1212 );
1213
1214 $this->add_responsive_control(
1215 $base_control_key . '_width',
1216 [
1217 'label' => esc_html__( 'Width', 'elementor' ),
1218 'type' => Controls_Manager::SLIDER,
1219 'size_units' => [ '%', 'vw', 'custom' ],
1220 'default' => [
1221 'unit' => '%',
1222 ],
1223 'tablet_default' => [
1224 'unit' => '%',
1225 ],
1226 'mobile_default' => [
1227 'unit' => '%',
1228 ],
1229 'range' => [
1230 '%' => [
1231 'min' => 100,
1232 'max' => 300,
1233 ],
1234 'vw' => [
1235 'min' => 100,
1236 'max' => 300,
1237 ],
1238 ],
1239 'condition' => [
1240 "shape_divider_$side" => array_keys( Shapes::filter_shapes( 'height_only', Shapes::FILTER_EXCLUDE ) ),
1241 ],
1242 'selectors' => [
1243 "{{WRAPPER}} > .elementor-shape-$side svg, {{WRAPPER}} > .e-con-inner > .elementor-shape-$side svg" => 'width: calc({{SIZE}}{{UNIT}} + 1.3px)',
1244 ],
1245 ]
1246 );
1247
1248 $this->add_responsive_control(
1249 $base_control_key . '_height',
1250 [
1251 'label' => esc_html__( 'Height', 'elementor' ),
1252 'type' => Controls_Manager::SLIDER,
1253 'size_units' => [ 'px', 'em', 'rem', 'custom' ],
1254 'range' => [
1255 'px' => [
1256 'max' => 500,
1257 ],
1258 'em' => [
1259 'max' => 50,
1260 ],
1261 'rem' => [
1262 'max' => 50,
1263 ],
1264 ],
1265 'condition' => [
1266 "shape_divider_$side!" => '',
1267 ],
1268 'selectors' => [
1269 "{{WRAPPER}} > .elementor-shape-$side svg, {{WRAPPER}} > .e-con-inner > .elementor-shape-$side svg" => 'height: {{SIZE}}{{UNIT}};',
1270 ],
1271 ]
1272 );
1273
1274 $this->add_control(
1275 $base_control_key . '_flip',
1276 [
1277 'label' => esc_html__( 'Flip', 'elementor' ),
1278 'type' => Controls_Manager::SWITCHER,
1279 'condition' => [
1280 "shape_divider_$side" => array_keys( Shapes::filter_shapes( 'has_flip' ) ),
1281 ],
1282 'selectors' => [
1283 "{{WRAPPER}} > .elementor-shape-$side svg, {{WRAPPER}} > .e-con-inner > .elementor-shape-$side svg" => 'transform: translateX(-50%) rotateY(180deg)',
1284 ],
1285 ]
1286 );
1287
1288 $this->add_control(
1289 $base_control_key . '_negative',
1290 [
1291 'label' => esc_html__( 'Invert', 'elementor' ),
1292 'type' => Controls_Manager::SWITCHER,
1293 'frontend_available' => true,
1294 'condition' => [
1295 "shape_divider_$side" => array_keys( Shapes::filter_shapes( 'has_negative' ) ),
1296 ],
1297 'render_type' => 'none',
1298 ]
1299 );
1300
1301 $this->add_control(
1302 $base_control_key . '_above_content',
1303 [
1304 'label' => esc_html__( 'Bring to Front', 'elementor' ),
1305 'type' => Controls_Manager::SWITCHER,
1306 'selectors' => [
1307 "{{WRAPPER}} > .elementor-shape-$side, {{WRAPPER}} > .e-con-inner > .elementor-shape-$side" => 'z-index: 2; pointer-events: none',
1308 ],
1309 'condition' => [
1310 "shape_divider_$side!" => '',
1311 ],
1312 ]
1313 );
1314
1315 $this->end_controls_tab();
1316 }
1317
1318 $this->end_controls_tabs();
1319
1320 $this->end_controls_section();
1321 }
1322 /**
1323 * Register the Container's style tab.
1324 *
1325 * @return void
1326 */
1327 protected function register_style_tab() {
1328 $this->register_background_controls();
1329
1330 $this->register_background_overlay_controls();
1331
1332 $this->register_border_controls();
1333
1334 $this->register_shape_dividers_controls();
1335 }
1336
1337 /**
1338 * Register the Container's advanced style controls.
1339 *
1340 * @return void
1341 */
1342 protected function register_advanced_controls() {
1343 $this->start_controls_section(
1344 'section_layout',
1345 [
1346 'label' => esc_html__( 'Layout', 'elementor' ),
1347 'tab' => Controls_Manager::TAB_ADVANCED,
1348 ]
1349 );
1350
1351 $this->add_responsive_control(
1352 'margin',
1353 [
1354 'label' => esc_html__( 'Margin', 'elementor' ),
1355 'type' => Controls_Manager::DIMENSIONS,
1356 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
1357 'selectors' => [
1358 '{{WRAPPER}}' => '--margin-top: {{TOP}}{{UNIT}}; --margin-bottom: {{BOTTOM}}{{UNIT}}; --margin-left: {{LEFT}}{{UNIT}}; --margin-right: {{RIGHT}}{{UNIT}};',
1359 ],
1360 ]
1361 );
1362
1363 $this->add_responsive_control(
1364 'padding',
1365 [
1366 'label' => esc_html__( 'Padding', 'elementor' ),
1367 'type' => Controls_Manager::DIMENSIONS,
1368 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
1369 'selectors' => [
1370 '{{WRAPPER}}' => '--padding-top: {{TOP}}{{UNIT}}; --padding-bottom: {{BOTTOM}}{{UNIT}}; --padding-left: {{LEFT}}{{UNIT}}; --padding-right: {{RIGHT}}{{UNIT}};',
1371 ],
1372 ]
1373 );
1374
1375 $this->add_group_control(
1376 Group_Control_Flex_Item::get_type(),
1377 [
1378 'name' => '_flex',
1379 'include' => [
1380 'align_self',
1381 'order',
1382 'order_custom',
1383 'size',
1384 'grow',
1385 'shrink',
1386 ],
1387 'selector' => '{{WRAPPER}}.e-con', // Hack to increase specificity.
1388 'separator' => 'before',
1389 ]
1390 );
1391
1392 $this->add_control(
1393 'position_description',
1394 [
1395 'type' => Controls_Manager::ALERT,
1396 'alert_type' => 'warning',
1397 'heading' => esc_html__( 'Please note!', 'elementor' ),
1398 'content' => esc_html__( 'Custom positioning is not considered best practice for responsive web design and should not be used too frequently.', 'elementor' ),
1399 'render_type' => 'ui',
1400 'condition' => [
1401 'position!' => '',
1402 ],
1403 ]
1404 );
1405
1406 // TODO: Copied from `common.php` - Extract to group control.
1407 $this->add_control(
1408 'position',
1409 [
1410 'label' => esc_html__( 'Position', 'elementor' ),
1411 'type' => Controls_Manager::SELECT,
1412 'default' => '',
1413 'options' => [
1414 '' => esc_html__( 'Default', 'elementor' ),
1415 'absolute' => esc_html__( 'Absolute', 'elementor' ),
1416 'fixed' => esc_html__( 'Fixed', 'elementor' ),
1417 ],
1418 'selectors' => [
1419 '{{WRAPPER}}' => '--position: {{VALUE}};',
1420 ],
1421 'frontend_available' => true,
1422 'separator' => 'before',
1423 ]
1424 );
1425
1426 $left = esc_html__( 'Left', 'elementor' );
1427 $right = esc_html__( 'Right', 'elementor' );
1428
1429 $start = is_rtl() ? $right : $left;
1430 $end = ! is_rtl() ? $right : $left;
1431
1432 $this->add_control(
1433 '_offset_orientation_h',
1434 [
1435 'label' => esc_html__( 'Horizontal Orientation', 'elementor' ),
1436 'type' => Controls_Manager::CHOOSE,
1437 'toggle' => false,
1438 'default' => 'start',
1439 'options' => [
1440 'start' => [
1441 'title' => $start,
1442 'icon' => 'eicon-h-align-left',
1443 ],
1444 'end' => [
1445 'title' => $end,
1446 'icon' => 'eicon-h-align-right',
1447 ],
1448 ],
1449 'classes' => 'elementor-control-start-end',
1450 'render_type' => 'ui',
1451 'condition' => [
1452 'position!' => '',
1453 ],
1454 ]
1455 );
1456
1457 $this->add_responsive_control(
1458 '_offset_x',
1459 [
1460 'label' => esc_html__( 'Offset', 'elementor' ),
1461 'type' => Controls_Manager::SLIDER,
1462 'range' => [
1463 'px' => [
1464 'min' => -1000,
1465 'max' => 1000,
1466 ],
1467 '%' => [
1468 'min' => -200,
1469 'max' => 200,
1470 ],
1471 'vw' => [
1472 'min' => -200,
1473 'max' => 200,
1474 ],
1475 'vh' => [
1476 'min' => -200,
1477 'max' => 200,
1478 ],
1479 ],
1480 'default' => [
1481 'size' => 0,
1482 ],
1483 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'vh', 'custom' ],
1484 'selectors' => [
1485 'body:not(.rtl) {{WRAPPER}}' => 'left: {{SIZE}}{{UNIT}}',
1486 'body.rtl {{WRAPPER}}' => 'right: {{SIZE}}{{UNIT}}',
1487 ],
1488 'condition' => [
1489 '_offset_orientation_h!' => 'end',
1490 'position!' => '',
1491 ],
1492 ]
1493 );
1494
1495 $this->add_responsive_control(
1496 '_offset_x_end',
1497 [
1498 'label' => esc_html__( 'Offset', 'elementor' ),
1499 'type' => Controls_Manager::SLIDER,
1500 'range' => [
1501 'px' => [
1502 'min' => -1000,
1503 'max' => 1000,
1504 ],
1505 '%' => [
1506 'min' => -200,
1507 'max' => 200,
1508 ],
1509 'vw' => [
1510 'min' => -200,
1511 'max' => 200,
1512 ],
1513 'vh' => [
1514 'min' => -200,
1515 'max' => 200,
1516 ],
1517 ],
1518 'default' => [
1519 'size' => 0,
1520 ],
1521 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'vh', 'custom' ],
1522 'selectors' => [
1523 'body:not(.rtl) {{WRAPPER}}' => 'right: {{SIZE}}{{UNIT}}',
1524 'body.rtl {{WRAPPER}}' => 'left: {{SIZE}}{{UNIT}}',
1525 ],
1526 'condition' => [
1527 '_offset_orientation_h' => 'end',
1528 'position!' => '',
1529 ],
1530 ]
1531 );
1532
1533 $this->add_control(
1534 '_offset_orientation_v',
1535 [
1536 'label' => esc_html__( 'Vertical Orientation', 'elementor' ),
1537 'type' => Controls_Manager::CHOOSE,
1538 'toggle' => false,
1539 'default' => 'start',
1540 'options' => [
1541 'start' => [
1542 'title' => esc_html__( 'Top', 'elementor' ),
1543 'icon' => 'eicon-v-align-top',
1544 ],
1545 'end' => [
1546 'title' => esc_html__( 'Bottom', 'elementor' ),
1547 'icon' => 'eicon-v-align-bottom',
1548 ],
1549 ],
1550 'render_type' => 'ui',
1551 'condition' => [
1552 'position!' => '',
1553 ],
1554 ]
1555 );
1556
1557 $this->add_responsive_control(
1558 '_offset_y',
1559 [
1560 'label' => esc_html__( 'Offset', 'elementor' ),
1561 'type' => Controls_Manager::SLIDER,
1562 'range' => [
1563 'px' => [
1564 'min' => -1000,
1565 'max' => 1000,
1566 ],
1567 '%' => [
1568 'min' => -200,
1569 'max' => 200,
1570 ],
1571 'vh' => [
1572 'min' => -200,
1573 'max' => 200,
1574 ],
1575 'vw' => [
1576 'min' => -200,
1577 'max' => 200,
1578 ],
1579 ],
1580 'size_units' => [ 'px', '%', 'em', 'rem', 'vh', 'vw', 'custom' ],
1581 'default' => [
1582 'size' => 0,
1583 ],
1584 'selectors' => [
1585 '{{WRAPPER}}' => 'top: {{SIZE}}{{UNIT}}',
1586 ],
1587 'condition' => [
1588 '_offset_orientation_v!' => 'end',
1589 'position!' => '',
1590 ],
1591 ]
1592 );
1593
1594 $this->add_responsive_control(
1595 '_offset_y_end',
1596 [
1597 'label' => esc_html__( 'Offset', 'elementor' ),
1598 'type' => Controls_Manager::SLIDER,
1599 'range' => [
1600 'px' => [
1601 'min' => -1000,
1602 'max' => 1000,
1603 ],
1604 '%' => [
1605 'min' => -200,
1606 'max' => 200,
1607 ],
1608 'vh' => [
1609 'min' => -200,
1610 'max' => 200,
1611 ],
1612 'vw' => [
1613 'min' => -200,
1614 'max' => 200,
1615 ],
1616 ],
1617 'size_units' => [ 'px', '%', 'em', 'rem', 'vh', 'vw', 'custom' ],
1618 'default' => [
1619 'size' => 0,
1620 ],
1621 'selectors' => [
1622 '{{WRAPPER}}' => 'bottom: {{SIZE}}{{UNIT}}',
1623 ],
1624 'condition' => [
1625 '_offset_orientation_v' => 'end',
1626 'position!' => '',
1627 ],
1628 ]
1629 );
1630
1631 $this->add_responsive_control(
1632 'z_index',
1633 [
1634 'label' => esc_html__( 'Z-Index', 'elementor' ),
1635 'type' => Controls_Manager::NUMBER,
1636 'min' => 0,
1637 'selectors' => [
1638 '{{WRAPPER}}' => '--z-index: {{VALUE}};',
1639 ],
1640 ]
1641 );
1642
1643 $this->add_control(
1644 '_element_id',
1645 [
1646 'label' => esc_html__( 'CSS ID', 'elementor' ),
1647 'type' => Controls_Manager::TEXT,
1648 'default' => '',
1649 'ai' => [
1650 'active' => false,
1651 ],
1652 'dynamic' => [
1653 'active' => true,
1654 ],
1655 'title' => esc_html__( 'Add your custom id WITHOUT the Pound key. e.g: my-id', 'elementor' ),
1656 'style_transfer' => false,
1657 'classes' => 'elementor-control-direction-ltr',
1658 ]
1659 );
1660
1661 $this->add_control(
1662 'css_classes',
1663 [
1664 'label' => esc_html__( 'CSS Classes', 'elementor' ),
1665 'type' => Controls_Manager::TEXT,
1666 'default' => '',
1667 'ai' => [
1668 'active' => false,
1669 ],
1670 'dynamic' => [
1671 'active' => true,
1672 ],
1673 'prefix_class' => '',
1674 'title' => esc_html__( 'Add your custom class WITHOUT the dot. e.g: my-class', 'elementor' ),
1675 'classes' => 'elementor-control-direction-ltr',
1676 ]
1677 );
1678
1679 Plugin::$instance->controls_manager->add_display_conditions_controls( $this );
1680
1681 $this->end_controls_section();
1682 }
1683
1684 /**
1685 * Register the Container's motion effects controls.
1686 *
1687 * @return void
1688 */
1689 protected function register_motion_effects_controls() {
1690 $this->start_controls_section(
1691 'section_effects',
1692 [
1693 'label' => esc_html__( 'Motion Effects', 'elementor' ),
1694 'tab' => Controls_Manager::TAB_ADVANCED,
1695 ]
1696 );
1697
1698 Plugin::$instance->controls_manager->add_motion_effects_promotion_control( $this );
1699
1700 $this->add_responsive_control(
1701 'animation',
1702 [
1703 'label' => esc_html__( 'Entrance Animation', 'elementor' ),
1704 'type' => Controls_Manager::ANIMATION,
1705 'frontend_available' => true,
1706 ]
1707 );
1708
1709 $this->add_control(
1710 'animation_duration',
1711 [
1712 'label' => esc_html__( 'Animation Duration', 'elementor' ),
1713 'type' => Controls_Manager::SELECT,
1714 'default' => '',
1715 'options' => [
1716 'slow' => esc_html__( 'Slow', 'elementor' ),
1717 '' => esc_html__( 'Normal', 'elementor' ),
1718 'fast' => esc_html__( 'Fast', 'elementor' ),
1719 ],
1720 'prefix_class' => 'animated-',
1721 'condition' => [
1722 'animation!' => '',
1723 ],
1724 ]
1725 );
1726
1727 $this->add_control(
1728 'animation_delay',
1729 [
1730 'label' => esc_html__( 'Animation Delay', 'elementor' ) . ' (ms)',
1731 'type' => Controls_Manager::NUMBER,
1732 'default' => '',
1733 'min' => 0,
1734 'step' => 100,
1735 'condition' => [
1736 'animation!' => '',
1737 ],
1738 'render_type' => 'none',
1739 'frontend_available' => true,
1740 ]
1741 );
1742
1743 $this->end_controls_section();
1744 }
1745
1746 /**
1747 * Register the Container's responsive controls.
1748 *
1749 * @return void
1750 */
1751 protected function register_responsive_controls() {
1752 $this->start_controls_section(
1753 '_section_responsive',
1754 [
1755 'label' => esc_html__( 'Responsive', 'elementor' ),
1756 'tab' => Controls_Manager::TAB_ADVANCED,
1757 ]
1758 );
1759
1760 $this->add_control(
1761 'heading_visibility',
1762 [
1763 'label' => esc_html__( 'Visibility', 'elementor' ),
1764 'type' => Controls_Manager::HEADING,
1765 ]
1766 );
1767
1768 $this->add_control(
1769 'responsive_description',
1770 [
1771 'raw' => sprintf(
1772 /* translators: 1: Link open tag, 2: Link close tag. */
1773 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' ),
1774 '<a href="javascript: $e.run( \'panel/close\' )">',
1775 '</a>'
1776 ),
1777 'type' => Controls_Manager::RAW_HTML,
1778 'content_classes' => 'elementor-descriptor',
1779 ]
1780 );
1781
1782 $this->add_hidden_device_controls();
1783
1784 $this->end_controls_section();
1785 }
1786
1787 /**
1788 * Register the Container's advanced tab.
1789 *
1790 * @return void
1791 */
1792 protected function register_advanced_tab() {
1793 $this->register_advanced_controls();
1794
1795 $this->register_motion_effects_controls();
1796
1797 $this->hook_sticky_notice_into_transform_section();
1798
1799 $this->register_transform_section( 'con' );
1800
1801 $this->register_responsive_controls();
1802
1803 Plugin::$instance->controls_manager->add_custom_attributes_controls( $this );
1804
1805 Plugin::$instance->controls_manager->add_custom_css_controls( $this );
1806 }
1807
1808 protected function hook_sticky_notice_into_transform_section() {
1809 add_action( 'elementor/element/container/_section_transform/after_section_start', function( Container $container ) {
1810 if ( ! empty( $container->get_controls( 'transform_sticky_notice' ) ) ) {
1811 return;
1812 }
1813
1814 $container->add_control(
1815 'transform_sticky_notice',
1816 [
1817 'type' => Controls_Manager::ALERT,
1818 'alert_type' => 'warning',
1819 'content' => esc_html__( 'Note: Avoid applying transform properties on sticky containers. Doing so might cause unexpected results.', 'elementor' ),
1820 ]
1821 );
1822 } );
1823 }
1824
1825 /**
1826 * Register the Container's controls.
1827 *
1828 * @return void
1829 */
1830 protected function register_controls() {
1831 $this->register_layout_tab();
1832 $this->register_style_tab();
1833 $this->register_advanced_tab();
1834 }
1835
1836 public function on_import( $element ) {
1837 return self::slider_to_gaps_converter( $element );
1838 }
1839
1840 /**
1841 * convert slider to gaps control for the 3.16 upgrade script
1842 *
1843 * @param $element
1844 * @return array
1845 */
1846 public static function slider_to_gaps_converter( $element ) {
1847 $breakpoints = array_keys( (array) Plugin::$instance->breakpoints->get_breakpoints() );
1848 $breakpoints[] = 'desktop';
1849 $control_name = 'flex_gap';
1850
1851 foreach ( $breakpoints as $breakpoint ) {
1852 $control = 'desktop' !== $breakpoint
1853 ? $control_name . '_' . $breakpoint
1854 : $control_name;
1855
1856 if ( ! isset( $element['settings'][ $control ] ) ) {
1857 continue;
1858 }
1859
1860 $already_using_gaps_control = isset( $element['settings'][ $control ]['isLinked'] ); // Slider control won't have the 'isLinked' property.
1861
1862 if ( ! $already_using_gaps_control ) {
1863 $old_size = strval( $element['settings'][ $control ]['size'] );
1864
1865 $element['settings'][ $control ]['column'] = $old_size;
1866 $element['settings'][ $control ]['row'] = $old_size;
1867 $element['settings'][ $control ]['isLinked'] = true;
1868 }
1869 }
1870
1871 return $element;
1872 }
1873 }
1874