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

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