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

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