PluginProbe
Elementor Website Builder – more than just a page builder / 3.30.0-beta1
Elementor Website Builder – more than just a page builder v3.30.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 / widgets / common-base.php

common-base.php in Elementor Website Builder – more than just a page builder 3.30.0-beta1, at includes/widgets/common-base.php

1,359 lines 33.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Elementor;
3
4 if ( ! defined( 'ABSPATH' ) ) {
5 exit; // Exit if accessed directly.
6 }
7
8 /**
9 * Elementor common widget.
10 *
11 * Elementor base widget that gives you all the advanced options of the basic
12 * widget.
13 *
14 * @since 1.0.0
15 */
16 class Widget_Common_Base extends Widget_Base {
17
18 const WRAPPER_SELECTOR = '{{WRAPPER}} .elementor-widget-container';
19 const WRAPPER_SELECTOR_CHILD = '{{WRAPPER}} > .elementor-widget-container';
20 const WRAPPER_SELECTOR_HOVER = '{{WRAPPER}}:hover .elementor-widget-container';
21 const WRAPPER_SELECTOR_HOVER_CHILD = '{{WRAPPER}}:hover > .elementor-widget-container';
22 const MASK_SELECTOR_DEFAULT = '{{WRAPPER}}:not( .elementor-widget-image ) .elementor-widget-container';
23 const MASK_SELECTOR_IMG = '{{WRAPPER}}.elementor-widget-image .elementor-widget-container img';
24 const TRANSFORM_SELECTOR_CLASS = ' > .elementor-widget-container';
25 const MARGIN = 'margin: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};';
26
27 /**
28 * Get widget name.
29 *
30 * Retrieve common widget name.
31 *
32 * @since 1.0.0
33 * @access public
34 *
35 * @return string Widget name.
36 */
37 public function get_name() {
38 return 'common-base';
39 }
40
41 /**
42 * Show in panel.
43 *
44 * Whether to show the common widget in the panel or not.
45 *
46 * @since 1.0.0
47 * @access public
48 *
49 * @return bool Whether to show the widget in the panel.
50 */
51 public function show_in_panel() {
52 return false;
53 }
54
55 /**
56 * Get Responsive Device Args
57 *
58 * Receives an array of device args, and duplicates it for each active breakpoint.
59 * Returns an array of device args.
60 *
61 * @since 3.4.7
62 * @deprecated 3.7.0 Not needed anymore because responsive conditioning in the Editor was fixed in v3.7.0.
63 * @access protected
64 *
65 * @param array $args arguments to duplicate per breakpoint.
66 * @param array $devices_to_exclude
67 *
68 * @return array responsive device args
69 */
70 protected function get_responsive_device_args( array $args, array $devices_to_exclude = [] ) {
71 Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.7.0' );
72
73 $device_args = [];
74 $breakpoints = Plugin::$instance->breakpoints->get_active_breakpoints();
75
76 foreach ( $breakpoints as $breakpoint_key => $breakpoint ) {
77 // If the device is not excluded, add it to the device args array.
78 if ( ! in_array( $breakpoint_key, $devices_to_exclude, true ) ) {
79 $parsed_device_args = $this->parse_device_args_placeholders( $args, $breakpoint_key );
80
81 $device_args[ $breakpoint_key ] = $parsed_device_args;
82 }
83 }
84
85 return $device_args;
86 }
87
88 /**
89 * Parse Device Args Placeholders
90 *
91 * Receives an array of args. Iterates over the args, and replaces the {{DEVICE}} placeholder, if exists, with the
92 * passed breakpoint key.
93 *
94 * @since 3.4.7
95 * @access private
96 *
97 * @param array $args
98 * @param string $breakpoint_key
99 * @return array parsed device args
100 */
101 private function parse_device_args_placeholders( array $args, $breakpoint_key ) {
102 $parsed_args = [];
103
104 foreach ( $args as $arg_key => $arg_value ) {
105 $arg_key = str_replace( '{{DEVICE}}', $breakpoint_key, $arg_key );
106
107 if ( is_array( $arg_value ) ) {
108 $arg_value = $this->parse_device_args_placeholders( $arg_value, $breakpoint_key );
109 }
110
111 $parsed_args[ $arg_key ] = $arg_value;
112 }
113
114 return $parsed_args;
115 }
116
117 /**
118 * @param String $shape Shape name.
119 *
120 * @return string The shape path in the assets folder.
121 */
122 private function get_shape_url( $shape ) {
123 return ELEMENTOR_ASSETS_URL . 'mask-shapes/' . $shape . '.svg';
124 }
125
126 /**
127 * Return a translated user-friendly list of the available masking shapes.
128 *
129 * @param bool $add_custom Determine if the output should contain `Custom` options.
130 *
131 * @return array Array of shapes with their URL as key.
132 */
133 private function get_shapes( $add_custom = true ) {
134 $shapes = [
135 'circle' => [
136 'title' => esc_html__( 'Circle', 'elementor' ),
137 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/circle.svg',
138 ],
139 'oval-vertical' => [
140 'title' => esc_html__( 'Oval vertical', 'elementor' ),
141 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/oval-vertical.svg',
142 ],
143 'oval-horizontal' => [
144 'title' => esc_html__( 'Oval horizontal', 'elementor' ),
145 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/oval-horizontal.svg',
146 ],
147 'pill-vertical' => [
148 'title' => esc_html__( 'Pill vertical', 'elementor' ),
149 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/pill-vertical.svg',
150 ],
151 'pill-horizontal' => [
152 'title' => esc_html__( 'Pill horizontal', 'elementor' ),
153 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/pill-horizontal.svg',
154 ],
155 'triangle' => [
156 'title' => esc_html__( 'Triangle', 'elementor' ),
157 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/triangle.svg',
158 ],
159 'diamond' => [
160 'title' => esc_html__( 'Diamond', 'elementor' ),
161 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/diamond.svg',
162 ],
163 'pentagon' => [
164 'title' => esc_html__( 'Pentagon', 'elementor' ),
165 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/pentagon.svg',
166 ],
167 'hexagon-vertical' => [
168 'title' => esc_html__( 'Hexagon vertical', 'elementor' ),
169 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/hexagon-vertical.svg',
170 ],
171 'hexagon-horizontal' => [
172 'title' => esc_html__( 'Hexagon horizontal', 'elementor' ),
173 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/hexagon-horizontal.svg',
174 ],
175 'heptagon' => [
176 'title' => esc_html__( 'Heptagon', 'elementor' ),
177 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/heptagon.svg',
178 ],
179 'octagon' => [
180 'title' => esc_html__( 'Octagon', 'elementor' ),
181 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/octagon.svg',
182 ],
183 'parallelogram-right' => [
184 'title' => esc_html__( 'Parallelogram right', 'elementor' ),
185 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/parallelogram-right.svg',
186 ],
187 'parallelogram-left' => [
188 'title' => esc_html__( 'Parallelogram left', 'elementor' ),
189 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/parallelogram-left.svg',
190 ],
191 'trapezoid-up' => [
192 'title' => esc_html__( 'Trapezoid Up', 'elementor' ),
193 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/trapezoid-up.svg',
194 ],
195 'trapezoid-down' => [
196 'title' => esc_html__( 'Trapezoid Down', 'elementor' ),
197 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/trapezoid-down.svg',
198 ],
199 'flower' => [
200 'title' => esc_html__( 'Flower', 'elementor' ),
201 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/flower.svg',
202 ],
203 'sketch' => [
204 'title' => esc_html__( 'Sketch', 'elementor' ),
205 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/sketch.svg',
206 ],
207 'hexagon' => [
208 'title' => esc_html__( 'Hexagon Donut', 'elementor' ),
209 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/hexagon.svg',
210 ],
211 'blob' => [
212 'title' => esc_html__( 'Blob', 'elementor' ),
213 'image' => ELEMENTOR_ASSETS_URL . 'mask-shapes/blob.svg',
214 ],
215 ];
216
217 if ( $add_custom ) {
218 $shapes['custom'] = [
219 'type' => 'button',
220 'title' => esc_html__( 'Custom Mask', 'elementor' ),
221 ];
222 }
223
224 return $shapes;
225 }
226
227 /**
228 * Gets a string of CSS rules to apply, and returns an array of selectors with those rules.
229 * This function has been created in order to deal with masking for image widget.
230 * For most of the widgets the mask is being applied to the wrapper itself, but in the case of an image widget,
231 * the `img` tag should be masked directly. So instead of writing a lot of selectors every time,
232 * this function builds both of those selectors easily.
233 *
234 * @param string $rules The CSS rules to apply.
235 *
236 * @return array Selectors with the rules applied.
237 */
238 private function get_mask_selectors( $rules ) {
239 $mask_selectors = [
240 'default' => static::MASK_SELECTOR_DEFAULT,
241 'image' => static::MASK_SELECTOR_IMG,
242 ];
243
244 return [
245 $mask_selectors['default'] => $rules,
246 $mask_selectors['image'] => $rules,
247 ];
248 }
249
250 /**
251 * Register the Layout section.
252 *
253 * @return void
254 */
255 private function register_layout_section() {
256 $this->start_controls_section(
257 '_section_style',
258 [
259 'label' => esc_html__( 'Layout', 'elementor' ),
260 'tab' => Controls_Manager::TAB_ADVANCED,
261 ]
262 );
263
264 // Element Name for the Navigator
265 $this->add_control(
266 '_title',
267 [
268 'label' => esc_html__( 'Title', 'elementor' ),
269 'type' => Controls_Manager::HIDDEN,
270 'render_type' => 'none',
271 ]
272 );
273
274 $this->add_responsive_control(
275 '_margin',
276 [
277 'label' => esc_html__( 'Margin', 'elementor' ),
278 'type' => Controls_Manager::DIMENSIONS,
279 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
280 'selectors' => [
281 static::WRAPPER_SELECTOR_CHILD => static::MARGIN,
282 ],
283 ]
284 );
285
286 $this->add_responsive_control(
287 '_padding',
288 [
289 'label' => esc_html__( 'Padding', 'elementor' ),
290 'type' => Controls_Manager::DIMENSIONS,
291 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
292 'selectors' => [
293 static::WRAPPER_SELECTOR_CHILD => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
294 ],
295 ]
296 );
297
298 $experiments_manager = Plugin::$instance->experiments;
299 $is_container_active = $experiments_manager->is_feature_active( 'container' );
300
301 $this->add_responsive_control(
302 '_element_width',
303 [
304 'label' => esc_html__( 'Width', 'elementor' ),
305 'type' => Controls_Manager::SELECT,
306 'default' => '',
307 'options' => [
308 '' => esc_html__( 'Default', 'elementor' ),
309 'inherit' => esc_html__( 'Full Width', 'elementor' ) . ' (100%)',
310 'auto' => esc_html__( 'Inline', 'elementor' ) . ' (auto)',
311 'initial' => esc_html__( 'Custom', 'elementor' ),
312 ],
313 'selectors_dictionary' => [
314 'inherit' => '100%',
315 ],
316 'prefix_class' => 'elementor-widget%s__width-',
317 'selectors' => [
318 '{{WRAPPER}}' => 'width: {{VALUE}}; max-width: {{VALUE}}',
319 ],
320 ]
321 );
322
323 $this->add_responsive_control(
324 '_element_custom_width',
325 [
326 'label' => esc_html__( 'Custom Width', 'elementor' ),
327 'type' => Controls_Manager::SLIDER,
328 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
329 'default' => [
330 'unit' => '%',
331 ],
332 'range' => [
333 'px' => [
334 'max' => 1000,
335 ],
336 ],
337 'selectors' => [
338 '{{WRAPPER}}' => '--container-widget-width: {{SIZE}}{{UNIT}}; --container-widget-flex-grow: 0; width: var( --container-widget-width, {{SIZE}}{{UNIT}} ); max-width: {{SIZE}}{{UNIT}}',
339 ],
340 'condition' => [ '_element_width' => 'initial' ],
341 ]
342 );
343
344 $this->add_control(
345 '_heading_grid_item',
346 [
347 'type' => Controls_Manager::HEADING,
348 'label' => esc_html__( 'Grid Item', 'elementor' ),
349 'separator' => 'before',
350 ]
351 );
352
353 $this->add_responsive_control(
354 '_grid_column',
355 [
356 'label' => esc_html__( 'Column Span', 'elementor' ),
357 'type' => Controls_Manager::SELECT,
358 'options' => [
359 '' => ' Default',
360 '1' => '1',
361 '2' => '2',
362 '3' => '3',
363 '4' => '4',
364 '5' => '5',
365 '6' => '6',
366 '7' => '7',
367 '8' => '8',
368 '9' => '9',
369 '10' => '10',
370 '11' => '11',
371 '12' => '12',
372 'custom' => 'Custom',
373 ],
374 'selectors' => [
375 '{{WRAPPER}}' => 'grid-column: span {{VALUE}};',
376 ],
377 ]
378 );
379
380 $this->add_responsive_control(
381 '_grid_column_custom',
382 [
383 'label' => esc_html__( 'Custom', 'elementor' ),
384 'type' => Controls_Manager::TEXT,
385 'ai' => [
386 'active' => false,
387 ],
388 'selectors' => [
389 '{{WRAPPER}}' => 'grid-column: {{VALUE}}',
390 ],
391 'condition' => [
392 '_grid_column' => 'custom',
393 ],
394 ]
395 );
396
397 $this->add_responsive_control(
398 '_grid_row',
399 [
400 'label' => esc_html__( 'Row Span', 'elementor' ),
401 'type' => Controls_Manager::SELECT,
402 'options' => [
403 '' => ' Default',
404 '1' => '1',
405 '2' => '2',
406 '3' => '3',
407 '4' => '4',
408 '5' => '5',
409 '6' => '6',
410 '7' => '7',
411 '8' => '8',
412 '9' => '9',
413 '10' => '10',
414 '11' => '11',
415 '12' => '12',
416 'custom' => 'Custom',
417 ],
418 'selectors' => [
419 '{{WRAPPER}}' => 'grid-row: span {{VALUE}};',
420 ],
421 ]
422 );
423
424 $this->add_responsive_control(
425 '_grid_row_custom',
426 [
427 'label' => esc_html__( 'Custom', 'elementor' ),
428 'type' => Controls_Manager::TEXT,
429 'separator' => 'after',
430 'ai' => [
431 'active' => false,
432 ],
433 'selectors' => [
434 '{{WRAPPER}}' => 'grid-row: {{VALUE}}',
435 ],
436 'condition' => [
437 '_grid_row' => 'custom',
438 ],
439 ]
440 );
441
442 // Register Flex controls only if the Container experiment is active.
443 if ( $is_container_active ) {
444 $this->add_group_control(
445 Group_Control_Flex_Item::get_type(),
446 [
447 'name' => '_flex',
448 // Hack to increase specificity and make sure that the current widget overrides the
449 // parent flex settings.
450 'selector' => '{{WRAPPER}}.elementor-element',
451 'include' => [
452 'align_self',
453 'order',
454 'order_custom',
455 'size',
456 'grow',
457 'shrink',
458 ],
459 'fields_options' => [
460 'align_self' => [
461 'separator' => 'before',
462 ],
463 ],
464 ]
465 );
466 }
467
468 $vertical_align_conditions = [
469 '_element_width!' => '',
470 '_position' => '',
471 ];
472
473 if ( $is_container_active ) {
474 $vertical_align_conditions['_element_vertical_align!'] = ''; // TODO: For BC.
475 }
476
477 // TODO: For BC - Remove in the future.
478 $this->add_responsive_control(
479 '_element_vertical_align',
480 [
481 'label' => esc_html__( 'Vertical Align', 'elementor' ),
482 'type' => Controls_Manager::CHOOSE,
483 'options' => [
484 'flex-start' => [
485 'title' => esc_html__( 'Start', 'elementor' ),
486 'icon' => 'eicon-v-align-top',
487 ],
488 'center' => [
489 'title' => esc_html__( 'Center', 'elementor' ),
490 'icon' => 'eicon-v-align-middle',
491 ],
492 'flex-end' => [
493 'title' => esc_html__( 'End', 'elementor' ),
494 'icon' => 'eicon-v-align-bottom',
495 ],
496 ],
497 'condition' => $vertical_align_conditions,
498 'selectors' => [
499 '{{WRAPPER}}' => 'align-self: {{VALUE}}',
500 ],
501 ]
502 );
503
504 $this->add_control(
505 '_position_description',
506 [
507 'type' => Controls_Manager::ALERT,
508 'alert_type' => 'warning',
509 'heading' => esc_html__( 'Please note!', 'elementor' ),
510 'content' => esc_html__( 'Custom positioning is not considered best practice for responsive web design and should not be used too frequently.', 'elementor' ),
511 'render_type' => 'ui',
512 'condition' => [
513 '_position!' => '',
514 ],
515 ]
516 );
517
518 $this->add_control(
519 '_position',
520 [
521 'label' => esc_html__( 'Position', 'elementor' ),
522 'type' => Controls_Manager::SELECT,
523 'default' => '',
524 'options' => [
525 '' => esc_html__( 'Default', 'elementor' ),
526 'absolute' => esc_html__( 'Absolute', 'elementor' ),
527 'fixed' => esc_html__( 'Fixed', 'elementor' ),
528 ],
529 'prefix_class' => 'elementor-',
530 'frontend_available' => true,
531 'separator' => 'before',
532 ]
533 );
534
535 $start = is_rtl() ? esc_html__( 'Right', 'elementor' ) : esc_html__( 'Left', 'elementor' );
536 $end = ! is_rtl() ? esc_html__( 'Right', 'elementor' ) : esc_html__( 'Left', 'elementor' );
537
538 $this->add_control(
539 '_offset_orientation_h',
540 [
541 'label' => esc_html__( 'Horizontal Orientation', 'elementor' ),
542 'type' => Controls_Manager::CHOOSE,
543 'toggle' => false,
544 'default' => 'start',
545 'options' => [
546 'start' => [
547 'title' => $start,
548 'icon' => 'eicon-h-align-left',
549 ],
550 'end' => [
551 'title' => $end,
552 'icon' => 'eicon-h-align-right',
553 ],
554 ],
555 'classes' => 'elementor-control-start-end',
556 'render_type' => 'ui',
557 'condition' => [
558 '_position!' => '',
559 ],
560 ]
561 );
562
563 $this->add_responsive_control(
564 '_offset_x',
565 [
566 'label' => esc_html__( 'Offset', 'elementor' ),
567 'type' => Controls_Manager::SLIDER,
568 'range' => [
569 'px' => [
570 'min' => -1000,
571 'max' => 1000,
572 ],
573 '%' => [
574 'min' => -200,
575 'max' => 200,
576 ],
577 'vw' => [
578 'min' => -200,
579 'max' => 200,
580 ],
581 'vh' => [
582 'min' => -200,
583 'max' => 200,
584 ],
585 ],
586 'default' => [
587 'size' => 0,
588 ],
589 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'vh', 'custom' ],
590 'selectors' => [
591 'body:not(.rtl) {{WRAPPER}}' => 'left: {{SIZE}}{{UNIT}}',
592 'body.rtl {{WRAPPER}}' => 'right: {{SIZE}}{{UNIT}}',
593 ],
594 'condition' => [
595 '_offset_orientation_h!' => 'end',
596 '_position!' => '',
597 ],
598 ]
599 );
600
601 $this->add_responsive_control(
602 '_offset_x_end',
603 [
604 'label' => esc_html__( 'Offset', 'elementor' ),
605 'type' => Controls_Manager::SLIDER,
606 'range' => [
607 'px' => [
608 'min' => -1000,
609 'max' => 1000,
610 ],
611 '%' => [
612 'min' => -200,
613 'max' => 200,
614 ],
615 'vw' => [
616 'min' => -200,
617 'max' => 200,
618 ],
619 'vh' => [
620 'min' => -200,
621 'max' => 200,
622 ],
623 ],
624 'default' => [
625 'size' => 0,
626 ],
627 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'vh', 'custom' ],
628 'selectors' => [
629 'body:not(.rtl) {{WRAPPER}}' => 'right: {{SIZE}}{{UNIT}}',
630 'body.rtl {{WRAPPER}}' => 'left: {{SIZE}}{{UNIT}}',
631 ],
632 'condition' => [
633 '_offset_orientation_h' => 'end',
634 '_position!' => '',
635 ],
636 ]
637 );
638
639 $this->add_control(
640 '_offset_orientation_v',
641 [
642 'label' => esc_html__( 'Vertical Orientation', 'elementor' ),
643 'type' => Controls_Manager::CHOOSE,
644 'toggle' => false,
645 'default' => 'start',
646 'options' => [
647 'start' => [
648 'title' => esc_html__( 'Top', 'elementor' ),
649 'icon' => 'eicon-v-align-top',
650 ],
651 'end' => [
652 'title' => esc_html__( 'Bottom', 'elementor' ),
653 'icon' => 'eicon-v-align-bottom',
654 ],
655 ],
656 'render_type' => 'ui',
657 'condition' => [
658 '_position!' => '',
659 ],
660 ]
661 );
662
663 $this->add_responsive_control(
664 '_offset_y',
665 [
666 'label' => esc_html__( 'Offset', 'elementor' ),
667 'type' => Controls_Manager::SLIDER,
668 'range' => [
669 'px' => [
670 'min' => -1000,
671 'max' => 1000,
672 ],
673 '%' => [
674 'min' => -200,
675 'max' => 200,
676 ],
677 'vh' => [
678 'min' => -200,
679 'max' => 200,
680 ],
681 'vw' => [
682 'min' => -200,
683 'max' => 200,
684 ],
685 ],
686 'size_units' => [ 'px', '%', 'em', 'rem', 'vh', 'vw', 'custom' ],
687 'default' => [
688 'size' => 0,
689 ],
690 'selectors' => [
691 '{{WRAPPER}}' => 'top: {{SIZE}}{{UNIT}}',
692 ],
693 'condition' => [
694 '_offset_orientation_v!' => 'end',
695 '_position!' => '',
696 ],
697 ]
698 );
699
700 $this->add_responsive_control(
701 '_offset_y_end',
702 [
703 'label' => esc_html__( 'Offset', 'elementor' ),
704 'type' => Controls_Manager::SLIDER,
705 'range' => [
706 'px' => [
707 'min' => -1000,
708 'max' => 1000,
709 ],
710 '%' => [
711 'min' => -200,
712 'max' => 200,
713 ],
714 'vh' => [
715 'min' => -200,
716 'max' => 200,
717 ],
718 'vw' => [
719 'min' => -200,
720 'max' => 200,
721 ],
722 ],
723 'size_units' => [ 'px', '%', 'em', 'rem', 'vh', 'vw', 'custom' ],
724 'default' => [
725 'size' => 0,
726 ],
727 'selectors' => [
728 '{{WRAPPER}}' => 'bottom: {{SIZE}}{{UNIT}}',
729 ],
730 'condition' => [
731 '_offset_orientation_v' => 'end',
732 '_position!' => '',
733 ],
734 ]
735 );
736
737 $this->add_responsive_control(
738 '_z_index',
739 [
740 'label' => esc_html__( 'Z-Index', 'elementor' ),
741 'type' => Controls_Manager::NUMBER,
742 'selectors' => [
743 '{{WRAPPER}}' => 'z-index: {{VALUE}};',
744 ],
745 ]
746 );
747
748 $this->add_control(
749 '_element_id',
750 [
751 'label' => esc_html__( 'CSS ID', 'elementor' ),
752 'type' => Controls_Manager::TEXT,
753 'dynamic' => [
754 'active' => true,
755 ],
756 'ai' => [
757 'active' => false,
758 ],
759 'default' => '',
760 'title' => esc_html__( 'Add your custom id WITHOUT the Pound key. e.g: my-id', 'elementor' ),
761 'style_transfer' => false,
762 'classes' => 'elementor-control-direction-ltr',
763 ]
764 );
765
766 $this->add_control(
767 '_css_classes',
768 [
769 'label' => esc_html__( 'CSS Classes', 'elementor' ),
770 'type' => Controls_Manager::TEXT,
771 'ai' => [
772 'active' => false,
773 ],
774 'dynamic' => [
775 'active' => true,
776 ],
777 'prefix_class' => '',
778 'title' => esc_html__( 'Add your custom class WITHOUT the dot. e.g: my-class', 'elementor' ),
779 'classes' => 'elementor-control-direction-ltr',
780 ]
781 );
782
783 Plugin::$instance->controls_manager->add_display_conditions_controls( $this );
784
785 $this->end_controls_section();
786 }
787
788 /**
789 * Register the Motion Effects section.
790 *
791 * @return void
792 */
793 private function register_effects_section() {
794 $this->start_controls_section(
795 'section_effects',
796 [
797 'label' => esc_html__( 'Motion Effects', 'elementor' ),
798 'tab' => Controls_Manager::TAB_ADVANCED,
799 ]
800 );
801
802 Plugin::$instance->controls_manager->add_motion_effects_promotion_control( $this );
803
804 $this->add_responsive_control(
805 '_animation',
806 [
807 'label' => esc_html__( 'Entrance Animation', 'elementor' ),
808 'type' => Controls_Manager::ANIMATION,
809 'frontend_available' => true,
810 ]
811 );
812
813 $this->add_control(
814 'animation_duration',
815 [
816 'label' => esc_html__( 'Animation Duration', 'elementor' ),
817 'type' => Controls_Manager::SELECT,
818 'default' => '',
819 'options' => [
820 'slow' => esc_html__( 'Slow', 'elementor' ),
821 '' => esc_html__( 'Normal', 'elementor' ),
822 'fast' => esc_html__( 'Fast', 'elementor' ),
823 ],
824 'prefix_class' => 'animated-',
825 'condition' => [
826 '_animation!' => '',
827 ],
828 ]
829 );
830
831 $this->add_control(
832 '_animation_delay',
833 [
834 'label' => esc_html__( 'Animation Delay', 'elementor' ) . ' (ms)',
835 'type' => Controls_Manager::NUMBER,
836 'default' => '',
837 'min' => 0,
838 'step' => 100,
839 'condition' => [
840 '_animation!' => '',
841 ],
842 'render_type' => 'none',
843 'frontend_available' => true,
844 ]
845 );
846
847 $this->end_controls_section();
848 }
849
850 /** Register the Background section.
851 *
852 * @return void
853 */
854 private function register_background_section() {
855 $this->start_controls_section(
856 '_section_background',
857 [
858 'label' => esc_html__( 'Background', 'elementor' ),
859 'tab' => Controls_Manager::TAB_ADVANCED,
860 ]
861 );
862
863 $this->start_controls_tabs( '_tabs_background' );
864
865 $this->start_controls_tab(
866 '_tab_background_normal',
867 [
868 'label' => esc_html__( 'Normal', 'elementor' ),
869 ]
870 );
871
872 $this->add_group_control(
873 Group_Control_Background::get_type(),
874 [
875 'name' => '_background',
876 'selector' => static::WRAPPER_SELECTOR_CHILD,
877 ]
878 );
879
880 $this->end_controls_tab();
881
882 $this->start_controls_tab(
883 '_tab_background_hover',
884 [
885 'label' => esc_html__( 'Hover', 'elementor' ),
886 ]
887 );
888
889 $this->add_group_control(
890 Group_Control_Background::get_type(),
891 [
892 'name' => '_background_hover',
893 'selector' => static::WRAPPER_SELECTOR_HOVER,
894 ]
895 );
896
897 $this->add_control(
898 '_background_hover_transition',
899 [
900 'label' => esc_html__( 'Transition Duration', 'elementor' ) . ' (s)',
901 'type' => Controls_Manager::SLIDER,
902 'range' => [
903 'px' => [
904 'min' => 0,
905 'max' => 3,
906 'step' => 0.1,
907 ],
908 ],
909 'render_type' => 'ui',
910 'separator' => 'before',
911 'selectors' => [
912 static::WRAPPER_SELECTOR_CHILD => 'transition: background {{SIZE}}s',
913 ],
914 ]
915 );
916
917 $this->end_controls_tab();
918
919 $this->end_controls_tabs();
920
921 $this->end_controls_section();
922 }
923
924 /**
925 * Register the Border section.
926 *
927 * @return void
928 */
929 private function register_border_section() {
930 $this->start_controls_section(
931 '_section_border',
932 [
933 'label' => esc_html__( 'Border', 'elementor' ),
934 'tab' => Controls_Manager::TAB_ADVANCED,
935 ]
936 );
937
938 $this->start_controls_tabs( '_tabs_border' );
939
940 $this->start_controls_tab(
941 '_tab_border_normal',
942 [
943 'label' => esc_html__( 'Normal', 'elementor' ),
944 ]
945 );
946
947 $this->add_group_control(
948 Group_Control_Border::get_type(),
949 [
950 'name' => '_border',
951 'selector' => static::WRAPPER_SELECTOR_CHILD,
952 ]
953 );
954
955 $this->add_responsive_control(
956 '_border_radius',
957 [
958 'label' => esc_html__( 'Border Radius', 'elementor' ),
959 'type' => Controls_Manager::DIMENSIONS,
960 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ],
961 'selectors' => [
962 static::WRAPPER_SELECTOR_CHILD => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
963 ],
964 ]
965 );
966
967 $this->add_group_control(
968 Group_Control_Box_Shadow::get_type(),
969 [
970 'name' => '_box_shadow',
971 'selector' => static::WRAPPER_SELECTOR_CHILD,
972 ]
973 );
974
975 $this->end_controls_tab();
976
977 $this->start_controls_tab(
978 '_tab_border_hover',
979 [
980 'label' => esc_html__( 'Hover', 'elementor' ),
981 ]
982 );
983
984 $this->add_group_control(
985 Group_Control_Border::get_type(),
986 [
987 'name' => '_border_hover',
988 'selector' => static::WRAPPER_SELECTOR_HOVER,
989 ]
990 );
991
992 $this->add_responsive_control(
993 '_border_radius_hover',
994 [
995 'label' => esc_html__( 'Border Radius', 'elementor' ),
996 'type' => Controls_Manager::DIMENSIONS,
997 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ],
998 'selectors' => [
999 static::WRAPPER_SELECTOR_HOVER_CHILD => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
1000 ],
1001 ]
1002 );
1003
1004 $this->add_group_control(
1005 Group_Control_Box_Shadow::get_type(),
1006 [
1007 'name' => '_box_shadow_hover',
1008 'selector' => static::WRAPPER_SELECTOR_HOVER,
1009 ]
1010 );
1011
1012 $this->add_control(
1013 '_border_hover_transition',
1014 [
1015 'label' => esc_html__( 'Transition Duration', 'elementor' ) . ' (s)',
1016 'type' => Controls_Manager::SLIDER,
1017 'separator' => 'before',
1018 'range' => [
1019 'px' => [
1020 'min' => 0,
1021 'max' => 3,
1022 'step' => 0.1,
1023 ],
1024 ],
1025 'selectors' => [
1026 static::WRAPPER_SELECTOR => 'transition: background {{_background_hover_transition.SIZE}}s, border {{SIZE}}s, border-radius {{SIZE}}s, box-shadow {{SIZE}}s',
1027 ],
1028 ]
1029 );
1030
1031 $this->end_controls_tab();
1032
1033 $this->end_controls_tabs();
1034
1035 $this->end_controls_section();
1036 }
1037
1038
1039 /**
1040 * Register the Mask section.
1041 *
1042 * @return void
1043 */
1044 private function register_masking_section() {
1045 $this->start_controls_section(
1046 '_section_masking',
1047 [
1048 'label' => esc_html__( 'Mask', 'elementor' ),
1049 'tab' => Controls_Manager::TAB_ADVANCED,
1050 ]
1051 );
1052
1053 $this->add_control(
1054 '_mask_switch',
1055 [
1056 'label' => esc_html__( 'Mask', 'elementor' ),
1057 'type' => Controls_Manager::SWITCHER,
1058 'label_on' => esc_html__( 'On', 'elementor' ),
1059 'label_off' => esc_html__( 'Off', 'elementor' ),
1060 'default' => '',
1061 ]
1062 );
1063
1064 $this->add_control(
1065 '_mask_shape',
1066 [
1067 'label' => esc_html__( 'Shape', 'elementor' ),
1068 'type' => Controls_Manager::VISUAL_CHOICE,
1069 'label_block' => true,
1070 'columns' => 4,
1071 'options' => $this->get_shapes(),
1072 'default' => 'circle',
1073 'selectors' => $this->get_mask_selectors( '-webkit-mask-image: url( ' . ELEMENTOR_ASSETS_URL . 'mask-shapes/{{VALUE}}.svg );' ),
1074 'condition' => [
1075 '_mask_switch!' => '',
1076 ],
1077 ]
1078 );
1079
1080 $this->add_control(
1081 '_mask_image',
1082 [
1083 'label' => esc_html__( 'Image', 'elementor' ),
1084 'type' => Controls_Manager::MEDIA,
1085 'media_types' => [ 'image' ],
1086 'should_include_svg_inline_option' => true,
1087 'library_type' => 'image/svg+xml',
1088 'dynamic' => [
1089 'active' => true,
1090 ],
1091 'selectors' => $this->get_mask_selectors( '-webkit-mask-image: url( {{URL}} );' ),
1092 'condition' => [
1093 '_mask_switch!' => '',
1094 '_mask_shape' => 'custom',
1095 ],
1096 ]
1097 );
1098
1099 $this->add_control(
1100 '_mask_notice',
1101 [
1102 'type' => Controls_Manager::HIDDEN,
1103 'raw' => esc_html__( 'Need More Shapes?', 'elementor' ) .
1104 '<br>' .
1105 sprintf(
1106 '%1$s <a target="_blank" href="https://go.elementor.com/mask-control">%2$s</a>',
1107 esc_html__( 'Explore additional Premium Shape packs and use them in your site.', 'elementor' ),
1108 esc_html__( 'Learn more', 'elementor' ),
1109 ),
1110 'content_classes' => 'elementor-panel-alert elementor-panel-alert-info',
1111 'condition' => [
1112 '_mask_switch!' => '',
1113 ],
1114 ]
1115 );
1116
1117 $this->add_responsive_control(
1118 '_mask_size',
1119 [
1120 'label' => esc_html__( 'Size', 'elementor' ),
1121 'type' => Controls_Manager::SELECT,
1122 'options' => [
1123 'contain' => esc_html__( 'Fit', 'elementor' ),
1124 'cover' => esc_html__( 'Fill', 'elementor' ),
1125 'custom' => esc_html__( 'Custom', 'elementor' ),
1126 ],
1127 'default' => 'contain',
1128 'selectors' => $this->get_mask_selectors( '-webkit-mask-size: {{VALUE}};' ),
1129 'condition' => [
1130 '_mask_switch!' => '',
1131 ],
1132 ]
1133 );
1134
1135 $this->add_responsive_control(
1136 '_mask_size_scale',
1137 [
1138 'label' => esc_html__( 'Scale', 'elementor' ),
1139 'type' => Controls_Manager::SLIDER,
1140 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
1141 'range' => [
1142 'px' => [
1143 'min' => 0,
1144 'max' => 500,
1145 ],
1146 'em' => [
1147 'min' => 0,
1148 'max' => 100,
1149 ],
1150 '%' => [
1151 'min' => 0,
1152 'max' => 200,
1153 ],
1154 ],
1155 'default' => [
1156 'unit' => '%',
1157 'size' => 100,
1158 ],
1159 'selectors' => $this->get_mask_selectors( '-webkit-mask-size: {{SIZE}}{{UNIT}};' ),
1160 'condition' => [
1161 '_mask_switch!' => '',
1162 '_mask_size' => 'custom',
1163 ],
1164 ]
1165 );
1166
1167 $this->add_responsive_control(
1168 '_mask_position',
1169 [
1170 'label' => esc_html__( 'Position', 'elementor' ),
1171 'type' => Controls_Manager::SELECT,
1172 'options' => [
1173 'center center' => esc_html__( 'Center Center', 'elementor' ),
1174 'center left' => esc_html__( 'Center Left', 'elementor' ),
1175 'center right' => esc_html__( 'Center Right', 'elementor' ),
1176 'top center' => esc_html__( 'Top Center', 'elementor' ),
1177 'top left' => esc_html__( 'Top Left', 'elementor' ),
1178 'top right' => esc_html__( 'Top Right', 'elementor' ),
1179 'bottom center' => esc_html__( 'Bottom Center', 'elementor' ),
1180 'bottom left' => esc_html__( 'Bottom Left', 'elementor' ),
1181 'bottom right' => esc_html__( 'Bottom Right', 'elementor' ),
1182 'custom' => esc_html__( 'Custom', 'elementor' ),
1183 ],
1184 'default' => 'center center',
1185 'selectors' => $this->get_mask_selectors( '-webkit-mask-position: {{VALUE}};' ),
1186 'condition' => [
1187 '_mask_switch!' => '',
1188 ],
1189 ]
1190 );
1191
1192 $this->add_responsive_control(
1193 '_mask_position_x',
1194 [
1195 'label' => esc_html__( 'X Position', 'elementor' ),
1196 'type' => Controls_Manager::SLIDER,
1197 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
1198 'range' => [
1199 'px' => [
1200 'min' => -500,
1201 'max' => 500,
1202 ],
1203 'em' => [
1204 'min' => -100,
1205 'max' => 100,
1206 ],
1207 '%' => [
1208 'min' => -100,
1209 'max' => 100,
1210 ],
1211 'vw' => [
1212 'min' => -100,
1213 'max' => 100,
1214 ],
1215 ],
1216 'default' => [
1217 'unit' => '%',
1218 'size' => 0,
1219 ],
1220 'selectors' => $this->get_mask_selectors( '-webkit-mask-position-x: {{SIZE}}{{UNIT}};' ),
1221 'condition' => [
1222 '_mask_switch!' => '',
1223 '_mask_position' => 'custom',
1224 ],
1225 ]
1226 );
1227
1228 $this->add_responsive_control(
1229 '_mask_position_y',
1230 [
1231 'label' => esc_html__( 'Y Position', 'elementor' ),
1232 'type' => Controls_Manager::SLIDER,
1233 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
1234 'range' => [
1235 'px' => [
1236 'min' => -500,
1237 'max' => 500,
1238 ],
1239 'em' => [
1240 'min' => -100,
1241 'max' => 100,
1242 ],
1243 '%' => [
1244 'min' => -100,
1245 'max' => 100,
1246 ],
1247 'vw' => [
1248 'min' => -100,
1249 'max' => 100,
1250 ],
1251 ],
1252 'default' => [
1253 'unit' => '%',
1254 'size' => 0,
1255 ],
1256 'selectors' => $this->get_mask_selectors( '-webkit-mask-position-y: {{SIZE}}{{UNIT}};' ),
1257 'condition' => [
1258 '_mask_switch!' => '',
1259 '_mask_position' => 'custom',
1260 ],
1261 ]
1262 );
1263
1264 $this->add_responsive_control(
1265 '_mask_repeat',
1266 [
1267 'label' => esc_html__( 'Repeat', 'elementor' ),
1268 'type' => Controls_Manager::SELECT,
1269 'options' => [
1270 'no-repeat' => esc_html__( 'No-repeat', 'elementor' ),
1271 'repeat' => esc_html__( 'Repeat', 'elementor' ),
1272 'repeat-x' => esc_html__( 'Repeat-x', 'elementor' ),
1273 'repeat-Y' => esc_html__( 'Repeat-y', 'elementor' ),
1274 'round' => esc_html__( 'Round', 'elementor' ),
1275 'space' => esc_html__( 'Space', 'elementor' ),
1276 ],
1277 'default' => 'no-repeat',
1278 'selectors' => $this->get_mask_selectors( '-webkit-mask-repeat: {{VALUE}};' ),
1279 'condition' => [
1280 '_mask_switch!' => '',
1281 '_mask_size!' => 'cover',
1282 ],
1283 ]
1284 );
1285
1286 $this->end_controls_section();
1287 }
1288
1289
1290 /**
1291 * Register the Responsive section.
1292 *
1293 * @return void
1294 */
1295 private function register_responsive_section() {
1296 $this->start_controls_section(
1297 '_section_responsive',
1298 [
1299 'label' => esc_html__( 'Responsive', 'elementor' ),
1300 'tab' => Controls_Manager::TAB_ADVANCED,
1301 ]
1302 );
1303
1304 $this->add_control(
1305 'responsive_description',
1306 [
1307 'raw' => sprintf(
1308 /* translators: 1: Link open tag, 2: Link close tag. */
1309 esc_html__( 'Responsive visibility will take effect only on %1$s preview mode %2$s or live page, and not while editing in Elementor.', 'elementor' ),
1310 '<a href="javascript: $e.run( \'panel/close\' )">',
1311 '</a>'
1312 ),
1313 'type' => Controls_Manager::RAW_HTML,
1314 'content_classes' => 'elementor-descriptor',
1315 ]
1316 );
1317
1318 $this->add_hidden_device_controls();
1319
1320 $this->end_controls_section();
1321 }
1322
1323 /**
1324 * Register common widget controls.
1325 *
1326 * Adds different input fields to allow the user to change and customize the widget settings.
1327 *
1328 * @since 3.1.0
1329 * @access protected
1330 */
1331 protected function register_controls() {
1332 $this->register_layout_section();
1333
1334 $this->register_effects_section();
1335
1336 $this->register_transform_section( '', static::TRANSFORM_SELECTOR_CLASS );
1337
1338 $this->register_background_section();
1339
1340 $this->register_border_section();
1341
1342 $this->register_masking_section();
1343
1344 $this->register_responsive_section();
1345
1346 $register_common_controls = apply_filters(
1347 'elementor/widget/common/register_css_attributes_control',
1348 true,
1349 $this
1350 );
1351
1352 if ( $register_common_controls ) {
1353 Plugin::$instance->controls_manager->add_custom_attributes_controls( $this );
1354
1355 Plugin::$instance->controls_manager->add_custom_css_controls( $this );
1356 }
1357 }
1358 }
1359