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

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