PluginProbe
Elementor Website Builder – more than just a page builder / 3.13.1
Elementor Website Builder – more than just a page builder v3.13.1
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 / modules / nested-tabs / widgets / nested-tabs.php

nested-tabs.php in Elementor Website Builder – more than just a page builder 3.13.1, at modules/nested-tabs/widgets/nested-tabs.php

1,183 lines 34.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Elementor\Modules\NestedTabs\Widgets;
3
4 use Elementor\Controls_Manager;
5 use Elementor\Core\Kits\Documents\Tabs\Global_Colors;
6 use Elementor\Core\Kits\Documents\Tabs\Global_Typography;
7 use Elementor\Group_Control_Background;
8 use Elementor\Group_Control_Border;
9 use Elementor\Group_Control_Box_Shadow;
10 use Elementor\Group_Control_Text_Shadow;
11 use Elementor\Group_Control_Text_Stroke;
12 use Elementor\Group_Control_Typography;
13 use Elementor\Icons_Manager;
14 use Elementor\Modules\NestedElements\Base\Widget_Nested_Base;
15 use Elementor\Modules\NestedElements\Controls\Control_Nested_Repeater;
16 use Elementor\Plugin;
17 use Elementor\Repeater;
18 use Elementor\Modules\DynamicTags\Module as TagsModule;
19 use Elementor\Utils;
20 use Elementor\Core\Breakpoints\Manager as Breakpoints_Manager;
21
22 if ( ! defined( 'ABSPATH' ) ) {
23 exit; // Exit if accessed directly
24 }
25
26 class NestedTabs extends Widget_Nested_Base {
27
28 public function get_name() {
29 return 'nested-tabs';
30 }
31
32 public function get_title() {
33 return esc_html__( 'Tabs', 'elementor' );
34 }
35
36 public function get_icon() {
37 return 'eicon-tabs';
38 }
39
40 public function get_keywords() {
41 return [ 'nested', 'tabs', 'accordion', 'toggle' ];
42 }
43
44 protected function tab_content_container( int $index ) {
45 return [
46 'elType' => 'container',
47 'settings' => [
48 '_title' => sprintf( __( 'Tab #%s', 'elementor' ), $index ),
49 'content_width' => 'full',
50 ],
51 ];
52 }
53
54 protected function get_default_children_elements() {
55 return [
56 $this->tab_content_container( 1 ),
57 $this->tab_content_container( 2 ),
58 $this->tab_content_container( 3 ),
59 ];
60 }
61
62 protected function get_default_repeater_title_setting_key() {
63 return 'tab_title';
64 }
65
66 protected function get_default_children_title() {
67 return esc_html__( 'Tab #%d', 'elementor' );
68 }
69
70 protected function get_default_children_placeholder_selector() {
71 return '.e-n-tabs-content';
72 }
73
74 protected function get_html_wrapper_class() {
75 return 'elementor-widget-n-tabs';
76 }
77
78 protected function register_controls() {
79 $start = is_rtl() ? 'right' : 'left';
80 $end = is_rtl() ? 'left' : 'right';
81 $tooltip_start = is_rtl() ? esc_html__( 'Right', 'elementor' ) : esc_html__( 'Left', 'elementor' );
82 $tooltip_end = is_rtl() ? esc_html__( 'Left', 'elementor' ) : esc_html__( 'Right', 'elementor' );
83 $nested_tabs_heading_selector_class = ':is( {{WRAPPER}} > .elementor-widget-container > .e-n-tabs > .e-n-tabs-heading, {{WRAPPER}} > .elementor-widget-container > .e-n-tabs > .e-n-tabs-content )';
84 $nested_tabs_content_selector_class = ':where( {{WRAPPER}} > .elementor-widget-container > .e-n-tabs > .e-n-tabs-content ) > .e-con';
85
86 $this->start_controls_section( 'section_tabs', [
87 'label' => esc_html__( 'Tabs', 'elementor' ),
88 ] );
89
90 $repeater = new Repeater();
91
92 $repeater->add_control( 'tab_title', [
93 'label' => esc_html__( 'Title', 'elementor' ),
94 'type' => Controls_Manager::TEXT,
95 'default' => esc_html__( 'Tab Title', 'elementor' ),
96 'placeholder' => esc_html__( 'Tab Title', 'elementor' ),
97 'label_block' => true,
98 'dynamic' => [
99 'active' => true,
100 ],
101 ] );
102
103 $repeater->add_control(
104 'tab_icon',
105 [
106 'label' => esc_html__( 'Icon', 'elementor' ),
107 'type' => Controls_Manager::ICONS,
108 'fa4compatibility' => 'icon',
109 'skin' => 'inline',
110 'label_block' => false,
111 ]
112 );
113
114 $repeater->add_control(
115 'tab_icon_active',
116 [
117 'label' => esc_html__( 'Active Icon', 'elementor' ),
118 'type' => Controls_Manager::ICONS,
119 'fa4compatibility' => 'icon',
120 'skin' => 'inline',
121 'label_block' => false,
122 'condition' => [
123 'tab_icon[value]!' => '',
124 ],
125 ]
126 );
127
128 $repeater->add_control(
129 'element_id',
130 [
131 'label' => esc_html__( 'CSS ID', 'elementor' ),
132 'type' => Controls_Manager::TEXT,
133 'default' => '',
134 'ai' => [
135 'active' => false,
136 ],
137 'dynamic' => [
138 'active' => true,
139 ],
140 'title' => esc_html__( 'Add your custom id WITHOUT the Pound key. e.g: my-id', 'elementor' ),
141 'style_transfer' => false,
142 'classes' => 'elementor-control-direction-ltr',
143 ]
144 );
145
146 $this->add_control( 'tabs', [
147 'label' => esc_html__( 'Tabs Items', 'elementor' ),
148 'type' => Control_Nested_Repeater::CONTROL_TYPE,
149 'fields' => $repeater->get_controls(),
150 'default' => [
151 [
152 'tab_title' => esc_html__( 'Tab #1', 'elementor' ),
153 ],
154 [
155 'tab_title' => esc_html__( 'Tab #2', 'elementor' ),
156 ],
157 [
158 'tab_title' => esc_html__( 'Tab #3', 'elementor' ),
159 ],
160 ],
161 'title_field' => '{{{ tab_title }}}',
162 'button_text' => 'Add Tab',
163 ] );
164
165 $this->add_responsive_control( 'tabs_direction', [
166 'label' => esc_html__( 'Direction', 'elementor' ),
167 'type' => Controls_Manager::CHOOSE,
168 'options' => [
169 'top' => [
170 'title' => esc_html__( 'Top', 'elementor' ),
171 'icon' => 'eicon-v-align-top',
172 ],
173 'bottom' => [
174 'title' => esc_html__( 'Bottom', 'elementor' ),
175 'icon' => 'eicon-v-align-bottom',
176 ],
177 'end' => [
178 'title' => $tooltip_end,
179 'icon' => 'eicon-h-align-' . $end,
180 ],
181 'start' => [
182 'title' => $tooltip_start,
183 'icon' => 'eicon-h-align-' . $start,
184 ],
185 ],
186 'separator' => 'before',
187 'selectors_dictionary' => [
188 'top' => '--n-tabs-direction: column; --n-tabs-heading-direction: row; --n-tabs-heading-width: initial;',
189 'bottom' => '--n-tabs-direction: column-reverse; --n-tabs-heading-direction: row; --n-tabs-heading-width: initial;',
190 'end' => '--n-tabs-direction: row-reverse; --n-tabs-heading-direction: column; --n-tabs-heading-width: 240px;',
191 'start' => '--n-tabs-direction: row; --n-tabs-heading-direction: column; --n-tabs-heading-width: 240px;',
192 ],
193 'selectors' => [
194 '{{WRAPPER}}' => '{{VALUE}}',
195 ],
196 ] );
197
198 $this->add_responsive_control( 'tabs_justify_horizontal', [
199 'label' => esc_html__( 'Justify', 'elementor' ),
200 'type' => Controls_Manager::CHOOSE,
201 'options' => [
202 'start' => [
203 'title' => esc_html__( 'Start', 'elementor' ),
204 'icon' => 'eicon-flex eicon-align-start-h',
205 ],
206 'center' => [
207 'title' => esc_html__( 'Center', 'elementor' ),
208 'icon' => 'eicon-h-align-center',
209 ],
210 'end' => [
211 'title' => esc_html__( 'End', 'elementor' ),
212 'icon' => 'eicon-flex eicon-align-end-h',
213 ],
214 'stretch' => [
215 'title' => esc_html__( 'Justified', 'elementor' ),
216 'icon' => 'eicon-h-align-stretch',
217 ],
218 ],
219 'selectors_dictionary' => [
220 'start' => '--n-tabs-heading-justify-content: flex-start; --n-tabs-title-width: initial; --n-tabs-title-height: initial; --n-tabs-title-align-items: center; --n-tabs-title-flex-grow: 0;',
221 'center' => '--n-tabs-heading-justify-content: center; --n-tabs-title-width: initial; --n-tabs-title-height: initial; --n-tabs-title-align-items: center; --n-tabs-title-flex-grow: 0;',
222 'end' => '--n-tabs-heading-justify-content: flex-end; --n-tabs-title-width: initial; --n-tabs-title-height: initial; --n-tabs-title-align-items: center; --n-tabs-title-flex-grow: 0;',
223 'stretch' => '--n-tabs-heading-justify-content: initial; --n-tabs-title-width: 100%; --n-tabs-title-height: initial; --n-tabs-title-align-items: center; --n-tabs-title-flex-grow: 1;',
224 ],
225 'selectors' => [
226 '{{WRAPPER}}' => '{{VALUE}}',
227 ],
228 'condition' => [
229 'tabs_direction' => [
230 '',
231 'top',
232 'bottom',
233 ],
234 ],
235 ] );
236
237 $this->add_responsive_control( 'tabs_justify_vertical', [
238 'label' => esc_html__( 'Justify', 'elementor' ),
239 'type' => Controls_Manager::CHOOSE,
240 'options' => [
241 'start' => [
242 'title' => esc_html__( 'Start', 'elementor' ),
243 'icon' => 'eicon-flex eicon-align-start-v',
244 ],
245 'center' => [
246 'title' => esc_html__( 'Center', 'elementor' ),
247 'icon' => 'eicon-v-align-middle',
248 ],
249 'end' => [
250 'title' => esc_html__( 'End', 'elementor' ),
251 'icon' => 'eicon-flex eicon-align-end-v',
252 ],
253 'stretch' => [
254 'title' => esc_html__( 'Justified', 'elementor' ),
255 'icon' => 'eicon-v-align-stretch',
256 ],
257 ],
258 'selectors_dictionary' => [
259 'start' => '--n-tabs-heading-justify-content: flex-start; --n-tabs-title-width: initial; --n-tabs-title-height: initial; --n-tabs-title-align-items: initial;',
260 'center' => '--n-tabs-heading-justify-content: center; --n-tabs-title-width: initial; --n-tabs-title-height: initial; --n-tabs-title-align-items: initial;',
261 'end' => '--n-tabs-heading-justify-content: flex-end; --n-tabs-title-width: initial; --n-tabs-title-height: initial; --n-tabs-title-align-items: initial;',
262 'stretch' => '--n-tabs-heading-justify-content: flex-start; --n-tabs-title-width: initial; --n-tabs-title-height: 100%; --n-tabs-title-align-items: center;',
263 ],
264 'selectors' => [
265 '{{WRAPPER}}' => '{{VALUE}}',
266 ],
267 'condition' => [
268 'tabs_direction' => [
269 'start',
270 'end',
271 ],
272 ],
273 ] );
274
275 $this->add_responsive_control( 'tabs_width', [
276 'label' => esc_html__( 'Width', 'elementor' ),
277 'type' => Controls_Manager::SLIDER,
278 'range' => [
279 '%' => [
280 'min' => 10,
281 'max' => 50,
282 ],
283 'px' => [
284 'min' => 20,
285 'max' => 600,
286 ],
287 ],
288 'default' => [
289 'unit' => '%',
290 ],
291 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
292 'selectors' => [
293 '{{WRAPPER}}' => '--n-tabs-heading-width: {{SIZE}}{{UNIT}}',
294 ],
295 'condition' => [
296 'tabs_direction' => [
297 'start',
298 'end',
299 ],
300 ],
301 ] );
302
303 $this->add_responsive_control( 'title_alignment', [
304 'label' => esc_html__( 'Align Title', 'elementor' ),
305 'type' => Controls_Manager::CHOOSE,
306 'options' => [
307 'start' => [
308 'title' => esc_html__( 'Left', 'elementor' ),
309 'icon' => 'eicon-text-align-left',
310 ],
311 'center' => [
312 'title' => esc_html__( 'Center', 'elementor' ),
313 'icon' => 'eicon-text-align-center',
314 ],
315 'end' => [
316 'title' => esc_html__( 'Right', 'elementor' ),
317 'icon' => 'eicon-text-align-right',
318 ],
319 ],
320 'selectors_dictionary' => [
321 'start' => '--n-tabs-title-justify-content: flex-start; --n-tabs-title-align-items: flex-start;',
322 'center' => '--n-tabs-title-justify-content: center; --n-tabs-title-align-items: center;',
323 'end' => '--n-tabs-title-justify-content: flex-end; --n-tabs-title-align-items: flex-end;',
324 ],
325 'selectors' => [
326 '{{WRAPPER}}' => '{{VALUE}}',
327 ],
328 ] );
329
330 $this->end_controls_section();
331
332 $this->start_controls_section( 'section_tabs_responsive', [
333 'label' => esc_html__( 'Responsive Settings', 'elementor' ),
334 ] );
335
336 $dropdown_options = [];
337 $excluded_breakpoints = [
338 'laptop',
339 'tablet_extra',
340 'widescreen',
341 ];
342
343 foreach ( Plugin::$instance->breakpoints->get_active_breakpoints() as $breakpoint_key => $breakpoint_instance ) {
344 // Exclude the larger breakpoints from the dropdown selector.
345 if ( in_array( $breakpoint_key, $excluded_breakpoints, true ) ) {
346 continue;
347 }
348
349 $dropdown_options[ $breakpoint_key ] = sprintf(
350 /* translators: 1: Breakpoint label, 2: `>` character, 3: Breakpoint value. */
351 esc_html__( '%1$s (%2$s %3$dpx)', 'elementor' ),
352 $breakpoint_instance->get_label(),
353 '>',
354 $breakpoint_instance->get_value()
355 );
356 }
357
358 $this->add_control(
359 'breakpoint_selector',
360 [
361 'label' => esc_html__( 'Breakpoint', 'elementor' ),
362 'type' => Controls_Manager::SELECT,
363 'description' => esc_html__( 'Note: Choose at which breakpoint tabs will automatically switch to a vertical (“accordion”) layout.', 'elementor' ),
364 'options' => $dropdown_options,
365 'default' => 'mobile',
366 'prefix_class' => 'e-n-tabs-',
367 ]
368 );
369
370 $this->end_controls_section();
371
372 $this->start_controls_section( 'section_tabs_style', [
373 'label' => esc_html__( 'Tabs', 'elementor' ),
374 'tab' => Controls_Manager::TAB_STYLE,
375 ] );
376
377 $this->add_responsive_control( 'tabs_title_space_between', [
378 'label' => esc_html__( 'Gap between tabs', 'elementor' ),
379 'type' => Controls_Manager::SLIDER,
380 'range' => [
381 'px' => [
382 'min' => 0,
383 'max' => 400,
384 ],
385 ],
386 'size_units' => [ 'px' ],
387 'selectors' => [
388 '{{WRAPPER}}' => '--n-tabs-title-gap: {{SIZE}}{{UNIT}}',
389 ],
390 ] );
391
392 $this->add_responsive_control( 'tabs_title_spacing', [
393 'label' => esc_html__( 'Distance from content', 'elementor' ),
394 'type' => Controls_Manager::SLIDER,
395 'range' => [
396 'px' => [
397 'min' => 0,
398 'max' => 400,
399 ],
400 ],
401 'size_units' => [ 'px' ],
402 'selectors' => [
403 '{{WRAPPER}}' => '--n-tabs-gap: {{SIZE}}{{UNIT}}',
404 ],
405 ] );
406
407 $this->start_controls_tabs( 'tabs_title_style' );
408
409 $this->start_controls_tab(
410 'tabs_title_normal',
411 [
412 'label' => esc_html__( 'Normal', 'elementor' ),
413 ]
414 );
415
416 $this->add_group_control(
417 Group_Control_Background::get_type(),
418 [
419 'name' => 'tabs_title_background_color',
420 'types' => [ 'classic', 'gradient' ],
421 'exclude' => [ 'image' ],
422 'selector' => ':is( {{WRAPPER}} > .elementor-widget-container > .e-n-tabs > .e-n-tabs-heading > .e-n-tab-title:not( .e-active ):not( :hover ), {{WRAPPER}} > .elementor-widget-container > .e-n-tabs > .e-n-tabs-content > .e-n-tab-title:not( .e-active ) )',
423 'fields_options' => [
424 'color' => [
425 'label' => esc_html__( 'Background Color', 'elementor' ),
426 'selectors' => [
427 '{{SELECTOR}}' => 'background: {{VALUE}}',
428 ],
429 ],
430 ],
431 ]
432 );
433
434 $this->add_group_control(
435 Group_Control_Border::get_type(),
436 [
437 'name' => 'tabs_title_border',
438 'selector' => "{$nested_tabs_heading_selector_class} > .e-n-tab-title:not( .e-active ):not( :hover )",
439 'fields_options' => [
440 'color' => [
441 'label' => esc_html__( 'Border Color', 'elementor' ),
442 ],
443 'width' => [
444 'label' => esc_html__( 'Border Width', 'elementor' ),
445 ],
446 ],
447 ]
448 );
449
450 $this->add_group_control(
451 Group_Control_Box_Shadow::get_type(),
452 [
453 'name' => 'tabs_title_box_shadow',
454 'label' => esc_html__( 'Shadow', 'elementor' ),
455 'separator' => 'after',
456 'selector' => "{$nested_tabs_heading_selector_class} > .e-n-tab-title:not( .e-active ):not( :hover )",
457 ]
458 );
459
460 $this->end_controls_tab();
461
462 $this->start_controls_tab(
463 'tabs_title_hover',
464 [
465 'label' => esc_html__( 'Hover', 'elementor' ),
466 ]
467 );
468
469 $this->add_group_control(
470 Group_Control_Background::get_type(),
471 [
472 'name' => 'tabs_title_background_color_hover',
473 'types' => [ 'classic', 'gradient' ],
474 'exclude' => [ 'image' ],
475 'selector' => "{$nested_tabs_heading_selector_class} > .e-normal:not( .e-active ):hover",
476 'fields_options' => [
477 'background' => [
478 'default' => 'classic',
479 ],
480 'color' => [
481 'global' => [
482 'default' => Global_Colors::COLOR_ACCENT,
483 ],
484 'label' => esc_html__( 'Background Color', 'elementor' ),
485 'selectors' => [
486 '{{SELECTOR}}' => 'background: {{VALUE}};',
487 ],
488 ],
489 ],
490 ]
491 );
492
493 $this->add_group_control(
494 Group_Control_Border::get_type(),
495 [
496 'name' => 'tabs_title_border_hover',
497 'selector' => "{$nested_tabs_heading_selector_class} > .e-normal:not( .e-active ):hover",
498 'fields_options' => [
499 'color' => [
500 'label' => esc_html__( 'Border Color', 'elementor' ),
501 ],
502 'width' => [
503 'label' => esc_html__( 'Border Width', 'elementor' ),
504 ],
505 ],
506 ]
507 );
508
509 $this->add_group_control(
510 Group_Control_Box_Shadow::get_type(),
511 [
512 'name' => 'tabs_title_box_shadow_hover',
513 'label' => esc_html__( 'Shadow', 'elementor' ),
514 'separator' => 'after',
515 'selector' => "{$nested_tabs_heading_selector_class} > .e-normal:not( .e-active ):hover",
516 ]
517 );
518
519 $this->add_control(
520 'hover_animation',
521 [
522 'label' => esc_html__( 'Hover Animation', 'elementor' ),
523 'type' => Controls_Manager::HOVER_ANIMATION,
524 ]
525 );
526
527 $this->add_control(
528 'tabs_title_transition_duration',
529 [
530 'label' => esc_html__( 'Transition Duration', 'elementor' ) . ' (s)',
531 'type' => Controls_Manager::SLIDER,
532 'selectors' => [
533 '{{WRAPPER}}' => '--n-tabs-title-transition: {{SIZE}}s',
534 ],
535 'range' => [
536 'px' => [
537 'max' => 3,
538 'step' => 0.1,
539 ],
540 ],
541 ]
542 );
543
544 $this->end_controls_tab();
545
546 $this->start_controls_tab(
547 'tabs_title_active',
548 [
549 'label' => esc_html__( 'Active', 'elementor' ),
550 ]
551 );
552
553 $this->add_group_control(
554 Group_Control_Background::get_type(),
555 [
556 'name' => 'tabs_title_background_color_active',
557 'types' => [ 'classic', 'gradient' ],
558 'exclude' => [ 'image' ],
559 'selector' => "{$nested_tabs_heading_selector_class} > .e-n-tab-title.e-active",
560 'fields_options' => [
561 'background' => [
562 'default' => 'classic',
563 ],
564 'color' => [
565 'global' => [
566 'default' => Global_Colors::COLOR_ACCENT,
567 ],
568 'label' => esc_html__( 'Background Color', 'elementor' ),
569 'selectors' => [
570 '{{SELECTOR}}' => 'background: {{VALUE}};',
571 ],
572 ],
573 ],
574 ]
575 );
576
577 $this->add_group_control(
578 Group_Control_Border::get_type(),
579 [
580 'name' => 'tabs_title_border_active',
581 'selector' => "{$nested_tabs_heading_selector_class} > .e-n-tab-title.e-active",
582 'fields_options' => [
583 'color' => [
584 'label' => esc_html__( 'Border Color', 'elementor' ),
585 ],
586 'width' => [
587 'label' => esc_html__( 'Border Width', 'elementor' ),
588 ],
589 ],
590 ]
591 );
592
593 $this->add_group_control(
594 Group_Control_Box_Shadow::get_type(),
595 [
596 'name' => 'tabs_title_box_shadow_active',
597 'label' => esc_html__( 'Shadow', 'elementor' ),
598 'selector' => "{$nested_tabs_heading_selector_class} > .e-n-tab-title.e-active",
599 ]
600 );
601
602 $this->end_controls_tab();
603
604 $this->end_controls_tabs();
605
606 $this->add_responsive_control(
607 'tabs_title_border_radius',
608 [
609 'label' => esc_html__( 'Border Radius', 'elementor' ),
610 'type' => Controls_Manager::DIMENSIONS,
611 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ],
612 'separator' => 'before',
613 'selectors' => [
614 '{{WRAPPER}}' => '--n-tabs-title-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
615 ],
616 ]
617 );
618
619 $this->add_responsive_control(
620 'padding',
621 [
622 'label' => esc_html__( 'Padding', 'elementor' ),
623 'type' => Controls_Manager::DIMENSIONS,
624 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
625 'selectors' => [
626 '{{WRAPPER}}' => '--n-tabs-title-padding-top: {{TOP}}{{UNIT}}; --n-tabs-title-padding-right: {{RIGHT}}{{UNIT}}; --n-tabs-title-padding-bottom: {{BOTTOM}}{{UNIT}}; --n-tabs-title-padding-left: {{LEFT}}{{UNIT}};',
627 ],
628 ]
629 );
630
631 $this->end_controls_section();
632
633 $this->start_controls_section( 'section_title_style', [
634 'label' => esc_html__( 'Titles', 'elementor' ),
635 'tab' => Controls_Manager::TAB_STYLE,
636 ] );
637
638 $this->add_group_control( Group_Control_Typography::get_type(), [
639 'name' => 'title_typography',
640 'global' => [
641 'default' => Global_Typography::TYPOGRAPHY_ACCENT,
642 ],
643 'selector' => "{$nested_tabs_heading_selector_class} > :is( .e-n-tab-title > .e-n-tab-title-text, .e-n-tab-title )",
644 'fields_options' => [
645 'font_size' => [
646 'selectors' => [
647 '{{WRAPPER}}' => '--n-tabs-title-font-size: {{SIZE}}{{UNIT}}',
648 ],
649 ],
650 ],
651 ] );
652
653 $this->start_controls_tabs( 'title_style' );
654
655 $this->start_controls_tab(
656 'title_normal',
657 [
658 'label' => esc_html__( 'Normal', 'elementor' ),
659 ]
660 );
661
662 $this->add_control(
663 'title_text_color',
664 [
665 'label' => esc_html__( 'Color', 'elementor' ),
666 'type' => Controls_Manager::COLOR,
667 'selectors' => [
668 '{{WRAPPER}}' => '--n-tabs-title-color: {{VALUE}}',
669 ],
670 ]
671 );
672
673 $this->add_group_control(
674 Group_Control_Text_Shadow::get_type(),
675 [
676 'name' => 'title_text_shadow',
677 'selector' => "{$nested_tabs_heading_selector_class} > .e-n-tab-title:not( .e-active ):not( :hover )",
678 'fields_options' => [
679 'text_shadow_type' => [
680 'label' => esc_html_x( 'Shadow', 'Text Shadow Control', 'elementor' ),
681 ],
682 ],
683 ]
684 );
685
686 $this->add_group_control(
687 Group_Control_Text_Stroke::get_type(),
688 [
689 'name' => 'title_text_stroke',
690 'selector' => "{$nested_tabs_heading_selector_class} > .e-n-tab-title:not( .e-active ):not( :hover ) :is( span, a, i )",
691 'fields_options' => [
692 'text_stroke_type' => [
693 'label' => esc_html__( 'Stroke', 'elementor' ),
694 ],
695 ],
696 ]
697 );
698
699 $this->end_controls_tab();
700
701 $this->start_controls_tab(
702 'title_hover',
703 [
704 'label' => esc_html__( 'Hover', 'elementor' ),
705 ]
706 );
707
708 $this->add_control(
709 'title_text_color_hover',
710 [
711 'label' => esc_html__( 'Color', 'elementor' ),
712 'type' => Controls_Manager::COLOR,
713 'selectors' => [
714 '{{WRAPPER}} .e-normal:not( .e-active ):hover' => '--n-tabs-title-color-hover: {{VALUE}}',
715 ],
716 ]
717 );
718
719 $this->add_group_control(
720 Group_Control_Text_Shadow::get_type(),
721 [
722 'name' => 'title_text_shadow_hover',
723 'selector' => "{$nested_tabs_heading_selector_class} > .e-normal:not( .e-active ):hover",
724 'fields_options' => [
725 'text_shadow_type' => [
726 'label' => esc_html_x( 'Shadow', 'Text Shadow Control', 'elementor' ),
727 ],
728 ],
729
730 ]
731 );
732
733 $this->add_group_control(
734 Group_Control_Text_Stroke::get_type(),
735 [
736 'name' => 'title_text_stroke_hover',
737 'selector' => "{$nested_tabs_heading_selector_class} > .e-normal:not( .e-active ):hover :is( span, a, i )",
738 'fields_options' => [
739 'text_stroke_type' => [
740 'label' => esc_html__( 'Stroke', 'elementor' ),
741 ],
742 ],
743 ]
744 );
745
746 $this->end_controls_tab();
747
748 $this->start_controls_tab(
749 'title_active',
750 [
751 'label' => esc_html__( 'Active', 'elementor' ),
752 ]
753 );
754
755 $this->add_control(
756 'title_text_color_active',
757 [
758 'label' => esc_html__( 'Color', 'elementor' ),
759 'type' => Controls_Manager::COLOR,
760 'selectors' => [
761 '{{WRAPPER}}' => '--n-tabs-title-color-active: {{VALUE}}',
762 ],
763 ]
764 );
765
766 $this->add_group_control(
767 Group_Control_Text_Shadow::get_type(),
768 [
769 'name' => 'title_text_shadow_active',
770 'selector' => "{$nested_tabs_heading_selector_class} > .e-n-tab-title.e-active",
771 'fields_options' => [
772 'text_shadow_type' => [
773 'label' => esc_html_x( 'Shadow', 'Text Shadow Control', 'elementor' ),
774 ],
775 ],
776 ]
777 );
778
779 $this->add_group_control(
780 Group_Control_Text_Stroke::get_type(),
781 [
782 'name' => 'title_text_stroke_active',
783 'selector' => "{$nested_tabs_heading_selector_class} > .e-n-tab-title.e-active :is( span, a, i )",
784 'fields_options' => [
785 'text_stroke_type' => [
786 'label' => esc_html__( 'Stroke', 'elementor' ),
787 ],
788 ],
789 ]
790 );
791
792 $this->end_controls_tab();
793
794 $this->end_controls_tabs();
795
796 $this->end_controls_section();
797
798 $this->start_controls_section( 'icon_section_style', [
799 'label' => esc_html__( 'Icon', 'elementor' ),
800 'tab' => Controls_Manager::TAB_STYLE,
801 ] );
802
803 $this->add_responsive_control( 'icon_position', [
804 'label' => esc_html__( 'Position', 'elementor' ),
805 'type' => Controls_Manager::CHOOSE,
806 'options' => [
807 'top' => [
808 'title' => esc_html__( 'Top', 'elementor' ),
809 'icon' => 'eicon-v-align-top',
810 ],
811 'end' => [
812 'title' => $tooltip_end,
813 'icon' => 'eicon-h-align-' . $end,
814 ],
815 'bottom' => [
816 'title' => esc_html__( 'Bottom', 'elementor' ),
817 'icon' => 'eicon-v-align-bottom',
818 ],
819 'start' => [
820 'title' => $tooltip_start,
821 'icon' => 'eicon-h-align-' . $start,
822 ],
823 ],
824 'selectors_dictionary' => [
825 // The toggle variables for 'align items' and 'justify content' have been added to separate the styling of the two 'flex direction' modes.
826 'top' => '--n-tabs-title-direction: column; --n-tabs-icon-order: initial; --n-tabs-title-justify-content-toggle: center; --n-tabs-title-align-items-toggle: initial;',
827 'end' => '--n-tabs-title-direction: row; --n-tabs-icon-order: 1; --n-tabs-title-justify-content-toggle: initial; --n-tabs-title-align-items-toggle: center;',
828 'bottom' => '--n-tabs-title-direction: column; --n-tabs-icon-order: 1; --n-tabs-title-justify-content-toggle: center; --n-tabs-title-align-items-toggle: initial;',
829 'start' => '--n-tabs-title-direction: row; --n-tabs-icon-order: initial; --n-tabs-title-justify-content-toggle: initial; --n-tabs-title-align-items-toggle: center;',
830 ],
831 'selectors' => [
832 '{{WRAPPER}}' => '{{VALUE}}',
833 ],
834 ] );
835
836 $this->add_responsive_control( 'icon_size', [
837 'label' => esc_html__( 'Size', 'elementor' ),
838 'type' => Controls_Manager::SLIDER,
839 'range' => [
840 'px' => [
841 'min' => 0,
842 'max' => 100,
843 ],
844 'em' => [
845 'min' => 0,
846 'max' => 10,
847 'step' => 0.1,
848 ],
849 'rem' => [
850 'min' => 0,
851 'max' => 10,
852 'step' => 0.1,
853 ],
854 ],
855 'size_units' => [ 'px', 'em', 'rem', 'vw', 'custom' ],
856 'selectors' => [
857 '{{WRAPPER}}' => '--n-tabs-icon-size: {{SIZE}}{{UNIT}}',
858 ],
859 ] );
860
861 $this->add_responsive_control( 'icon_spacing', [
862 'label' => esc_html__( 'Spacing', 'elementor' ),
863 'type' => Controls_Manager::SLIDER,
864 'range' => [
865 'px' => [
866 'min' => 0,
867 'max' => 400,
868 ],
869 'vw' => [
870 'min' => 0,
871 'max' => 50,
872 'step' => 0.1,
873 ],
874 ],
875 'size_units' => [ 'px', 'em', 'rem', 'vw', 'custom' ],
876 'selectors' => [
877 '{{WRAPPER}}' => '--n-tabs-icon-gap: {{SIZE}}{{UNIT}}',
878 ],
879 ] );
880
881 $this->start_controls_tabs( 'icon_style_states' );
882
883 $this->start_controls_tab(
884 'icon_section_normal',
885 [
886 'label' => esc_html__( 'Normal', 'elementor' ),
887 ]
888 );
889
890 $this->add_control( 'icon_color', [
891 'label' => esc_html__( 'Color', 'elementor' ),
892 'type' => Controls_Manager::COLOR,
893 'selectors' => [
894 '{{WRAPPER}}' => '--n-tabs-icon-color: {{VALUE}};',
895 ],
896 ] );
897
898 $this->end_controls_tab();
899
900 $this->start_controls_tab(
901 'icon_section_hover',
902 [
903 'label' => esc_html__( 'Hover', 'elementor' ),
904 ]
905 );
906
907 $this->add_control( 'icon_color_hover', [
908 'label' => esc_html__( 'Color', 'elementor' ),
909 'type' => Controls_Manager::COLOR,
910 'selectors' => [
911 '{{WRAPPER}} .e-normal:not( .e-active ):hover' => '--n-tabs-icon-color-hover: {{VALUE}};',
912 ],
913 ] );
914
915 $this->end_controls_tab();
916
917 $this->start_controls_tab(
918 'icon_section_active',
919 [
920 'label' => esc_html__( 'Active', 'elementor' ),
921 ]
922 );
923
924 $this->add_control( 'icon_color_active', [
925 'label' => esc_html__( 'Color', 'elementor' ),
926 'type' => Controls_Manager::COLOR,
927 'selectors' => [
928 '{{WRAPPER}}' => '--n-tabs-icon-color-active: {{VALUE}};',
929 ],
930 ] );
931
932 $this->end_controls_tab();
933
934 $this->end_controls_tabs();
935
936 $this->end_controls_section();
937
938 $this->start_controls_section( 'section_box_style', [
939 'label' => esc_html__( 'Content', 'elementor' ),
940 'tab' => Controls_Manager::TAB_STYLE,
941 ] );
942
943 $this->add_group_control(
944 Group_Control_Background::get_type(),
945 [
946 'name' => 'box_background_color',
947 'types' => [ 'classic', 'gradient' ],
948 'exclude' => [ 'image' ],
949 'selector' => $nested_tabs_content_selector_class,
950 'fields_options' => [
951 'color' => [
952 'label' => esc_html__( 'Background Color', 'elementor' ),
953 ],
954 ],
955 ]
956 );
957
958 $this->add_group_control(
959 Group_Control_Border::get_type(),
960 [
961 'name' => 'box_border',
962 'selector' => $nested_tabs_content_selector_class,
963 'fields_options' => [
964 'color' => [
965 'label' => esc_html__( 'Border Color', 'elementor' ),
966 ],
967 'width' => [
968 'label' => esc_html__( 'Border Width', 'elementor' ),
969 ],
970 ],
971 ]
972 );
973
974 $this->add_responsive_control(
975 'box_border_radius',
976 [
977 'label' => esc_html__( 'Border Radius', 'elementor' ),
978 'type' => Controls_Manager::DIMENSIONS,
979 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ],
980 'selectors' => [
981 $nested_tabs_content_selector_class => '--border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
982 ],
983 ]
984 );
985
986 $this->add_group_control(
987 Group_Control_Box_Shadow::get_type(),
988 [
989 'name' => 'box_shadow_box_shadow',
990 'selector' => $nested_tabs_content_selector_class,
991 'condition' => [
992 'box_height!' => 'height',
993 ],
994 ]
995 );
996
997 $this->add_responsive_control(
998 'box_padding',
999 [
1000 'label' => esc_html__( 'Padding', 'elementor' ),
1001 'type' => Controls_Manager::DIMENSIONS,
1002 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
1003 'selectors' => [
1004 $nested_tabs_content_selector_class => '--padding-top: {{TOP}}{{UNIT}}; --padding-right: {{RIGHT}}{{UNIT}}; --padding-bottom: {{BOTTOM}}{{UNIT}}; --padding-left: {{LEFT}}{{UNIT}};',
1005 ],
1006 ]
1007 );
1008
1009 $this->end_controls_section();
1010 }
1011
1012 protected function render() {
1013 // Copied from tabs.php
1014 $settings = $this->get_settings_for_display();
1015 $tabs = $settings['tabs'];
1016
1017 $id_int = substr( $this->get_id_int(), 0, 3 );
1018
1019 $a11y_improvements_experiment = Plugin::$instance->experiments->is_feature_active( 'a11y_improvements' );
1020
1021 if ( ! empty( $settings['link'] ) ) {
1022 $this->add_link_attributes( 'elementor-tabs', $settings['link'] );
1023 }
1024
1025 $this->add_render_attribute( 'elementor-tabs', 'class', 'e-n-tabs' );
1026 $this->add_render_attribute( 'tab-title-text', 'class', 'e-n-tab-title-text' );
1027 $this->add_render_attribute( 'tab-icon', 'class', 'e-n-tab-icon' );
1028 $this->add_render_attribute( 'tab-icon-active', 'class', [ 'e-n-tab-icon', 'e-active' ] );
1029
1030 $tabs_title_html = '';
1031 $mobile_tabs_title_html = '';
1032
1033 foreach ( $tabs as $index => $item ) {
1034 // Tabs title.
1035 $tab_count = $index + 1;
1036 $tab_title_setting_key = $this->get_repeater_setting_key( 'tab_title', 'tabs', $index );
1037 $tab_title = $a11y_improvements_experiment ? $item['tab_title'] : '<a href="">' . $item['tab_title'] . '</a>';
1038 $tab_title_mobile_setting_key = $this->get_repeater_setting_key( 'tab_title_mobile', 'tabs', $tab_count );
1039 $tab_title_classes = [ 'e-n-tab-title', 'e-normal' ];
1040 $tab_title_mobile_classes = [ 'e-n-tab-title', 'e-collapse' ];
1041
1042 if ( $settings['hover_animation'] ) {
1043 array_push( $tab_title_classes, 'elementor-animation-' . $settings['hover_animation'] );
1044 array_push( $tab_title_mobile_classes, 'elementor-animation-' . $settings['hover_animation'] );
1045 }
1046
1047 $tab_id = empty( $item['element_id'] ) ? 'e-n-tabs-title-' . $id_int . $tab_count : $item['element_id'];
1048
1049 $this->add_render_attribute( $tab_title_setting_key, [
1050 'id' => $tab_id,
1051 'class' => $tab_title_classes,
1052 'aria-selected' => 1 === $tab_count ? 'true' : 'false',
1053 'data-tab' => $tab_count,
1054 'role' => 'tab',
1055 'tabindex' => 1 === $tab_count ? '0' : '-1',
1056 'aria-controls' => 'e-n-tab-content-' . $id_int . $tab_count,
1057 'aria-expanded' => 'false',
1058 ] );
1059
1060 $this->add_render_attribute( $tab_title_mobile_setting_key, [
1061 'class' => $tab_title_mobile_classes,
1062 'aria-selected' => 1 === $tab_count ? 'true' : 'false',
1063 'data-tab' => $tab_count,
1064 'role' => 'tab',
1065 'tabindex' => 1 === $tab_count ? '0' : '-1',
1066 'aria-controls' => 'e-n-tab-content-' . $id_int . $tab_count,
1067 'aria-expanded' => 'false',
1068 'id' => $tab_id . '-accordion',
1069 ] );
1070
1071 $title_render_attributes = $this->get_render_attribute_string( $tab_title_setting_key );
1072 $mobile_title_attributes = $this->get_render_attribute_string( $tab_title_mobile_setting_key );
1073 $tab_title_text_class = $this->get_render_attribute_string( 'tab-title-text' );
1074 $tab_icon_class = $this->get_render_attribute_string( 'tab-icon' );
1075
1076 $icon_html = Icons_Manager::try_get_icon_html( $item['tab_icon'], [ 'aria-hidden' => 'true' ] );
1077 $icon_active_html = $icon_html;
1078
1079 if ( $this->is_active_icon_exist( $item ) ) {
1080 $icon_active_html = Icons_Manager::try_get_icon_html( $item['tab_icon_active'], [ 'aria-hidden' => 'true' ] );
1081 }
1082
1083 $tabs_title_html .= "<div {$title_render_attributes}>";
1084 $tabs_title_html .= "\t<span {$tab_icon_class}>{$icon_html}{$icon_active_html}</span>";
1085 $tabs_title_html .= "\t<span {$tab_title_text_class}>{$tab_title}</span>";
1086 $tabs_title_html .= '</div>';
1087
1088 // Tabs content.
1089 ob_start();
1090 $this->print_child( $index );
1091 $tab_content = ob_get_clean();
1092
1093 $mobile_tabs_title_html .= "<div $mobile_title_attributes>";
1094 $mobile_tabs_title_html .= "\t<span {$tab_icon_class}>{$icon_html}{$icon_active_html}</span>";
1095 $mobile_tabs_title_html .= "\t<span {$tab_title_text_class}>{$tab_title}</span>";
1096 $mobile_tabs_title_html .= "</div>$tab_content";
1097 }
1098 ?>
1099 <div <?php $this->print_render_attribute_string( 'elementor-tabs' ); ?>>
1100 <div class="e-n-tabs-heading" role="tablist">
1101 <?php echo $tabs_title_html;// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
1102 </div>
1103 <div class="e-n-tabs-content" role="tablist" aria-orientation="vertical">
1104 <?php echo $mobile_tabs_title_html;// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
1105 </div>
1106 </div>
1107 <?php
1108 }
1109
1110 protected function content_template() {
1111 ?>
1112 <div class="e-n-tabs" role="tablist" aria-orientation="vertical">
1113 <# if ( settings['tabs'] ) {
1114 const elementUid = view.getIDInt().toString().substr( 0, 3 ); #>
1115 <div class="e-n-tabs-heading" role="tablist">
1116 <# _.each( settings['tabs'], function( item, index ) {
1117 const tabCount = index + 1,
1118 tabUid = elementUid + tabCount,
1119 tabWrapperKey = tabUid,
1120 tabTitleKey = 'tab-title-' + tabUid,
1121 tabIconKey = 'tab-icon-' + tabUid,
1122 tabIcon = elementor.helpers.renderIcon( view, item.tab_icon, { 'aria-hidden': true }, 'i' , 'object' ),
1123 hoverAnimationClass = settings['hover_animation'] ? `elementor-animation-${ settings['hover_animation'] }` : '';
1124
1125 let tabActiveIcon = tabIcon,
1126 tabId = 'e-n-tab-title-' + tabUid;
1127
1128 if ( '' !== item.tab_icon_active.value ) {
1129 tabActiveIcon = elementor.helpers.renderIcon( view, item.tab_icon_active, { 'aria-hidden': true }, 'i' , 'object' );
1130 }
1131
1132 if ( '' !== item.element_id ) {
1133 tabId = item.element_id;
1134 }
1135
1136 view.addRenderAttribute( tabWrapperKey, {
1137 'id': tabId,
1138 'class': [ 'e-n-tab-title','e-normal',hoverAnimationClass ],
1139 'data-tab': tabCount,
1140 'role': 'tab',
1141 'tabindex': 1 === tabCount ? '0' : '-1',
1142 'aria-controls': 'e-n-tab-content-' + tabUid,
1143 'aria-expanded': 'false',
1144 } );
1145
1146 view.addRenderAttribute( tabTitleKey, {
1147 'class': [ 'e-n-tab-title-text' ],
1148 'data-binding-type': 'repeater-item',
1149 'data-binding-repeater-name': 'tabs',
1150 'data-binding-setting': [ 'tab_title' ],
1151 'data-binding-index': tabCount,
1152 } );
1153
1154 view.addRenderAttribute( tabIconKey, {
1155 'class': [ 'e-n-tab-icon' ],
1156 'data-binding-type': 'repeater-item',
1157 'data-binding-repeater-name': 'tabs',
1158 'data-binding-setting': [ 'tab_icon.value', 'tab_icon_active.value' ],
1159 'data-binding-index': tabCount,
1160 } );
1161 #>
1162 <div {{{ view.getRenderAttributeString( tabWrapperKey ) }}}>
1163 <span {{{ view.getRenderAttributeString( tabIconKey ) }}}>{{{ tabIcon.value }}}{{{ tabActiveIcon.value }}}</span>
1164 <span {{{ view.getRenderAttributeString( tabTitleKey ) }}}>{{{ item.tab_title }}}</span>
1165 </div>
1166 <# } ); #>
1167 </div>
1168 <div class="e-n-tabs-content">
1169 </div>
1170 <# } #>
1171 </div>
1172 <?php
1173 }
1174
1175 /**
1176 * @param $item
1177 * @return bool
1178 */
1179 private function is_active_icon_exist( $item ) {
1180 return array_key_exists( 'tab_icon_active', $item ) && ! empty( $item['tab_icon_active'] ) && ! empty( $item['tab_icon_active']['value'] );
1181 }
1182 }
1183