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

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