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

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