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

1,885 lines 45.4 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->add_control(
698 'handle_slideshow_asset_loading',
699 [
700 'type' => Controls_Manager::HIDDEN,
701 'assets' => [
702 'styles' => [
703 [
704 'name' => 'e-swiper',
705 'conditions' => [
706 'terms' => [
707 [
708 'name' => 'background_background',
709 'operator' => '===',
710 'value' => 'slideshow',
711 ],
712 ],
713 ],
714 ],
715 ],
716 ],
717 ]
718 );
719
720 $this->end_controls_tab();
721
722 /**
723 * Hover.
724 */
725 $this->start_controls_tab(
726 'tab_background_hover',
727 [
728 'label' => esc_html__( 'Hover', 'elementor' ),
729 ]
730 );
731
732 $this->add_group_control(
733 Group_Control_Background::get_type(),
734 [
735 'name' => 'background_hover',
736 'selector' => '{{WRAPPER}}:hover',
737 ]
738 );
739
740 $this->add_control(
741 'background_hover_transition',
742 [
743 'label' => esc_html__( 'Transition Duration', 'elementor' ) . ' (s)',
744 'type' => Controls_Manager::SLIDER,
745 'default' => [
746 'size' => 0.3,
747 ],
748 'range' => [
749 'px' => [
750 'min' => 0,
751 'max' => 3,
752 'step' => 0.1,
753 ],
754 ],
755 'render_type' => 'ui',
756 'separator' => 'before',
757 'selectors' => [
758 '{{WRAPPER}}' => '--background-transition: {{SIZE}}s;',
759 ],
760 ]
761 );
762
763 $this->end_controls_tab();
764
765 $this->end_controls_tabs();
766
767 $this->end_controls_section();
768 }
769
770 /**
771 * Register the Container's background overlay controls.
772 *
773 * @return void
774 */
775 protected function register_background_overlay_controls() {
776 $this->start_controls_section(
777 'section_background_overlay',
778 [
779 'label' => esc_html__( 'Background Overlay', 'elementor' ),
780 'tab' => Controls_Manager::TAB_STYLE,
781 ]
782 );
783
784 $this->start_controls_tabs( 'tabs_background_overlay' );
785
786 /**
787 * Normal.
788 */
789 $this->start_controls_tab(
790 'tab_background_overlay',
791 [
792 'label' => esc_html__( 'Normal', 'elementor' ),
793 ]
794 );
795
796 $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';
797
798 $this->add_group_control(
799 Group_Control_Background::get_type(),
800 [
801 'name' => 'background_overlay',
802 'selector' => $background_overlay_selector,
803 'fields_options' => [
804 'background' => [
805 'selectors' => [
806 // Hack to set the `::before` content in order to render it only when there is a background overlay.
807 $background_overlay_selector => '--background-overlay: \'\';',
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-top-width: {{TOP}}{{UNIT}}; --border-right-width: {{RIGHT}}{{UNIT}}; --border-bottom-width: {{BOTTOM}}{{UNIT}}; --border-left-width: {{LEFT}}{{UNIT}};',
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-top-width: {{TOP}}{{UNIT}}; --border-right-width: {{RIGHT}}{{UNIT}}; --border-bottom-width: {{BOTTOM}}{{UNIT}}; --border-left-width: {{LEFT}}{{UNIT}};',
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: {{RIGHT}}{{UNIT}}; --border-bottom-right-radius: {{BOTTOM}}{{UNIT}}; --border-bottom-left-radius: {{LEFT}}{{UNIT}};',
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 'assets' => [
1199 'styles' => [
1200 [
1201 'name' => 'e-shapes',
1202 'conditions' => [
1203 'terms' => [
1204 [
1205 'name' => $base_control_key,
1206 'operator' => '!==',
1207 'value' => '',
1208 ],
1209 ],
1210 ],
1211 ],
1212 ],
1213 ],
1214 ]
1215 );
1216
1217 $this->add_control(
1218 $base_control_key . '_color',
1219 [
1220 'label' => esc_html__( 'Color', 'elementor' ),
1221 'type' => Controls_Manager::COLOR,
1222 'condition' => [
1223 "shape_divider_$side!" => '',
1224 ],
1225 'selectors' => [
1226 "{{WRAPPER}} > .elementor-shape-$side .elementor-shape-fill, {{WRAPPER}} > .e-con-inner > .elementor-shape-$side .elementor-shape-fill" => 'fill: {{UNIT}};',
1227 ],
1228 ]
1229 );
1230
1231 $this->add_responsive_control(
1232 $base_control_key . '_width',
1233 [
1234 'label' => esc_html__( 'Width', 'elementor' ),
1235 'type' => Controls_Manager::SLIDER,
1236 'size_units' => [ '%', 'vw', 'custom' ],
1237 'default' => [
1238 'unit' => '%',
1239 ],
1240 'tablet_default' => [
1241 'unit' => '%',
1242 ],
1243 'mobile_default' => [
1244 'unit' => '%',
1245 ],
1246 'range' => [
1247 '%' => [
1248 'min' => 100,
1249 'max' => 300,
1250 ],
1251 'vw' => [
1252 'min' => 100,
1253 'max' => 300,
1254 ],
1255 ],
1256 'condition' => [
1257 "shape_divider_$side" => array_keys( Shapes::filter_shapes( 'height_only', Shapes::FILTER_EXCLUDE ) ),
1258 ],
1259 'selectors' => [
1260 "{{WRAPPER}} > .elementor-shape-$side svg, {{WRAPPER}} > .e-con-inner > .elementor-shape-$side svg" => 'width: calc({{SIZE}}{{UNIT}} + 1.3px)',
1261 ],
1262 ]
1263 );
1264
1265 $this->add_responsive_control(
1266 $base_control_key . '_height',
1267 [
1268 'label' => esc_html__( 'Height', 'elementor' ),
1269 'type' => Controls_Manager::SLIDER,
1270 'size_units' => [ 'px', 'em', 'rem', 'custom' ],
1271 'range' => [
1272 'px' => [
1273 'max' => 500,
1274 ],
1275 'em' => [
1276 'max' => 50,
1277 ],
1278 'rem' => [
1279 'max' => 50,
1280 ],
1281 ],
1282 'condition' => [
1283 "shape_divider_$side!" => '',
1284 ],
1285 'selectors' => [
1286 "{{WRAPPER}} > .elementor-shape-$side svg, {{WRAPPER}} > .e-con-inner > .elementor-shape-$side svg" => 'height: {{SIZE}}{{UNIT}};',
1287 ],
1288 ]
1289 );
1290
1291 $this->add_control(
1292 $base_control_key . '_flip',
1293 [
1294 'label' => esc_html__( 'Flip', 'elementor' ),
1295 'type' => Controls_Manager::SWITCHER,
1296 'condition' => [
1297 "shape_divider_$side" => array_keys( Shapes::filter_shapes( 'has_flip' ) ),
1298 ],
1299 'selectors' => [
1300 "{{WRAPPER}} > .elementor-shape-$side svg, {{WRAPPER}} > .e-con-inner > .elementor-shape-$side svg" => 'transform: translateX(-50%) rotateY(180deg)',
1301 ],
1302 ]
1303 );
1304
1305 $this->add_control(
1306 $base_control_key . '_negative',
1307 [
1308 'label' => esc_html__( 'Invert', 'elementor' ),
1309 'type' => Controls_Manager::SWITCHER,
1310 'frontend_available' => true,
1311 'condition' => [
1312 "shape_divider_$side" => array_keys( Shapes::filter_shapes( 'has_negative' ) ),
1313 ],
1314 'render_type' => 'none',
1315 ]
1316 );
1317
1318 $this->add_control(
1319 $base_control_key . '_above_content',
1320 [
1321 'label' => esc_html__( 'Bring to Front', 'elementor' ),
1322 'type' => Controls_Manager::SWITCHER,
1323 'selectors' => [
1324 "{{WRAPPER}} > .elementor-shape-$side, {{WRAPPER}} > .e-con-inner > .elementor-shape-$side" => 'z-index: 2; pointer-events: none',
1325 ],
1326 'condition' => [
1327 "shape_divider_$side!" => '',
1328 ],
1329 ]
1330 );
1331
1332 $this->end_controls_tab();
1333 }
1334
1335 $this->end_controls_tabs();
1336
1337 $this->end_controls_section();
1338 }
1339 /**
1340 * Register the Container's style tab.
1341 *
1342 * @return void
1343 */
1344 protected function register_style_tab() {
1345 $this->register_background_controls();
1346
1347 $this->register_background_overlay_controls();
1348
1349 $this->register_border_controls();
1350
1351 $this->register_shape_dividers_controls();
1352 }
1353
1354 /**
1355 * Register the Container's advanced style controls.
1356 *
1357 * @return void
1358 */
1359 protected function register_advanced_controls() {
1360 $this->start_controls_section(
1361 'section_layout',
1362 [
1363 'label' => esc_html__( 'Layout', 'elementor' ),
1364 'tab' => Controls_Manager::TAB_ADVANCED,
1365 ]
1366 );
1367
1368 $this->add_responsive_control(
1369 'margin',
1370 [
1371 'label' => esc_html__( 'Margin', 'elementor' ),
1372 'type' => Controls_Manager::DIMENSIONS,
1373 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
1374 'selectors' => [
1375 '{{WRAPPER}}' => '--margin-top: {{TOP}}{{UNIT}}; --margin-bottom: {{BOTTOM}}{{UNIT}}; --margin-left: {{LEFT}}{{UNIT}}; --margin-right: {{RIGHT}}{{UNIT}};',
1376 ],
1377 ]
1378 );
1379
1380 $this->add_responsive_control(
1381 'padding',
1382 [
1383 'label' => esc_html__( 'Padding', 'elementor' ),
1384 'type' => Controls_Manager::DIMENSIONS,
1385 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
1386 'selectors' => [
1387 '{{WRAPPER}}' => '--padding-top: {{TOP}}{{UNIT}}; --padding-bottom: {{BOTTOM}}{{UNIT}}; --padding-left: {{LEFT}}{{UNIT}}; --padding-right: {{RIGHT}}{{UNIT}};',
1388 ],
1389 ]
1390 );
1391
1392 $this->add_group_control(
1393 Group_Control_Flex_Item::get_type(),
1394 [
1395 'name' => '_flex',
1396 'include' => [
1397 'align_self',
1398 'order',
1399 'order_custom',
1400 'size',
1401 'grow',
1402 'shrink',
1403 ],
1404 'selector' => '{{WRAPPER}}.e-con', // Hack to increase specificity.
1405 'separator' => 'before',
1406 ]
1407 );
1408
1409 $this->add_control(
1410 'position_description',
1411 [
1412 'type' => Controls_Manager::ALERT,
1413 'alert_type' => 'warning',
1414 'heading' => esc_html__( 'Please note!', 'elementor' ),
1415 'content' => esc_html__( 'Custom positioning is not considered best practice for responsive web design and should not be used too frequently.', 'elementor' ),
1416 'render_type' => 'ui',
1417 'condition' => [
1418 'position!' => '',
1419 ],
1420 ]
1421 );
1422
1423 // TODO: Copied from `common.php` - Extract to group control.
1424 $this->add_control(
1425 'position',
1426 [
1427 'label' => esc_html__( 'Position', 'elementor' ),
1428 'type' => Controls_Manager::SELECT,
1429 'default' => '',
1430 'options' => [
1431 '' => esc_html__( 'Default', 'elementor' ),
1432 'absolute' => esc_html__( 'Absolute', 'elementor' ),
1433 'fixed' => esc_html__( 'Fixed', 'elementor' ),
1434 ],
1435 'selectors' => [
1436 '{{WRAPPER}}' => '--position: {{VALUE}};',
1437 ],
1438 'frontend_available' => true,
1439 'separator' => 'before',
1440 ]
1441 );
1442
1443 $left = esc_html__( 'Left', 'elementor' );
1444 $right = esc_html__( 'Right', 'elementor' );
1445
1446 $start = is_rtl() ? $right : $left;
1447 $end = ! is_rtl() ? $right : $left;
1448
1449 $this->add_control(
1450 '_offset_orientation_h',
1451 [
1452 'label' => esc_html__( 'Horizontal Orientation', 'elementor' ),
1453 'type' => Controls_Manager::CHOOSE,
1454 'toggle' => false,
1455 'default' => 'start',
1456 'options' => [
1457 'start' => [
1458 'title' => $start,
1459 'icon' => 'eicon-h-align-left',
1460 ],
1461 'end' => [
1462 'title' => $end,
1463 'icon' => 'eicon-h-align-right',
1464 ],
1465 ],
1466 'classes' => 'elementor-control-start-end',
1467 'render_type' => 'ui',
1468 'condition' => [
1469 'position!' => '',
1470 ],
1471 ]
1472 );
1473
1474 $this->add_responsive_control(
1475 '_offset_x',
1476 [
1477 'label' => esc_html__( 'Offset', 'elementor' ),
1478 'type' => Controls_Manager::SLIDER,
1479 'range' => [
1480 'px' => [
1481 'min' => -1000,
1482 'max' => 1000,
1483 ],
1484 '%' => [
1485 'min' => -200,
1486 'max' => 200,
1487 ],
1488 'vw' => [
1489 'min' => -200,
1490 'max' => 200,
1491 ],
1492 'vh' => [
1493 'min' => -200,
1494 'max' => 200,
1495 ],
1496 ],
1497 'default' => [
1498 'size' => 0,
1499 ],
1500 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'vh', 'custom' ],
1501 'selectors' => [
1502 'body:not(.rtl) {{WRAPPER}}' => 'left: {{SIZE}}{{UNIT}}',
1503 'body.rtl {{WRAPPER}}' => 'right: {{SIZE}}{{UNIT}}',
1504 ],
1505 'condition' => [
1506 '_offset_orientation_h!' => 'end',
1507 'position!' => '',
1508 ],
1509 ]
1510 );
1511
1512 $this->add_responsive_control(
1513 '_offset_x_end',
1514 [
1515 'label' => esc_html__( 'Offset', 'elementor' ),
1516 'type' => Controls_Manager::SLIDER,
1517 'range' => [
1518 'px' => [
1519 'min' => -1000,
1520 'max' => 1000,
1521 ],
1522 '%' => [
1523 'min' => -200,
1524 'max' => 200,
1525 ],
1526 'vw' => [
1527 'min' => -200,
1528 'max' => 200,
1529 ],
1530 'vh' => [
1531 'min' => -200,
1532 'max' => 200,
1533 ],
1534 ],
1535 'default' => [
1536 'size' => 0,
1537 ],
1538 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'vh', 'custom' ],
1539 'selectors' => [
1540 'body:not(.rtl) {{WRAPPER}}' => 'right: {{SIZE}}{{UNIT}}',
1541 'body.rtl {{WRAPPER}}' => 'left: {{SIZE}}{{UNIT}}',
1542 ],
1543 'condition' => [
1544 '_offset_orientation_h' => 'end',
1545 'position!' => '',
1546 ],
1547 ]
1548 );
1549
1550 $this->add_control(
1551 '_offset_orientation_v',
1552 [
1553 'label' => esc_html__( 'Vertical Orientation', 'elementor' ),
1554 'type' => Controls_Manager::CHOOSE,
1555 'toggle' => false,
1556 'default' => 'start',
1557 'options' => [
1558 'start' => [
1559 'title' => esc_html__( 'Top', 'elementor' ),
1560 'icon' => 'eicon-v-align-top',
1561 ],
1562 'end' => [
1563 'title' => esc_html__( 'Bottom', 'elementor' ),
1564 'icon' => 'eicon-v-align-bottom',
1565 ],
1566 ],
1567 'render_type' => 'ui',
1568 'condition' => [
1569 'position!' => '',
1570 ],
1571 ]
1572 );
1573
1574 $this->add_responsive_control(
1575 '_offset_y',
1576 [
1577 'label' => esc_html__( 'Offset', 'elementor' ),
1578 'type' => Controls_Manager::SLIDER,
1579 'range' => [
1580 'px' => [
1581 'min' => -1000,
1582 'max' => 1000,
1583 ],
1584 '%' => [
1585 'min' => -200,
1586 'max' => 200,
1587 ],
1588 'vh' => [
1589 'min' => -200,
1590 'max' => 200,
1591 ],
1592 'vw' => [
1593 'min' => -200,
1594 'max' => 200,
1595 ],
1596 ],
1597 'size_units' => [ 'px', '%', 'em', 'rem', 'vh', 'vw', 'custom' ],
1598 'default' => [
1599 'size' => 0,
1600 ],
1601 'selectors' => [
1602 '{{WRAPPER}}' => 'top: {{SIZE}}{{UNIT}}',
1603 ],
1604 'condition' => [
1605 '_offset_orientation_v!' => 'end',
1606 'position!' => '',
1607 ],
1608 ]
1609 );
1610
1611 $this->add_responsive_control(
1612 '_offset_y_end',
1613 [
1614 'label' => esc_html__( 'Offset', 'elementor' ),
1615 'type' => Controls_Manager::SLIDER,
1616 'range' => [
1617 'px' => [
1618 'min' => -1000,
1619 'max' => 1000,
1620 ],
1621 '%' => [
1622 'min' => -200,
1623 'max' => 200,
1624 ],
1625 'vh' => [
1626 'min' => -200,
1627 'max' => 200,
1628 ],
1629 'vw' => [
1630 'min' => -200,
1631 'max' => 200,
1632 ],
1633 ],
1634 'size_units' => [ 'px', '%', 'em', 'rem', 'vh', 'vw', 'custom' ],
1635 'default' => [
1636 'size' => 0,
1637 ],
1638 'selectors' => [
1639 '{{WRAPPER}}' => 'bottom: {{SIZE}}{{UNIT}}',
1640 ],
1641 'condition' => [
1642 '_offset_orientation_v' => 'end',
1643 'position!' => '',
1644 ],
1645 ]
1646 );
1647
1648 $this->add_responsive_control(
1649 'z_index',
1650 [
1651 'label' => esc_html__( 'Z-Index', 'elementor' ),
1652 'type' => Controls_Manager::NUMBER,
1653 'min' => 0,
1654 'selectors' => [
1655 '{{WRAPPER}}' => '--z-index: {{VALUE}};',
1656 ],
1657 ]
1658 );
1659
1660 $this->add_control(
1661 '_element_id',
1662 [
1663 'label' => esc_html__( 'CSS ID', 'elementor' ),
1664 'type' => Controls_Manager::TEXT,
1665 'default' => '',
1666 'ai' => [
1667 'active' => false,
1668 ],
1669 'dynamic' => [
1670 'active' => true,
1671 ],
1672 'title' => esc_html__( 'Add your custom id WITHOUT the Pound key. e.g: my-id', 'elementor' ),
1673 'style_transfer' => false,
1674 'classes' => 'elementor-control-direction-ltr',
1675 ]
1676 );
1677
1678 $this->add_control(
1679 'css_classes',
1680 [
1681 'label' => esc_html__( 'CSS Classes', 'elementor' ),
1682 'type' => Controls_Manager::TEXT,
1683 'default' => '',
1684 'ai' => [
1685 'active' => false,
1686 ],
1687 'dynamic' => [
1688 'active' => true,
1689 ],
1690 'prefix_class' => '',
1691 'title' => esc_html__( 'Add your custom class WITHOUT the dot. e.g: my-class', 'elementor' ),
1692 'classes' => 'elementor-control-direction-ltr',
1693 ]
1694 );
1695
1696 Plugin::$instance->controls_manager->add_display_conditions_controls( $this );
1697
1698 $this->end_controls_section();
1699 }
1700
1701 /**
1702 * Register the Container's motion effects controls.
1703 *
1704 * @return void
1705 */
1706 protected function register_motion_effects_controls() {
1707 $this->start_controls_section(
1708 'section_effects',
1709 [
1710 'label' => esc_html__( 'Motion Effects', 'elementor' ),
1711 'tab' => Controls_Manager::TAB_ADVANCED,
1712 ]
1713 );
1714
1715 Plugin::$instance->controls_manager->add_motion_effects_promotion_control( $this );
1716
1717 $this->add_responsive_control(
1718 'animation',
1719 [
1720 'label' => esc_html__( 'Entrance Animation', 'elementor' ),
1721 'type' => Controls_Manager::ANIMATION,
1722 'frontend_available' => true,
1723 ]
1724 );
1725
1726 $this->add_control(
1727 'animation_duration',
1728 [
1729 'label' => esc_html__( 'Animation Duration', 'elementor' ),
1730 'type' => Controls_Manager::SELECT,
1731 'default' => '',
1732 'options' => [
1733 'slow' => esc_html__( 'Slow', 'elementor' ),
1734 '' => esc_html__( 'Normal', 'elementor' ),
1735 'fast' => esc_html__( 'Fast', 'elementor' ),
1736 ],
1737 'prefix_class' => 'animated-',
1738 'condition' => [
1739 'animation!' => '',
1740 ],
1741 ]
1742 );
1743
1744 $this->add_control(
1745 'animation_delay',
1746 [
1747 'label' => esc_html__( 'Animation Delay', 'elementor' ) . ' (ms)',
1748 'type' => Controls_Manager::NUMBER,
1749 'default' => '',
1750 'min' => 0,
1751 'step' => 100,
1752 'condition' => [
1753 'animation!' => '',
1754 ],
1755 'render_type' => 'none',
1756 'frontend_available' => true,
1757 ]
1758 );
1759
1760 $this->end_controls_section();
1761 }
1762
1763 /**
1764 * Register the Container's responsive controls.
1765 *
1766 * @return void
1767 */
1768 protected function register_responsive_controls() {
1769 $this->start_controls_section(
1770 '_section_responsive',
1771 [
1772 'label' => esc_html__( 'Responsive', 'elementor' ),
1773 'tab' => Controls_Manager::TAB_ADVANCED,
1774 ]
1775 );
1776
1777 $this->add_control(
1778 'heading_visibility',
1779 [
1780 'label' => esc_html__( 'Visibility', 'elementor' ),
1781 'type' => Controls_Manager::HEADING,
1782 ]
1783 );
1784
1785 $this->add_control(
1786 'responsive_description',
1787 [
1788 'raw' => sprintf(
1789 /* translators: 1: Link open tag, 2: Link close tag. */
1790 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' ),
1791 '<a href="javascript: $e.run( \'panel/close\' )">',
1792 '</a>'
1793 ),
1794 'type' => Controls_Manager::RAW_HTML,
1795 'content_classes' => 'elementor-descriptor',
1796 ]
1797 );
1798
1799 $this->add_hidden_device_controls();
1800
1801 $this->end_controls_section();
1802 }
1803
1804 /**
1805 * Register the Container's advanced tab.
1806 *
1807 * @return void
1808 */
1809 protected function register_advanced_tab() {
1810 $this->register_advanced_controls();
1811
1812 $this->register_motion_effects_controls();
1813
1814 $this->hook_sticky_notice_into_transform_section();
1815
1816 $this->register_transform_section( 'con' );
1817
1818 $this->register_responsive_controls();
1819
1820 Plugin::$instance->controls_manager->add_custom_attributes_controls( $this );
1821
1822 Plugin::$instance->controls_manager->add_custom_css_controls( $this );
1823 }
1824
1825 protected function hook_sticky_notice_into_transform_section() {
1826 add_action( 'elementor/element/container/_section_transform/after_section_start', function( Container $container ) {
1827 if ( ! empty( $container->get_controls( 'transform_sticky_notice' ) ) ) {
1828 return;
1829 }
1830
1831 $container->add_control(
1832 'transform_sticky_notice',
1833 [
1834 'type' => Controls_Manager::ALERT,
1835 'alert_type' => 'warning',
1836 'content' => esc_html__( 'Note: Avoid applying transform properties on sticky containers. Doing so might cause unexpected results.', 'elementor' ),
1837 ]
1838 );
1839 } );
1840 }
1841
1842 /**
1843 * Register the Container's controls.
1844 *
1845 * @return void
1846 */
1847 protected function register_controls() {
1848 $this->register_layout_tab();
1849 $this->register_style_tab();
1850 $this->register_advanced_tab();
1851 }
1852
1853 public function on_import( $element ) {
1854 return self::slider_to_gaps_converter( $element );
1855 }
1856
1857 /**
1858 * convert slider to gaps control for the 3.16 upgrade script
1859 *
1860 * @param $element
1861 * @return array
1862 */
1863 public static function slider_to_gaps_converter( $element ) {
1864 $breakpoints = array_keys( (array) Plugin::$instance->breakpoints->get_breakpoints() );
1865 $breakpoints[] = 'desktop';
1866 $control_name = 'flex_gap';
1867
1868 foreach ( $breakpoints as $breakpoint ) {
1869 $control = 'desktop' !== $breakpoint
1870 ? $control_name . '_' . $breakpoint
1871 : $control_name;
1872
1873 if ( isset( $element['settings'][ $control ] ) ) {
1874 $old_size = strval( $element['settings'][ $control ]['size'] );
1875
1876 $element['settings'][ $control ]['column'] = $old_size;
1877 $element['settings'][ $control ]['row'] = $old_size;
1878 $element['settings'][ $control ]['isLinked'] = true;
1879 }
1880 }
1881
1882 return $element;
1883 }
1884 }
1885