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

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