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

1,595 lines 36.2 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 $min_affected_device = Breakpoints_Manager::BREAKPOINT_KEY_TABLET;
293
294 $this->add_control(
295 'content_width',
296 [
297 'label' => esc_html__( 'Content Width', 'elementor' ),
298 'type' => Controls_Manager::SELECT,
299 'default' => 'boxed',
300 'options' => [
301 'boxed' => esc_html__( 'Boxed', 'elementor' ),
302 'full' => esc_html__( 'Full Width', 'elementor' ),
303 ],
304 'render_type' => 'ui',
305 'selectors' => [
306 '{{WRAPPER}}' => '{{VALUE}}',
307 ],
308 'selectors_dictionary' => [
309 'boxed' => '',
310 'full' => '--content-width: 100%;',
311 ],
312 ]
313 );
314
315 $width_control_settings = [
316 'label' => esc_html__( 'Width', 'elementor' ),
317 'type' => Controls_Manager::SLIDER,
318 'size_units' => [ 'px', '%', 'vw' ],
319 'range' => [
320 'px' => [
321 'min' => 500,
322 'max' => 1600,
323 ],
324 '%' => [
325 'min' => 0,
326 'max' => 100,
327 ],
328 'vw' => [
329 'min' => 0,
330 'max' => 100,
331 ],
332 ],
333 'default' => [
334 'unit' => '%',
335 ],
336 'min_affected_device' => [
337 Breakpoints_Manager::BREAKPOINT_KEY_DESKTOP => $min_affected_device,
338 Breakpoints_Manager::BREAKPOINT_KEY_LAPTOP => $min_affected_device,
339 Breakpoints_Manager::BREAKPOINT_KEY_TABLET_EXTRA => $min_affected_device,
340 Breakpoints_Manager::BREAKPOINT_KEY_TABLET => $min_affected_device,
341 ],
342 'separator' => 'none',
343 ];
344
345 $this->add_responsive_control(
346 'width',
347 array_merge( $width_control_settings, [
348 'selectors' => [
349 '{{WRAPPER}}' => '--width: {{SIZE}}{{UNIT}};',
350 ],
351 'condition' => [
352 'content_width' => 'full',
353 ],
354 'placeholder' => [
355 'size' => '100',
356 'unit' => '%',
357 ],
358 ] )
359 );
360
361 $this->add_responsive_control(
362 'boxed_width',
363 array_merge( $width_control_settings, [
364 'selectors' => [
365 '{{WRAPPER}}' => '--content-width: {{SIZE}}{{UNIT}};',
366 ],
367 'condition' => [
368 'content_width' => 'boxed',
369 ],
370 'default' => [
371 'unit' => 'px',
372 ],
373 'device_args' => [
374 Breakpoints_Manager::BREAKPOINT_KEY_DESKTOP => [
375 // Use the default width from the kit as a placeholder.
376 'placeholder' => $this->active_kit->get_settings_for_display( 'container_width' ),
377 ],
378 ],
379 ] )
380 );
381
382 $this->add_responsive_control(
383 'min_height',
384 [
385 'label' => esc_html__( 'Min Height', 'elementor' ),
386 'type' => Controls_Manager::SLIDER,
387 'size_units' => [ 'px', 'vh' ],
388 'range' => [
389 'px' => [
390 'min' => 0,
391 'max' => 1440,
392 ],
393 'vh' => [
394 'min' => 0,
395 'max' => 100,
396 ],
397 ],
398 'description' => sprintf(
399 esc_html__( 'To achieve full height Container use %s.', 'elementor' ),
400 '100vh'
401 ),
402 'selectors' => [
403 '{{WRAPPER}}' => '--min-height: {{SIZE}}{{UNIT}};',
404 ],
405 ]
406 );
407
408 $this->add_group_control(
409 Group_Control_Flex_Container::get_type(),
410 [
411 'name' => 'flex',
412 'selector' => '{{WRAPPER}}',
413 'fields_options' => [
414 'gap' => [
415 'label' => esc_html_x( 'Gap between elements', 'Flex Container Control', 'elementor' ),
416 // Use the default "elements gap" from the kit as a placeholder.
417 'placeholder' => $this->active_kit->get_settings_for_display( 'space_between_widgets' ),
418 ],
419 ],
420 ]
421 );
422
423 $this->end_controls_section();
424 }
425
426 /**
427 * Register the Container's items layout controls.
428 *
429 * @return void
430 */
431 protected function register_items_layout_controls() {
432 $this->start_controls_section(
433 'section_layout_additional_options',
434 [
435 'label' => esc_html__( 'Additional Options', 'elementor' ),
436 'tab' => Controls_Manager::TAB_LAYOUT,
437 ]
438 );
439
440 $this->add_control(
441 'overflow',
442 [
443 'label' => esc_html__( 'Overflow', 'elementor' ),
444 'type' => Controls_Manager::SELECT,
445 'default' => '',
446 'options' => [
447 '' => esc_html__( 'Default', 'elementor' ),
448 'hidden' => esc_html__( 'Hidden', 'elementor' ),
449 'auto' => esc_html__( 'Auto', 'elementor' ),
450 ],
451 'selectors' => [
452 '{{WRAPPER}}' => '--overflow: {{VALUE}}',
453 ],
454 ]
455 );
456
457 $possible_tags = [
458 'div' => 'div',
459 'header' => 'header',
460 'footer' => 'footer',
461 'main' => 'main',
462 'article' => 'article',
463 'section' => 'section',
464 'aside' => 'aside',
465 'nav' => 'nav',
466 'a' => 'a',
467 ];
468
469 $options = [
470 '' => esc_html__( 'Default', 'elementor' ),
471 ] + $possible_tags;
472
473 $this->add_control(
474 'html_tag',
475 [
476 'label' => esc_html__( 'HTML Tag', 'elementor' ),
477 'type' => Controls_Manager::SELECT,
478 'options' => $options,
479 ]
480 );
481
482 $this->add_control(
483 'link_note',
484 [
485 'type' => Controls_Manager::RAW_HTML,
486 'content_classes' => 'elementor-panel-alert elementor-panel-alert-warning',
487 'raw' => esc_html__( 'Don’t add links to elements nested in this container - it will break the layout.', 'elementor' ),
488 'condition' => [
489 'html_tag' => 'a',
490 ],
491 ]
492 );
493
494 $this->add_control(
495 'link',
496 [
497 'label' => esc_html__( 'Link', 'elementor' ),
498 'type' => Controls_Manager::URL,
499 'dynamic' => [
500 'active' => true,
501 ],
502 'placeholder' => esc_html__( 'https://your-link.com', 'elementor' ),
503 'condition' => [
504 'html_tag' => 'a',
505 ],
506 ]
507 );
508
509 $this->end_controls_section();
510 }
511
512 /**
513 * Register the Container's layout tab.
514 *
515 * @return void
516 */
517 protected function register_layout_tab() {
518 $this->register_container_layout_controls();
519
520 $this->register_items_layout_controls();
521 }
522
523 /**
524 * Register the Container's background controls.
525 *
526 * @return void
527 */
528 protected function register_background_controls() {
529 $this->start_controls_section(
530 'section_background',
531 [
532 'label' => esc_html__( 'Background', 'elementor' ),
533 'tab' => Controls_Manager::TAB_STYLE,
534 ]
535 );
536
537 $this->start_controls_tabs( 'tabs_background' );
538
539 /**
540 * Normal.
541 */
542 $this->start_controls_tab(
543 'tab_background_normal',
544 [
545 'label' => esc_html__( 'Normal', 'elementor' ),
546 ]
547 );
548
549 $this->add_group_control(
550 Group_Control_Background::get_type(),
551 [
552 'name' => 'background',
553 'types' => [ 'classic', 'gradient', 'video', 'slideshow' ],
554 'fields_options' => [
555 'background' => [
556 'frontend_available' => true,
557 ],
558 ],
559 ]
560 );
561
562 $this->end_controls_tab();
563
564 /**
565 * Hover.
566 */
567 $this->start_controls_tab(
568 'tab_background_hover',
569 [
570 'label' => esc_html__( 'Hover', 'elementor' ),
571 ]
572 );
573
574 $this->add_group_control(
575 Group_Control_Background::get_type(),
576 [
577 'name' => 'background_hover',
578 'selector' => '{{WRAPPER}}:hover',
579 ]
580 );
581
582 $this->add_control(
583 'background_hover_transition',
584 [
585 'label' => esc_html__( 'Transition Duration', 'elementor' ),
586 'type' => Controls_Manager::SLIDER,
587 'default' => [
588 'size' => 0.3,
589 ],
590 'range' => [
591 'px' => [
592 'max' => 3,
593 'step' => 0.1,
594 ],
595 ],
596 'render_type' => 'ui',
597 'separator' => 'before',
598 ]
599 );
600
601 $this->end_controls_tab();
602
603 $this->end_controls_tabs();
604
605 $this->end_controls_section();
606 }
607
608 /**
609 * Register the Container's background overlay controls.
610 *
611 * @return void
612 */
613 protected function register_background_overlay_controls() {
614 $this->start_controls_section(
615 'section_background_overlay',
616 [
617 'label' => esc_html__( 'Background Overlay', 'elementor' ),
618 'tab' => Controls_Manager::TAB_STYLE,
619 ]
620 );
621
622 $this->start_controls_tabs( 'tabs_background_overlay' );
623
624 /**
625 * Normal.
626 */
627 $this->start_controls_tab(
628 'tab_background_overlay',
629 [
630 'label' => esc_html__( 'Normal', 'elementor' ),
631 ]
632 );
633
634 $this->add_group_control(
635 Group_Control_Background::get_type(),
636 [
637 'name' => 'background_overlay',
638 'selector' => '{{WRAPPER}}::before',
639 'fields_options' => [
640 'background' => [
641 'selectors' => [
642 // Hack to set the `::before` content in order to render it only when there is a background overlay.
643 '{{WRAPPER}}::before' => '--background-overlay: \'\';',
644 ],
645 ],
646 ],
647 ]
648 );
649
650 $this->add_control(
651 'background_overlay_opacity',
652 [
653 'label' => esc_html__( 'Opacity', 'elementor' ),
654 'type' => Controls_Manager::SLIDER,
655 'default' => [
656 'size' => .5,
657 ],
658 'range' => [
659 'px' => [
660 'max' => 1,
661 'step' => 0.01,
662 ],
663 ],
664 'selectors' => [
665 '{{WRAPPER}}' => '--overlay-opacity: {{SIZE}};',
666 ],
667 'condition' => [
668 'background_overlay_background' => [ 'classic', 'gradient' ],
669 ],
670 ]
671 );
672
673 $this->add_group_control(
674 Group_Control_Css_Filter::get_type(),
675 [
676 'name' => 'css_filters',
677 'selector' => '{{WRAPPER}}::before',
678 'conditions' => [
679 'relation' => 'or',
680 'terms' => [
681 [
682 'name' => 'background_overlay_image[url]',
683 'operator' => '!==',
684 'value' => '',
685 ],
686 [
687 'name' => 'background_overlay_color',
688 'operator' => '!==',
689 'value' => '',
690 ],
691 ],
692 ],
693 ]
694 );
695
696 $this->add_control(
697 'overlay_blend_mode',
698 [
699 'label' => esc_html__( 'Blend Mode', 'elementor' ),
700 'type' => Controls_Manager::SELECT,
701 'options' => [
702 '' => esc_html__( 'Normal', 'elementor' ),
703 'multiply' => esc_html__( 'Multiply', 'elementor' ),
704 'screen' => esc_html__( 'Screen', 'elementor' ),
705 'overlay' => esc_html__( 'Overlay', 'elementor' ),
706 'darken' => esc_html__( 'Darken', 'elementor' ),
707 'lighten' => esc_html__( 'Lighten', 'elementor' ),
708 'color-dodge' => esc_html__( 'Color Dodge', 'elementor' ),
709 'saturation' => esc_html__( 'Saturation', 'elementor' ),
710 'color' => esc_html__( 'Color', 'elementor' ),
711 'luminosity' => esc_html__( 'Luminosity', 'elementor' ),
712 ],
713 'selectors' => [
714 '{{WRAPPER}}' => '--overlay-mix-blend-mode: {{VALUE}}',
715 ],
716 'conditions' => [
717 'relation' => 'or',
718 'terms' => [
719 [
720 'name' => 'background_overlay_image[url]',
721 'operator' => '!==',
722 'value' => '',
723 ],
724 [
725 'name' => 'background_overlay_color',
726 'operator' => '!==',
727 'value' => '',
728 ],
729 ],
730 ],
731 ]
732 );
733
734 $this->end_controls_tab();
735
736 /**
737 * Hover.
738 */
739 $this->start_controls_tab(
740 'tab_background_overlay_hover',
741 [
742 'label' => esc_html__( 'Hover', 'elementor' ),
743 ]
744 );
745
746 $this->add_group_control(
747 Group_Control_Background::get_type(),
748 [
749 'name' => 'background_overlay_hover',
750 'selector' => '{{WRAPPER}}:hover::before',
751 'fields_options' => [
752 'background' => [
753 'selectors' => [
754 // Hack to set the `::before` content in order to render it only when there is a background overlay.
755 '{{WRAPPER}}:hover::before' => '--background-overlay: \'\';',
756 ],
757 ],
758 ],
759 ]
760 );
761
762 $this->add_control(
763 'background_overlay_hover_opacity',
764 [
765 'label' => esc_html__( 'Opacity', 'elementor' ),
766 'type' => Controls_Manager::SLIDER,
767 'default' => [
768 'size' => .5,
769 ],
770 'range' => [
771 'px' => [
772 'max' => 1,
773 'step' => 0.01,
774 ],
775 ],
776 'selectors' => [
777 '{{WRAPPER}}:hover' => '--overlay-opacity: {{SIZE}};',
778 ],
779 'condition' => [
780 'background_overlay_hover_background' => [ 'classic', 'gradient' ],
781 ],
782 ]
783 );
784
785 $this->add_group_control(
786 Group_Control_Css_Filter::get_type(),
787 [
788 'name' => 'css_filters_hover',
789 'selector' => '{{WRAPPER}}:hover::before',
790 ]
791 );
792
793 $this->add_control(
794 'background_overlay_hover_transition',
795 [
796 'label' => esc_html__( 'Transition Duration', 'elementor' ),
797 'type' => Controls_Manager::SLIDER,
798 'range' => [
799 'px' => [
800 'max' => 3,
801 'step' => 0.1,
802 ],
803 ],
804 'render_type' => 'ui',
805 'separator' => 'before',
806 'selectors' => [
807 '{{WRAPPER}}' => '--overlay-transition: {{SIZE}}s;',
808 ],
809 ]
810 );
811
812 $this->end_controls_tab();
813
814 $this->end_controls_tabs();
815
816 $this->end_controls_section();
817 }
818
819 /**
820 * Register the Container's border controls.
821 *
822 * @return void
823 */
824 protected function register_border_controls() {
825 $this->start_controls_section(
826 'section_border',
827 [
828 'label' => esc_html__( 'Border', 'elementor' ),
829 'tab' => Controls_Manager::TAB_STYLE,
830 ]
831 );
832
833 $this->start_controls_tabs( 'tabs_border' );
834
835 /**
836 * Normal.
837 */
838 $this->start_controls_tab(
839 'tab_border',
840 [
841 'label' => esc_html__( 'Normal', 'elementor' ),
842 ]
843 );
844
845 $this->add_group_control(
846 Group_Control_Border::get_type(),
847 [
848 'name' => 'border',
849 ]
850 );
851
852 $this->add_responsive_control(
853 'border_radius',
854 [
855 'label' => esc_html__( 'Border Radius', 'elementor' ),
856 'type' => Controls_Manager::DIMENSIONS,
857 'size_units' => [ 'px', '%' ],
858 'selectors' => [
859 '{{WRAPPER}}' => '--border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
860 ],
861 ]
862 );
863
864 $this->add_group_control(
865 Group_Control_Box_Shadow::get_type(),
866 [
867 'name' => 'box_shadow',
868 ]
869 );
870
871 $this->end_controls_tab();
872
873 /**
874 * Hover.
875 */
876 $this->start_controls_tab(
877 'tab_border_hover',
878 [
879 'label' => esc_html__( 'Hover', 'elementor' ),
880 ]
881 );
882
883 $this->add_group_control(
884 Group_Control_Border::get_type(),
885 [
886 'name' => 'border_hover',
887 'selector' => '{{WRAPPER}}:hover',
888 ]
889 );
890
891 $this->add_responsive_control(
892 'border_radius_hover',
893 [
894 'label' => esc_html__( 'Border Radius', 'elementor' ),
895 'type' => Controls_Manager::DIMENSIONS,
896 'size_units' => [ 'px', '%' ],
897 'selectors' => [
898 '{{WRAPPER}}:hover' => '--border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
899 ],
900 ]
901 );
902
903 $this->add_group_control(
904 Group_Control_Box_Shadow::get_type(),
905 [
906 'name' => 'box_shadow_hover',
907 'selector' => '{{WRAPPER}}:hover',
908 ]
909 );
910
911 $this->add_control(
912 'border_hover_transition',
913 [
914 'label' => esc_html__( 'Transition Duration', 'elementor' ),
915 'type' => Controls_Manager::SLIDER,
916 'separator' => 'before',
917 'default' => [
918 'size' => 0.3,
919 ],
920 'range' => [
921 'px' => [
922 'max' => 3,
923 'step' => 0.1,
924 ],
925 ],
926 'conditions' => [
927 'relation' => 'or',
928 'terms' => [
929 [
930 'name' => 'background_background',
931 'operator' => '!==',
932 'value' => '',
933 ],
934 [
935 'name' => 'border_border',
936 'operator' => '!==',
937 'value' => '',
938 ],
939 ],
940 ],
941 'selectors' => [
942 '{{WRAPPER}}' => '--transition: background {{background_hover_transition.SIZE}}s, border {{SIZE}}s, border-radius {{SIZE}}s, box-shadow {{SIZE}}s;
943 --overlay-transition: background {{background_overlay_hover_transition.SIZE}}s, border-radius {{SIZE}}s, opacity {{background_overlay_hover_transition.SIZE}}s',
944 ],
945 ]
946 );
947
948 $this->end_controls_tab();
949
950 $this->end_controls_tabs();
951
952 $this->end_controls_section();
953 }
954
955 /**
956 * Register the Container's shape dividers controls.
957 * TODO: Copied from `section.php`.
958 *
959 * @return void
960 */
961 protected function register_shape_dividers_controls() {
962 $this->start_controls_section(
963 'section_shape_divider',
964 [
965 'label' => esc_html__( 'Shape Divider', 'elementor' ),
966 'tab' => Controls_Manager::TAB_STYLE,
967 ]
968 );
969
970 $this->start_controls_tabs( 'tabs_shape_dividers' );
971
972 $shapes_options = [
973 '' => esc_html__( 'None', 'elementor' ),
974 ];
975
976 foreach ( Shapes::get_shapes() as $shape_name => $shape_props ) {
977 $shapes_options[ $shape_name ] = $shape_props['title'];
978 }
979
980 foreach ( [
981 'top' => esc_html__( 'Top', 'elementor' ),
982 'bottom' => esc_html__( 'Bottom', 'elementor' ),
983 ] as $side => $side_label ) {
984 $base_control_key = "shape_divider_$side";
985
986 $this->start_controls_tab(
987 "tab_$base_control_key",
988 [
989 'label' => $side_label,
990 ]
991 );
992
993 $this->add_control(
994 $base_control_key,
995 [
996 'label' => esc_html__( 'Type', 'elementor' ),
997 'type' => Controls_Manager::SELECT,
998 'options' => $shapes_options,
999 'render_type' => 'none',
1000 'frontend_available' => true,
1001 ]
1002 );
1003
1004 $this->add_control(
1005 $base_control_key . '_color',
1006 [
1007 'label' => esc_html__( 'Color', 'elementor' ),
1008 'type' => Controls_Manager::COLOR,
1009 'condition' => [
1010 "shape_divider_$side!" => '',
1011 ],
1012 'selectors' => [
1013 "{{WRAPPER}} > .elementor-shape-$side .elementor-shape-fill" => 'fill: {{UNIT}};',
1014 ],
1015 ]
1016 );
1017
1018 $this->add_responsive_control(
1019 $base_control_key . '_width',
1020 [
1021 'label' => esc_html__( 'Width', 'elementor' ),
1022 'type' => Controls_Manager::SLIDER,
1023 'default' => [
1024 'unit' => '%',
1025 ],
1026 'tablet_default' => [
1027 'unit' => '%',
1028 ],
1029 'mobile_default' => [
1030 'unit' => '%',
1031 ],
1032 'range' => [
1033 '%' => [
1034 'min' => 100,
1035 'max' => 300,
1036 ],
1037 ],
1038 'condition' => [
1039 "shape_divider_$side" => array_keys( Shapes::filter_shapes( 'height_only', Shapes::FILTER_EXCLUDE ) ),
1040 ],
1041 'selectors' => [
1042 "{{WRAPPER}} > .elementor-shape-$side svg" => 'width: calc({{SIZE}}{{UNIT}} + 1.3px)',
1043 ],
1044 ]
1045 );
1046
1047 $this->add_responsive_control(
1048 $base_control_key . '_height',
1049 [
1050 'label' => esc_html__( 'Height', 'elementor' ),
1051 'type' => Controls_Manager::SLIDER,
1052 'range' => [
1053 'px' => [
1054 'max' => 500,
1055 ],
1056 ],
1057 'condition' => [
1058 "shape_divider_$side!" => '',
1059 ],
1060 'selectors' => [
1061 "{{WRAPPER}} > .elementor-shape-$side svg" => 'height: {{SIZE}}{{UNIT}};',
1062 ],
1063 ]
1064 );
1065
1066 $this->add_control(
1067 $base_control_key . '_flip',
1068 [
1069 'label' => esc_html__( 'Flip', 'elementor' ),
1070 'type' => Controls_Manager::SWITCHER,
1071 'condition' => [
1072 "shape_divider_$side" => array_keys( Shapes::filter_shapes( 'has_flip' ) ),
1073 ],
1074 'selectors' => [
1075 "{{WRAPPER}} > .elementor-shape-$side svg" => 'transform: translateX(-50%) rotateY(180deg)',
1076 ],
1077 ]
1078 );
1079
1080 $this->add_control(
1081 $base_control_key . '_negative',
1082 [
1083 'label' => esc_html__( 'Invert', 'elementor' ),
1084 'type' => Controls_Manager::SWITCHER,
1085 'frontend_available' => true,
1086 'condition' => [
1087 "shape_divider_$side" => array_keys( Shapes::filter_shapes( 'has_negative' ) ),
1088 ],
1089 'render_type' => 'none',
1090 ]
1091 );
1092
1093 $this->add_control(
1094 $base_control_key . '_above_content',
1095 [
1096 'label' => esc_html__( 'Bring to Front', 'elementor' ),
1097 'type' => Controls_Manager::SWITCHER,
1098 'selectors' => [
1099 "{{WRAPPER}} > .elementor-shape-$side" => 'z-index: 2; pointer-events: none',
1100 ],
1101 'condition' => [
1102 "shape_divider_$side!" => '',
1103 ],
1104 ]
1105 );
1106
1107 $this->end_controls_tab();
1108 }
1109
1110 $this->end_controls_tabs();
1111
1112 $this->end_controls_section();
1113 }
1114 /**
1115 * Register the Container's style tab.
1116 *
1117 * @return void
1118 */
1119 protected function register_style_tab() {
1120 $this->register_background_controls();
1121
1122 $this->register_background_overlay_controls();
1123
1124 $this->register_border_controls();
1125
1126 $this->register_shape_dividers_controls();
1127 }
1128
1129 /**
1130 * Register the Container's advanced style controls.
1131 *
1132 * @return void
1133 */
1134 protected function register_advanced_controls() {
1135 $this->start_controls_section(
1136 'section_layout',
1137 [
1138 'label' => esc_html__( 'Layout', 'elementor' ),
1139 'tab' => Controls_Manager::TAB_ADVANCED,
1140 ]
1141 );
1142
1143 $this->add_responsive_control(
1144 'margin',
1145 [
1146 'label' => esc_html__( 'Margin', 'elementor' ),
1147 'type' => Controls_Manager::DIMENSIONS,
1148 'size_units' => [ 'px', 'em', '%', 'rem' ],
1149 'selectors' => [
1150 '{{WRAPPER}}' => '--margin-top: {{TOP}}{{UNIT}}; --margin-right: {{RIGHT}}{{UNIT}}; --margin-bottom: {{BOTTOM}}{{UNIT}}; --margin-left:{{LEFT}}{{UNIT}};',
1151 ],
1152 ]
1153 );
1154
1155 $this->add_responsive_control(
1156 'padding',
1157 [
1158 'label' => esc_html__( 'Padding', 'elementor' ),
1159 'type' => Controls_Manager::DIMENSIONS,
1160 'size_units' => [ 'px', 'em', '%', 'rem' ],
1161 'selectors' => [
1162 '{{WRAPPER}}' => '--padding-top: {{TOP}}{{UNIT}}; --padding-right: {{RIGHT}}{{UNIT}}; --padding-bottom: {{BOTTOM}}{{UNIT}}; --padding-left: {{LEFT}}{{UNIT}};',
1163 ],
1164 ]
1165 );
1166
1167 $this->add_group_control(
1168 Group_Control_Flex_Item::get_type(),
1169 [
1170 'name' => '_flex',
1171 'include' => [
1172 'align_self',
1173 'order',
1174 'order_custom',
1175 'size',
1176 'grow',
1177 'shrink',
1178 ],
1179 'selector' => '{{WRAPPER}}.e-container', // Hack to increase specificity.
1180 'separator' => 'before',
1181 ]
1182 );
1183
1184 $this->add_control(
1185 'position_description',
1186 [
1187 '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' ),
1188 'type' => Controls_Manager::RAW_HTML,
1189 'content_classes' => 'elementor-panel-alert elementor-panel-alert-warning',
1190 'render_type' => 'ui',
1191 'condition' => [
1192 'position!' => '',
1193 ],
1194 ]
1195 );
1196
1197 // TODO: Copied from `common.php` - Extract to group control.
1198 $this->add_control(
1199 'position',
1200 [
1201 'label' => esc_html__( 'Position', 'elementor' ),
1202 'type' => Controls_Manager::SELECT,
1203 'default' => '',
1204 'options' => [
1205 '' => esc_html__( 'Default', 'elementor' ),
1206 'absolute' => esc_html__( 'Absolute', 'elementor' ),
1207 'fixed' => esc_html__( 'Fixed', 'elementor' ),
1208 ],
1209 'selectors' => [
1210 '{{WRAPPER}}' => '--position: {{VALUE}};',
1211 ],
1212 'frontend_available' => true,
1213 'separator' => 'before',
1214 ]
1215 );
1216
1217 $left = esc_html__( 'Left', 'elementor' );
1218 $right = esc_html__( 'Right', 'elementor' );
1219
1220 $start = is_rtl() ? $right : $left;
1221 $end = ! is_rtl() ? $right : $left;
1222
1223 $this->add_control(
1224 '_offset_orientation_h',
1225 [
1226 'label' => esc_html__( 'Horizontal Orientation', 'elementor' ),
1227 'type' => Controls_Manager::CHOOSE,
1228 'toggle' => false,
1229 'default' => 'start',
1230 'options' => [
1231 'start' => [
1232 'title' => $start,
1233 'icon' => 'eicon-h-align-left',
1234 ],
1235 'end' => [
1236 'title' => $end,
1237 'icon' => 'eicon-h-align-right',
1238 ],
1239 ],
1240 'classes' => 'elementor-control-start-end',
1241 'render_type' => 'ui',
1242 'condition' => [
1243 'position!' => '',
1244 ],
1245 ]
1246 );
1247
1248 $this->add_responsive_control(
1249 '_offset_x',
1250 [
1251 'label' => esc_html__( 'Offset', 'elementor' ),
1252 'type' => Controls_Manager::SLIDER,
1253 'range' => [
1254 'px' => [
1255 'min' => -1000,
1256 'max' => 1000,
1257 'step' => 1,
1258 ],
1259 '%' => [
1260 'min' => -200,
1261 'max' => 200,
1262 ],
1263 'vw' => [
1264 'min' => -200,
1265 'max' => 200,
1266 ],
1267 'vh' => [
1268 'min' => -200,
1269 'max' => 200,
1270 ],
1271 ],
1272 'default' => [
1273 'size' => '0',
1274 ],
1275 'size_units' => [ 'px', '%', 'vw', 'vh' ],
1276 'selectors' => [
1277 'body:not(.rtl) {{WRAPPER}}' => 'left: {{SIZE}}{{UNIT}}',
1278 'body.rtl {{WRAPPER}}' => 'right: {{SIZE}}{{UNIT}}',
1279 ],
1280 'condition' => [
1281 '_offset_orientation_h!' => 'end',
1282 'position!' => '',
1283 ],
1284 ]
1285 );
1286
1287 $this->add_responsive_control(
1288 '_offset_x_end',
1289 [
1290 'label' => esc_html__( 'Offset', 'elementor' ),
1291 'type' => Controls_Manager::SLIDER,
1292 'range' => [
1293 'px' => [
1294 'min' => -1000,
1295 'max' => 1000,
1296 'step' => 0.1,
1297 ],
1298 '%' => [
1299 'min' => -200,
1300 'max' => 200,
1301 ],
1302 'vw' => [
1303 'min' => -200,
1304 'max' => 200,
1305 ],
1306 'vh' => [
1307 'min' => -200,
1308 'max' => 200,
1309 ],
1310 ],
1311 'default' => [
1312 'size' => '0',
1313 ],
1314 'size_units' => [ 'px', '%', 'vw', 'vh' ],
1315 'selectors' => [
1316 'body:not(.rtl) {{WRAPPER}}' => 'right: {{SIZE}}{{UNIT}}',
1317 'body.rtl {{WRAPPER}}' => 'left: {{SIZE}}{{UNIT}}',
1318 ],
1319 'condition' => [
1320 '_offset_orientation_h' => 'end',
1321 'position!' => '',
1322 ],
1323 ]
1324 );
1325
1326 $this->add_control(
1327 '_offset_orientation_v',
1328 [
1329 'label' => esc_html__( 'Vertical Orientation', 'elementor' ),
1330 'type' => Controls_Manager::CHOOSE,
1331 'toggle' => false,
1332 'default' => 'start',
1333 'options' => [
1334 'start' => [
1335 'title' => esc_html__( 'Top', 'elementor' ),
1336 'icon' => 'eicon-v-align-top',
1337 ],
1338 'end' => [
1339 'title' => esc_html__( 'Bottom', 'elementor' ),
1340 'icon' => 'eicon-v-align-bottom',
1341 ],
1342 ],
1343 'render_type' => 'ui',
1344 'condition' => [
1345 'position!' => '',
1346 ],
1347 ]
1348 );
1349
1350 $this->add_responsive_control(
1351 '_offset_y',
1352 [
1353 'label' => esc_html__( 'Offset', 'elementor' ),
1354 'type' => Controls_Manager::SLIDER,
1355 'range' => [
1356 'px' => [
1357 'min' => -1000,
1358 'max' => 1000,
1359 'step' => 1,
1360 ],
1361 '%' => [
1362 'min' => -200,
1363 'max' => 200,
1364 ],
1365 'vh' => [
1366 'min' => -200,
1367 'max' => 200,
1368 ],
1369 'vw' => [
1370 'min' => -200,
1371 'max' => 200,
1372 ],
1373 ],
1374 'size_units' => [ 'px', '%', 'vh', 'vw' ],
1375 'default' => [
1376 'size' => '0',
1377 ],
1378 'selectors' => [
1379 '{{WRAPPER}}' => 'top: {{SIZE}}{{UNIT}}',
1380 ],
1381 'condition' => [
1382 '_offset_orientation_v!' => 'end',
1383 'position!' => '',
1384 ],
1385 ]
1386 );
1387
1388 $this->add_responsive_control(
1389 '_offset_y_end',
1390 [
1391 'label' => esc_html__( 'Offset', 'elementor' ),
1392 'type' => Controls_Manager::SLIDER,
1393 'range' => [
1394 'px' => [
1395 'min' => -1000,
1396 'max' => 1000,
1397 'step' => 1,
1398 ],
1399 '%' => [
1400 'min' => -200,
1401 'max' => 200,
1402 ],
1403 'vh' => [
1404 'min' => -200,
1405 'max' => 200,
1406 ],
1407 'vw' => [
1408 'min' => -200,
1409 'max' => 200,
1410 ],
1411 ],
1412 'size_units' => [ 'px', '%', 'vh', 'vw' ],
1413 'default' => [
1414 'size' => '0',
1415 ],
1416 'selectors' => [
1417 '{{WRAPPER}}' => 'bottom: {{SIZE}}{{UNIT}}',
1418 ],
1419 'condition' => [
1420 '_offset_orientation_v' => 'end',
1421 'position!' => '',
1422 ],
1423 ]
1424 );
1425
1426 $this->add_responsive_control(
1427 'z_index',
1428 [
1429 'label' => esc_html__( 'Z-Index', 'elementor' ),
1430 'type' => Controls_Manager::NUMBER,
1431 'min' => 0,
1432 'selectors' => [
1433 '{{WRAPPER}}' => '--z-index: {{VALUE}};',
1434 ],
1435 ]
1436 );
1437
1438 $this->add_control(
1439 '_element_id',
1440 [
1441 'label' => esc_html__( 'CSS ID', 'elementor' ),
1442 'type' => Controls_Manager::TEXT,
1443 'default' => '',
1444 'dynamic' => [
1445 'active' => true,
1446 ],
1447 'title' => esc_html__( 'Add your custom id WITHOUT the Pound key. e.g: my-id', 'elementor' ),
1448 'style_transfer' => false,
1449 'classes' => 'elementor-control-direction-ltr',
1450 ]
1451 );
1452
1453 $this->add_control(
1454 'css_classes',
1455 [
1456 'label' => esc_html__( 'CSS Classes', 'elementor' ),
1457 'type' => Controls_Manager::TEXT,
1458 'default' => '',
1459 'dynamic' => [
1460 'active' => true,
1461 ],
1462 'prefix_class' => '',
1463 'title' => esc_html__( 'Add your custom class WITHOUT the dot. e.g: my-class', 'elementor' ),
1464 'classes' => 'elementor-control-direction-ltr',
1465 ]
1466 );
1467
1468 $this->end_controls_section();
1469 }
1470
1471 /**
1472 * Register the Container's motion effects controls.
1473 *
1474 * @return void
1475 */
1476 protected function register_motion_effects_controls() {
1477 $this->start_controls_section(
1478 'section_effects',
1479 [
1480 'label' => esc_html__( 'Motion Effects', 'elementor' ),
1481 'tab' => Controls_Manager::TAB_ADVANCED,
1482 ]
1483 );
1484
1485 $this->add_responsive_control(
1486 'animation',
1487 [
1488 'label' => esc_html__( 'Entrance Animation', 'elementor' ),
1489 'type' => Controls_Manager::ANIMATION,
1490 'frontend_available' => true,
1491 ]
1492 );
1493
1494 $this->add_control(
1495 'animation_duration',
1496 [
1497 'label' => esc_html__( 'Animation Duration', 'elementor' ),
1498 'type' => Controls_Manager::SELECT,
1499 'default' => '',
1500 'options' => [
1501 'slow' => esc_html__( 'Slow', 'elementor' ),
1502 '' => esc_html__( 'Normal', 'elementor' ),
1503 'fast' => esc_html__( 'Fast', 'elementor' ),
1504 ],
1505 'prefix_class' => 'animated-',
1506 'condition' => [
1507 'animation!' => '',
1508 ],
1509 ]
1510 );
1511
1512 $this->add_control(
1513 'animation_delay',
1514 [
1515 'label' => esc_html__( 'Animation Delay', 'elementor' ) . ' (ms)',
1516 'type' => Controls_Manager::NUMBER,
1517 'default' => '',
1518 'min' => 0,
1519 'step' => 100,
1520 'condition' => [
1521 'animation!' => '',
1522 ],
1523 'render_type' => 'none',
1524 'frontend_available' => true,
1525 ]
1526 );
1527
1528 $this->end_controls_section();
1529 }
1530
1531 /**
1532 * Register the Container's responsive controls.
1533 *
1534 * @return void
1535 */
1536 protected function register_responsive_controls() {
1537 $this->start_controls_section(
1538 '_section_responsive',
1539 [
1540 'label' => esc_html__( 'Responsive', 'elementor' ),
1541 'tab' => Controls_Manager::TAB_ADVANCED,
1542 ]
1543 );
1544
1545 $this->add_control(
1546 'heading_visibility',
1547 [
1548 'label' => esc_html__( 'Visibility', 'elementor' ),
1549 'type' => Controls_Manager::HEADING,
1550 ]
1551 );
1552
1553 $this->add_control(
1554 'responsive_description',
1555 [
1556 'raw' => esc_html__( 'Responsive visibility will take effect only on preview or live page, and not while editing in Elementor.', 'elementor' ),
1557 'type' => Controls_Manager::RAW_HTML,
1558 'content_classes' => 'elementor-descriptor',
1559 ]
1560 );
1561
1562 $this->add_hidden_device_controls();
1563
1564 $this->end_controls_section();
1565 }
1566
1567 /**
1568 * Register the Container's advanced tab.
1569 *
1570 * @return void
1571 */
1572 protected function register_advanced_tab() {
1573 $this->register_advanced_controls();
1574
1575 $this->register_motion_effects_controls();
1576
1577 $this->register_responsive_controls();
1578
1579 Plugin::$instance->controls_manager->add_custom_attributes_controls( $this );
1580
1581 Plugin::$instance->controls_manager->add_custom_css_controls( $this );
1582 }
1583
1584 /**
1585 * Register the Container's controls.
1586 *
1587 * @return void
1588 */
1589 protected function register_controls() {
1590 $this->register_layout_tab();
1591 $this->register_style_tab();
1592 $this->register_advanced_tab();
1593 }
1594 }
1595