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

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

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