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

1,604 lines 36.4 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 ],
354 'separator' => 'none',
355 ];
356
357 $this->add_responsive_control(
358 'width',
359 array_merge( $width_control_settings, [
360 'selectors' => [
361 '{{WRAPPER}}' => '--width: {{SIZE}}{{UNIT}};',
362 ],
363 'condition' => [
364 'content_width' => 'full',
365 ],
366 'placeholder' => [
367 'size' => '100',
368 'unit' => '%',
369 ],
370 ] )
371 );
372
373 $this->add_responsive_control(
374 'boxed_width',
375 array_merge( $width_control_settings, [
376 'selectors' => [
377 '{{WRAPPER}}' => '--content-width: {{SIZE}}{{UNIT}};',
378 ],
379 'condition' => [
380 'content_width' => 'boxed',
381 ],
382 'default' => [
383 'unit' => 'px',
384 ],
385 'device_args' => [
386 Breakpoints_Manager::BREAKPOINT_KEY_DESKTOP => [
387 // Use the default width from the kit as a placeholder.
388 'placeholder' => $this->active_kit->get_settings_for_display( 'container_width' ),
389 ],
390 ],
391 ] )
392 );
393
394 $this->add_responsive_control(
395 'min_height',
396 [
397 'label' => esc_html__( 'Min Height', 'elementor' ),
398 'type' => Controls_Manager::SLIDER,
399 'size_units' => [ 'px', 'vh' ],
400 'range' => [
401 'px' => [
402 'min' => 0,
403 'max' => 1440,
404 ],
405 'vh' => [
406 'min' => 0,
407 'max' => 100,
408 ],
409 ],
410 'description' => sprintf(
411 esc_html__( 'To achieve full height Container use %s.', 'elementor' ),
412 '100vh'
413 ),
414 'selectors' => [
415 '{{WRAPPER}}' => '--min-height: {{SIZE}}{{UNIT}};',
416 ],
417 ]
418 );
419
420 $this->add_control(
421 'overflow',
422 [
423 'label' => esc_html__( 'Overflow', 'elementor' ),
424 'type' => Controls_Manager::SELECT,
425 'separator' => 'before',
426 'default' => '',
427 'options' => [
428 '' => esc_html__( 'Default', 'elementor' ),
429 'hidden' => esc_html__( 'Hidden', 'elementor' ),
430 'auto' => esc_html__( 'Auto', 'elementor' ),
431 ],
432 'selectors' => [
433 '{{WRAPPER}}' => '--overflow: {{VALUE}}',
434 ],
435 ]
436 );
437
438 $possible_tags = [
439 'div' => 'div',
440 'header' => 'header',
441 'footer' => 'footer',
442 'main' => 'main',
443 'article' => 'article',
444 'section' => 'section',
445 'aside' => 'aside',
446 'nav' => 'nav',
447 'a' => 'a',
448 ];
449
450 $options = [
451 '' => esc_html__( 'Default', 'elementor' ),
452 ] + $possible_tags;
453
454 $this->add_control(
455 'html_tag',
456 [
457 'label' => esc_html__( 'HTML Tag', 'elementor' ),
458 'type' => Controls_Manager::SELECT,
459 'options' => $options,
460 ]
461 );
462
463 $this->add_control(
464 'link',
465 [
466 'label' => esc_html__( 'Link', 'elementor' ),
467 'type' => Controls_Manager::URL,
468 'dynamic' => [
469 'active' => true,
470 ],
471 'placeholder' => esc_html__( 'https://your-link.com', 'elementor' ),
472 'condition' => [
473 'html_tag' => 'a',
474 ],
475 'description' => esc_html__( 'Don\'t use for nested links, this will cause semantic issues and unexpected behavior.', 'elementor' ),
476 ]
477 );
478
479 $this->end_controls_section();
480
481 }
482
483 /**
484 * Register the Container's items layout controls.
485 *
486 * @return void
487 */
488 protected function register_items_layout_controls() {
489 $this->start_controls_section(
490 'section_layout_items',
491 [
492 'label' => esc_html__( 'Items', 'elementor' ),
493 'tab' => Controls_Manager::TAB_LAYOUT,
494 ]
495 );
496
497 $this->add_group_control(
498 Group_Control_Flex_Container::get_type(),
499 [
500 'name' => 'flex',
501 'selector' => '{{WRAPPER}}',
502 'fields_options' => [
503 'gap' => [
504 'label' => esc_html_x( 'Elements Gap', 'Flex Container Control', 'elementor' ),
505 // Use the default "elements gap" from the kit as a placeholder.
506 'placeholder' => $this->active_kit->get_settings_for_display( 'space_between_widgets' ),
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 'selectors' => [
1159 '{{WRAPPER}}' => '--margin-top: {{TOP}}{{UNIT}}; --margin-right: {{RIGHT}}{{UNIT}}; --margin-bottom: {{BOTTOM}}{{UNIT}}; --margin-left:{{LEFT}}{{UNIT}};',
1160 ],
1161 ]
1162 );
1163
1164 $this->add_responsive_control(
1165 'padding',
1166 [
1167 'label' => esc_html__( 'Padding', 'elementor' ),
1168 'type' => Controls_Manager::DIMENSIONS,
1169 'size_units' => [ 'px', 'em', '%', 'rem' ],
1170 'selectors' => [
1171 '{{WRAPPER}}' => '--padding-top: {{TOP}}{{UNIT}}; --padding-right: {{RIGHT}}{{UNIT}}; --padding-bottom: {{BOTTOM}}{{UNIT}}; --padding-left: {{LEFT}}{{UNIT}};',
1172 ],
1173 ]
1174 );
1175
1176 $this->add_group_control(
1177 Group_Control_Flex_Item::get_type(),
1178 [
1179 'name' => '_flex',
1180 'include' => [
1181 'align_self',
1182 'order',
1183 'order_custom',
1184 'size',
1185 'grow',
1186 'shrink',
1187 ],
1188 'selector' => '{{WRAPPER}}.e-container', // Hack to increase specificity.
1189 'separator' => 'before',
1190 ]
1191 );
1192
1193 $this->add_control(
1194 'position_description',
1195 [
1196 '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' ),
1197 'type' => Controls_Manager::RAW_HTML,
1198 'content_classes' => 'elementor-panel-alert elementor-panel-alert-warning',
1199 'render_type' => 'ui',
1200 'condition' => [
1201 'position!' => '',
1202 ],
1203 ]
1204 );
1205
1206 // TODO: Copied from `common.php` - Extract to group control.
1207 $this->add_control(
1208 'position',
1209 [
1210 'label' => esc_html__( 'Position', 'elementor' ),
1211 'type' => Controls_Manager::SELECT,
1212 'default' => '',
1213 'options' => [
1214 '' => esc_html__( 'Default', 'elementor' ),
1215 'absolute' => esc_html__( 'Absolute', 'elementor' ),
1216 'fixed' => esc_html__( 'Fixed', 'elementor' ),
1217 ],
1218 'selectors' => [
1219 '{{WRAPPER}}' => '--position: {{VALUE}};',
1220 ],
1221 'frontend_available' => true,
1222 'separator' => 'before',
1223 ]
1224 );
1225
1226 $left = esc_html__( 'Left', 'elementor' );
1227 $right = esc_html__( 'Right', 'elementor' );
1228
1229 $start = is_rtl() ? $right : $left;
1230 $end = ! is_rtl() ? $right : $left;
1231
1232 $this->add_control(
1233 '_offset_orientation_h',
1234 [
1235 'label' => esc_html__( 'Horizontal Orientation', 'elementor' ),
1236 'type' => Controls_Manager::CHOOSE,
1237 'toggle' => false,
1238 'default' => 'start',
1239 'options' => [
1240 'start' => [
1241 'title' => $start,
1242 'icon' => 'eicon-h-align-left',
1243 ],
1244 'end' => [
1245 'title' => $end,
1246 'icon' => 'eicon-h-align-right',
1247 ],
1248 ],
1249 'classes' => 'elementor-control-start-end',
1250 'render_type' => 'ui',
1251 'condition' => [
1252 'position!' => '',
1253 ],
1254 ]
1255 );
1256
1257 $this->add_responsive_control(
1258 '_offset_x',
1259 [
1260 'label' => esc_html__( 'Offset', 'elementor' ),
1261 'type' => Controls_Manager::SLIDER,
1262 'range' => [
1263 'px' => [
1264 'min' => -1000,
1265 'max' => 1000,
1266 'step' => 1,
1267 ],
1268 '%' => [
1269 'min' => -200,
1270 'max' => 200,
1271 ],
1272 'vw' => [
1273 'min' => -200,
1274 'max' => 200,
1275 ],
1276 'vh' => [
1277 'min' => -200,
1278 'max' => 200,
1279 ],
1280 ],
1281 'default' => [
1282 'size' => '0',
1283 ],
1284 'size_units' => [ 'px', '%', 'vw', 'vh' ],
1285 'selectors' => [
1286 'body:not(.rtl) {{WRAPPER}}' => 'left: {{SIZE}}{{UNIT}}',
1287 'body.rtl {{WRAPPER}}' => 'right: {{SIZE}}{{UNIT}}',
1288 ],
1289 'condition' => [
1290 '_offset_orientation_h!' => 'end',
1291 'position!' => '',
1292 ],
1293 ]
1294 );
1295
1296 $this->add_responsive_control(
1297 '_offset_x_end',
1298 [
1299 'label' => esc_html__( 'Offset', 'elementor' ),
1300 'type' => Controls_Manager::SLIDER,
1301 'range' => [
1302 'px' => [
1303 'min' => -1000,
1304 'max' => 1000,
1305 'step' => 0.1,
1306 ],
1307 '%' => [
1308 'min' => -200,
1309 'max' => 200,
1310 ],
1311 'vw' => [
1312 'min' => -200,
1313 'max' => 200,
1314 ],
1315 'vh' => [
1316 'min' => -200,
1317 'max' => 200,
1318 ],
1319 ],
1320 'default' => [
1321 'size' => '0',
1322 ],
1323 'size_units' => [ 'px', '%', 'vw', 'vh' ],
1324 'selectors' => [
1325 'body:not(.rtl) {{WRAPPER}}' => 'right: {{SIZE}}{{UNIT}}',
1326 'body.rtl {{WRAPPER}}' => 'left: {{SIZE}}{{UNIT}}',
1327 ],
1328 'condition' => [
1329 '_offset_orientation_h' => 'end',
1330 'position!' => '',
1331 ],
1332 ]
1333 );
1334
1335 $this->add_control(
1336 '_offset_orientation_v',
1337 [
1338 'label' => esc_html__( 'Vertical Orientation', 'elementor' ),
1339 'type' => Controls_Manager::CHOOSE,
1340 'toggle' => false,
1341 'default' => 'start',
1342 'options' => [
1343 'start' => [
1344 'title' => esc_html__( 'Top', 'elementor' ),
1345 'icon' => 'eicon-v-align-top',
1346 ],
1347 'end' => [
1348 'title' => esc_html__( 'Bottom', 'elementor' ),
1349 'icon' => 'eicon-v-align-bottom',
1350 ],
1351 ],
1352 'render_type' => 'ui',
1353 'condition' => [
1354 'position!' => '',
1355 ],
1356 ]
1357 );
1358
1359 $this->add_responsive_control(
1360 '_offset_y',
1361 [
1362 'label' => esc_html__( 'Offset', 'elementor' ),
1363 'type' => Controls_Manager::SLIDER,
1364 'range' => [
1365 'px' => [
1366 'min' => -1000,
1367 'max' => 1000,
1368 'step' => 1,
1369 ],
1370 '%' => [
1371 'min' => -200,
1372 'max' => 200,
1373 ],
1374 'vh' => [
1375 'min' => -200,
1376 'max' => 200,
1377 ],
1378 'vw' => [
1379 'min' => -200,
1380 'max' => 200,
1381 ],
1382 ],
1383 'size_units' => [ 'px', '%', 'vh', 'vw' ],
1384 'default' => [
1385 'size' => '0',
1386 ],
1387 'selectors' => [
1388 '{{WRAPPER}}' => 'top: {{SIZE}}{{UNIT}}',
1389 ],
1390 'condition' => [
1391 '_offset_orientation_v!' => 'end',
1392 'position!' => '',
1393 ],
1394 ]
1395 );
1396
1397 $this->add_responsive_control(
1398 '_offset_y_end',
1399 [
1400 'label' => esc_html__( 'Offset', 'elementor' ),
1401 'type' => Controls_Manager::SLIDER,
1402 'range' => [
1403 'px' => [
1404 'min' => -1000,
1405 'max' => 1000,
1406 'step' => 1,
1407 ],
1408 '%' => [
1409 'min' => -200,
1410 'max' => 200,
1411 ],
1412 'vh' => [
1413 'min' => -200,
1414 'max' => 200,
1415 ],
1416 'vw' => [
1417 'min' => -200,
1418 'max' => 200,
1419 ],
1420 ],
1421 'size_units' => [ 'px', '%', 'vh', 'vw' ],
1422 'default' => [
1423 'size' => '0',
1424 ],
1425 'selectors' => [
1426 '{{WRAPPER}}' => 'bottom: {{SIZE}}{{UNIT}}',
1427 ],
1428 'condition' => [
1429 '_offset_orientation_v' => 'end',
1430 'position!' => '',
1431 ],
1432 ]
1433 );
1434
1435 $this->add_responsive_control(
1436 'z_index',
1437 [
1438 'label' => esc_html__( 'Z-Index', 'elementor' ),
1439 'type' => Controls_Manager::NUMBER,
1440 'min' => 0,
1441 'selectors' => [
1442 '{{WRAPPER}}' => '--z-index: {{VALUE}};',
1443 ],
1444 ]
1445 );
1446
1447 $this->add_control(
1448 '_element_id',
1449 [
1450 'label' => esc_html__( 'CSS ID', 'elementor' ),
1451 'type' => Controls_Manager::TEXT,
1452 'default' => '',
1453 'dynamic' => [
1454 'active' => true,
1455 ],
1456 'title' => esc_html__( 'Add your custom id WITHOUT the Pound key. e.g: my-id', 'elementor' ),
1457 'style_transfer' => false,
1458 'classes' => 'elementor-control-direction-ltr',
1459 ]
1460 );
1461
1462 $this->add_control(
1463 'css_classes',
1464 [
1465 'label' => esc_html__( 'CSS Classes', 'elementor' ),
1466 'type' => Controls_Manager::TEXT,
1467 'default' => '',
1468 'dynamic' => [
1469 'active' => true,
1470 ],
1471 'prefix_class' => '',
1472 'title' => esc_html__( 'Add your custom class WITHOUT the dot. e.g: my-class', 'elementor' ),
1473 'classes' => 'elementor-control-direction-ltr',
1474 ]
1475 );
1476
1477 $this->end_controls_section();
1478 }
1479
1480 /**
1481 * Register the Container's motion effects controls.
1482 *
1483 * @return void
1484 */
1485 protected function register_motion_effects_controls() {
1486 $this->start_controls_section(
1487 'section_effects',
1488 [
1489 'label' => esc_html__( 'Motion Effects', 'elementor' ),
1490 'tab' => Controls_Manager::TAB_ADVANCED,
1491 ]
1492 );
1493
1494 $this->add_responsive_control(
1495 'animation',
1496 [
1497 'label' => esc_html__( 'Entrance Animation', 'elementor' ),
1498 'type' => Controls_Manager::ANIMATION,
1499 'frontend_available' => true,
1500 ]
1501 );
1502
1503 $this->add_control(
1504 'animation_duration',
1505 [
1506 'label' => esc_html__( 'Animation Duration', 'elementor' ),
1507 'type' => Controls_Manager::SELECT,
1508 'default' => '',
1509 'options' => [
1510 'slow' => esc_html__( 'Slow', 'elementor' ),
1511 '' => esc_html__( 'Normal', 'elementor' ),
1512 'fast' => esc_html__( 'Fast', 'elementor' ),
1513 ],
1514 'prefix_class' => 'animated-',
1515 'condition' => [
1516 'animation!' => '',
1517 ],
1518 ]
1519 );
1520
1521 $this->add_control(
1522 'animation_delay',
1523 [
1524 'label' => esc_html__( 'Animation Delay', 'elementor' ) . ' (ms)',
1525 'type' => Controls_Manager::NUMBER,
1526 'default' => '',
1527 'min' => 0,
1528 'step' => 100,
1529 'condition' => [
1530 'animation!' => '',
1531 ],
1532 'render_type' => 'none',
1533 'frontend_available' => true,
1534 ]
1535 );
1536
1537 $this->end_controls_section();
1538 }
1539
1540 /**
1541 * Register the Container's responsive controls.
1542 *
1543 * @return void
1544 */
1545 protected function register_responsive_controls() {
1546 $this->start_controls_section(
1547 '_section_responsive',
1548 [
1549 'label' => esc_html__( 'Responsive', 'elementor' ),
1550 'tab' => Controls_Manager::TAB_ADVANCED,
1551 ]
1552 );
1553
1554 $this->add_control(
1555 'heading_visibility',
1556 [
1557 'label' => esc_html__( 'Visibility', 'elementor' ),
1558 'type' => Controls_Manager::HEADING,
1559 ]
1560 );
1561
1562 $this->add_control(
1563 'responsive_description',
1564 [
1565 'raw' => esc_html__( 'Responsive visibility will take effect only on preview or live page, and not while editing in Elementor.', 'elementor' ),
1566 'type' => Controls_Manager::RAW_HTML,
1567 'content_classes' => 'elementor-descriptor',
1568 ]
1569 );
1570
1571 $this->add_hidden_device_controls();
1572
1573 $this->end_controls_section();
1574 }
1575
1576 /**
1577 * Register the Container's advanced tab.
1578 *
1579 * @return void
1580 */
1581 protected function register_advanced_tab() {
1582 $this->register_advanced_controls();
1583
1584 $this->register_motion_effects_controls();
1585
1586 $this->register_responsive_controls();
1587
1588 Plugin::$instance->controls_manager->add_custom_attributes_controls( $this );
1589
1590 Plugin::$instance->controls_manager->add_custom_css_controls( $this );
1591 }
1592
1593 /**
1594 * Register the Container's controls.
1595 *
1596 * @return void
1597 */
1598 protected function register_controls() {
1599 $this->register_layout_tab();
1600 $this->register_style_tab();
1601 $this->register_advanced_tab();
1602 }
1603 }
1604