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

container.php in Elementor Website Builder – more than just a page builder 3.6.0-dev1, at trunk/includes/elements/container.php

1,610 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 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 Item Control', 'elementor' ),
502 'placeholder' => '20',
503 ],
504 'direction' => [
505 'default' => 'column',
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 'allowed_dimensions' => 'vertical',
1158 'placeholder' => [
1159 'top' => '',
1160 'right' => 'auto',
1161 'bottom' => '',
1162 'left' => 'auto',
1163 ],
1164 'selectors' => [
1165 '{{WRAPPER}}' => '--margin: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
1166 ],
1167 ]
1168 );
1169
1170 $this->add_responsive_control(
1171 'padding',
1172 [
1173 'label' => esc_html__( 'Padding', 'elementor' ),
1174 'type' => Controls_Manager::DIMENSIONS,
1175 'size_units' => [ 'px', 'em', '%', 'rem' ],
1176 'selectors' => [
1177 '{{WRAPPER}}' => '--padding-top: {{TOP}}{{UNIT}}; --padding-right: {{RIGHT}}{{UNIT}}; --padding-bottom: {{BOTTOM}}{{UNIT}}; --padding-left: {{LEFT}}{{UNIT}};',
1178 ],
1179 ]
1180 );
1181
1182 $this->add_group_control(
1183 Group_Control_Flex_Item::get_type(),
1184 [
1185 'name' => '_flex',
1186 'include' => [
1187 'align_self',
1188 'order',
1189 'order_custom',
1190 'size',
1191 'grow',
1192 'shrink',
1193 ],
1194 'selector' => '{{WRAPPER}}.e-container', // Hack to increase specificity.
1195 'separator' => 'before',
1196 ]
1197 );
1198
1199 $this->add_control(
1200 'position_description',
1201 [
1202 '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' ),
1203 'type' => Controls_Manager::RAW_HTML,
1204 'content_classes' => 'elementor-panel-alert elementor-panel-alert-warning',
1205 'render_type' => 'ui',
1206 'condition' => [
1207 'position!' => '',
1208 ],
1209 ]
1210 );
1211
1212 // TODO: Copied from `common.php` - Extract to group control.
1213 $this->add_control(
1214 'position',
1215 [
1216 'label' => esc_html__( 'Position', 'elementor' ),
1217 'type' => Controls_Manager::SELECT,
1218 'default' => '',
1219 'options' => [
1220 '' => esc_html__( 'Default', 'elementor' ),
1221 'absolute' => esc_html__( 'Absolute', 'elementor' ),
1222 'fixed' => esc_html__( 'Fixed', 'elementor' ),
1223 ],
1224 'selectors' => [
1225 '{{WRAPPER}}' => '--position: {{VALUE}};',
1226 ],
1227 'frontend_available' => true,
1228 'separator' => 'before',
1229 ]
1230 );
1231
1232 $left = esc_html__( 'Left', 'elementor' );
1233 $right = esc_html__( 'Right', 'elementor' );
1234
1235 $start = is_rtl() ? $right : $left;
1236 $end = ! is_rtl() ? $right : $left;
1237
1238 $this->add_control(
1239 '_offset_orientation_h',
1240 [
1241 'label' => esc_html__( 'Horizontal Orientation', 'elementor' ),
1242 'type' => Controls_Manager::CHOOSE,
1243 'toggle' => false,
1244 'default' => 'start',
1245 'options' => [
1246 'start' => [
1247 'title' => $start,
1248 'icon' => 'eicon-h-align-left',
1249 ],
1250 'end' => [
1251 'title' => $end,
1252 'icon' => 'eicon-h-align-right',
1253 ],
1254 ],
1255 'classes' => 'elementor-control-start-end',
1256 'render_type' => 'ui',
1257 'condition' => [
1258 'position!' => '',
1259 ],
1260 ]
1261 );
1262
1263 $this->add_responsive_control(
1264 '_offset_x',
1265 [
1266 'label' => esc_html__( 'Offset', 'elementor' ),
1267 'type' => Controls_Manager::SLIDER,
1268 'range' => [
1269 'px' => [
1270 'min' => -1000,
1271 'max' => 1000,
1272 'step' => 1,
1273 ],
1274 '%' => [
1275 'min' => -200,
1276 'max' => 200,
1277 ],
1278 'vw' => [
1279 'min' => -200,
1280 'max' => 200,
1281 ],
1282 'vh' => [
1283 'min' => -200,
1284 'max' => 200,
1285 ],
1286 ],
1287 'default' => [
1288 'size' => '0',
1289 ],
1290 'size_units' => [ 'px', '%', 'vw', 'vh' ],
1291 'selectors' => [
1292 'body:not(.rtl) {{WRAPPER}}' => 'left: {{SIZE}}{{UNIT}}',
1293 'body.rtl {{WRAPPER}}' => 'right: {{SIZE}}{{UNIT}}',
1294 ],
1295 'condition' => [
1296 '_offset_orientation_h!' => 'end',
1297 'position!' => '',
1298 ],
1299 ]
1300 );
1301
1302 $this->add_responsive_control(
1303 '_offset_x_end',
1304 [
1305 'label' => esc_html__( 'Offset', 'elementor' ),
1306 'type' => Controls_Manager::SLIDER,
1307 'range' => [
1308 'px' => [
1309 'min' => -1000,
1310 'max' => 1000,
1311 'step' => 0.1,
1312 ],
1313 '%' => [
1314 'min' => -200,
1315 'max' => 200,
1316 ],
1317 'vw' => [
1318 'min' => -200,
1319 'max' => 200,
1320 ],
1321 'vh' => [
1322 'min' => -200,
1323 'max' => 200,
1324 ],
1325 ],
1326 'default' => [
1327 'size' => '0',
1328 ],
1329 'size_units' => [ 'px', '%', 'vw', 'vh' ],
1330 'selectors' => [
1331 'body:not(.rtl) {{WRAPPER}}' => 'right: {{SIZE}}{{UNIT}}',
1332 'body.rtl {{WRAPPER}}' => 'left: {{SIZE}}{{UNIT}}',
1333 ],
1334 'condition' => [
1335 '_offset_orientation_h' => 'end',
1336 'position!' => '',
1337 ],
1338 ]
1339 );
1340
1341 $this->add_control(
1342 '_offset_orientation_v',
1343 [
1344 'label' => esc_html__( 'Vertical Orientation', 'elementor' ),
1345 'type' => Controls_Manager::CHOOSE,
1346 'toggle' => false,
1347 'default' => 'start',
1348 'options' => [
1349 'start' => [
1350 'title' => esc_html__( 'Top', 'elementor' ),
1351 'icon' => 'eicon-v-align-top',
1352 ],
1353 'end' => [
1354 'title' => esc_html__( 'Bottom', 'elementor' ),
1355 'icon' => 'eicon-v-align-bottom',
1356 ],
1357 ],
1358 'render_type' => 'ui',
1359 'condition' => [
1360 'position!' => '',
1361 ],
1362 ]
1363 );
1364
1365 $this->add_responsive_control(
1366 '_offset_y',
1367 [
1368 'label' => esc_html__( 'Offset', 'elementor' ),
1369 'type' => Controls_Manager::SLIDER,
1370 'range' => [
1371 'px' => [
1372 'min' => -1000,
1373 'max' => 1000,
1374 'step' => 1,
1375 ],
1376 '%' => [
1377 'min' => -200,
1378 'max' => 200,
1379 ],
1380 'vh' => [
1381 'min' => -200,
1382 'max' => 200,
1383 ],
1384 'vw' => [
1385 'min' => -200,
1386 'max' => 200,
1387 ],
1388 ],
1389 'size_units' => [ 'px', '%', 'vh', 'vw' ],
1390 'default' => [
1391 'size' => '0',
1392 ],
1393 'selectors' => [
1394 '{{WRAPPER}}' => 'top: {{SIZE}}{{UNIT}}',
1395 ],
1396 'condition' => [
1397 '_offset_orientation_v!' => 'end',
1398 'position!' => '',
1399 ],
1400 ]
1401 );
1402
1403 $this->add_responsive_control(
1404 '_offset_y_end',
1405 [
1406 'label' => esc_html__( 'Offset', 'elementor' ),
1407 'type' => Controls_Manager::SLIDER,
1408 'range' => [
1409 'px' => [
1410 'min' => -1000,
1411 'max' => 1000,
1412 'step' => 1,
1413 ],
1414 '%' => [
1415 'min' => -200,
1416 'max' => 200,
1417 ],
1418 'vh' => [
1419 'min' => -200,
1420 'max' => 200,
1421 ],
1422 'vw' => [
1423 'min' => -200,
1424 'max' => 200,
1425 ],
1426 ],
1427 'size_units' => [ 'px', '%', 'vh', 'vw' ],
1428 'default' => [
1429 'size' => '0',
1430 ],
1431 'selectors' => [
1432 '{{WRAPPER}}' => 'bottom: {{SIZE}}{{UNIT}}',
1433 ],
1434 'condition' => [
1435 '_offset_orientation_v' => 'end',
1436 'position!' => '',
1437 ],
1438 ]
1439 );
1440
1441 $this->add_responsive_control(
1442 'z_index',
1443 [
1444 'label' => esc_html__( 'Z-Index', 'elementor' ),
1445 'type' => Controls_Manager::NUMBER,
1446 'min' => 0,
1447 'selectors' => [
1448 '{{WRAPPER}}' => '--z-index: {{VALUE}};',
1449 ],
1450 ]
1451 );
1452
1453 $this->add_control(
1454 '_element_id',
1455 [
1456 'label' => esc_html__( 'CSS ID', 'elementor' ),
1457 'type' => Controls_Manager::TEXT,
1458 'default' => '',
1459 'dynamic' => [
1460 'active' => true,
1461 ],
1462 'title' => esc_html__( 'Add your custom id WITHOUT the Pound key. e.g: my-id', 'elementor' ),
1463 'style_transfer' => false,
1464 'classes' => 'elementor-control-direction-ltr',
1465 ]
1466 );
1467
1468 $this->add_control(
1469 'css_classes',
1470 [
1471 'label' => esc_html__( 'CSS Classes', 'elementor' ),
1472 'type' => Controls_Manager::TEXT,
1473 'default' => '',
1474 'dynamic' => [
1475 'active' => true,
1476 ],
1477 'prefix_class' => '',
1478 'title' => esc_html__( 'Add your custom class WITHOUT the dot. e.g: my-class', 'elementor' ),
1479 'classes' => 'elementor-control-direction-ltr',
1480 ]
1481 );
1482
1483 $this->end_controls_section();
1484 }
1485
1486 /**
1487 * Register the Container's motion effects controls.
1488 *
1489 * @return void
1490 */
1491 protected function register_motion_effects_controls() {
1492 $this->start_controls_section(
1493 'section_effects',
1494 [
1495 'label' => esc_html__( 'Motion Effects', 'elementor' ),
1496 'tab' => Controls_Manager::TAB_ADVANCED,
1497 ]
1498 );
1499
1500 $this->add_responsive_control(
1501 'animation',
1502 [
1503 'label' => esc_html__( 'Entrance Animation', 'elementor' ),
1504 'type' => Controls_Manager::ANIMATION,
1505 'frontend_available' => true,
1506 ]
1507 );
1508
1509 $this->add_control(
1510 'animation_duration',
1511 [
1512 'label' => esc_html__( 'Animation Duration', 'elementor' ),
1513 'type' => Controls_Manager::SELECT,
1514 'default' => '',
1515 'options' => [
1516 'slow' => esc_html__( 'Slow', 'elementor' ),
1517 '' => esc_html__( 'Normal', 'elementor' ),
1518 'fast' => esc_html__( 'Fast', 'elementor' ),
1519 ],
1520 'prefix_class' => 'animated-',
1521 'condition' => [
1522 'animation!' => '',
1523 ],
1524 ]
1525 );
1526
1527 $this->add_control(
1528 'animation_delay',
1529 [
1530 'label' => esc_html__( 'Animation Delay', 'elementor' ) . ' (ms)',
1531 'type' => Controls_Manager::NUMBER,
1532 'default' => '',
1533 'min' => 0,
1534 'step' => 100,
1535 'condition' => [
1536 'animation!' => '',
1537 ],
1538 'render_type' => 'none',
1539 'frontend_available' => true,
1540 ]
1541 );
1542
1543 $this->end_controls_section();
1544 }
1545
1546 /**
1547 * Register the Container's responsive controls.
1548 *
1549 * @return void
1550 */
1551 protected function register_responsive_controls() {
1552 $this->start_controls_section(
1553 '_section_responsive',
1554 [
1555 'label' => esc_html__( 'Responsive', 'elementor' ),
1556 'tab' => Controls_Manager::TAB_ADVANCED,
1557 ]
1558 );
1559
1560 $this->add_control(
1561 'heading_visibility',
1562 [
1563 'label' => esc_html__( 'Visibility', 'elementor' ),
1564 'type' => Controls_Manager::HEADING,
1565 ]
1566 );
1567
1568 $this->add_control(
1569 'responsive_description',
1570 [
1571 'raw' => esc_html__( 'Responsive visibility will take effect only on preview or live page, and not while editing in Elementor.', 'elementor' ),
1572 'type' => Controls_Manager::RAW_HTML,
1573 'content_classes' => 'elementor-descriptor',
1574 ]
1575 );
1576
1577 $this->add_hidden_device_controls();
1578
1579 $this->end_controls_section();
1580 }
1581
1582 /**
1583 * Register the Container's advanced tab.
1584 *
1585 * @return void
1586 */
1587 protected function register_advanced_tab() {
1588 $this->register_advanced_controls();
1589
1590 $this->register_motion_effects_controls();
1591
1592 $this->register_responsive_controls();
1593
1594 Plugin::$instance->controls_manager->add_custom_attributes_controls( $this );
1595
1596 Plugin::$instance->controls_manager->add_custom_css_controls( $this );
1597 }
1598
1599 /**
1600 * Register the Container's controls.
1601 *
1602 * @return void
1603 */
1604 protected function register_controls() {
1605 $this->register_layout_tab();
1606 $this->register_style_tab();
1607 $this->register_advanced_tab();
1608 }
1609 }
1610