PluginProbe
Elementor Website Builder – more than just a page builder / 3.14.0-beta4
Elementor Website Builder – more than just a page builder v3.14.0-beta4
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.14.0-beta4, at modules/nested-tabs/widgets/nested-tabs.php

1,206 lines 35.3 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 'frontend_available' => true,
236 ] );
237
238 $this->add_responsive_control( 'tabs_justify_vertical', [
239 'label' => esc_html__( 'Justify', 'elementor' ),
240 'type' => Controls_Manager::CHOOSE,
241 'options' => [
242 'start' => [
243 'title' => esc_html__( 'Start', 'elementor' ),
244 'icon' => 'eicon-flex eicon-align-start-v',
245 ],
246 'center' => [
247 'title' => esc_html__( 'Center', 'elementor' ),
248 'icon' => 'eicon-v-align-middle',
249 ],
250 'end' => [
251 'title' => esc_html__( 'End', 'elementor' ),
252 'icon' => 'eicon-flex eicon-align-end-v',
253 ],
254 'stretch' => [
255 'title' => esc_html__( 'Justified', 'elementor' ),
256 'icon' => 'eicon-v-align-stretch',
257 ],
258 ],
259 'selectors_dictionary' => [
260 'start' => '--n-tabs-heading-justify-content: flex-start; --n-tabs-title-width: initial; --n-tabs-title-height: initial; --n-tabs-title-align-items: initial;',
261 'center' => '--n-tabs-heading-justify-content: center; --n-tabs-title-width: initial; --n-tabs-title-height: initial; --n-tabs-title-align-items: initial;',
262 'end' => '--n-tabs-heading-justify-content: flex-end; --n-tabs-title-width: initial; --n-tabs-title-height: initial; --n-tabs-title-align-items: initial;',
263 'stretch' => '--n-tabs-heading-justify-content: flex-start; --n-tabs-title-width: initial; --n-tabs-title-height: 100%; --n-tabs-title-align-items: center;',
264 ],
265 'selectors' => [
266 '{{WRAPPER}}' => '{{VALUE}}',
267 ],
268 'condition' => [
269 'tabs_direction' => [
270 'start',
271 'end',
272 ],
273 ],
274 ] );
275
276 $this->add_responsive_control( 'tabs_width', [
277 'label' => esc_html__( 'Width', 'elementor' ),
278 'type' => Controls_Manager::SLIDER,
279 'range' => [
280 '%' => [
281 'min' => 10,
282 'max' => 50,
283 ],
284 'px' => [
285 'min' => 20,
286 'max' => 600,
287 ],
288 ],
289 'default' => [
290 'unit' => '%',
291 ],
292 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
293 'selectors' => [
294 '{{WRAPPER}}' => '--n-tabs-heading-width: {{SIZE}}{{UNIT}}',
295 ],
296 'condition' => [
297 'tabs_direction' => [
298 'start',
299 'end',
300 ],
301 ],
302 ] );
303
304 $this->add_responsive_control( 'title_alignment', [
305 'label' => esc_html__( 'Align Title', 'elementor' ),
306 'type' => Controls_Manager::CHOOSE,
307 'options' => [
308 'start' => [
309 'title' => esc_html__( 'Left', 'elementor' ),
310 'icon' => 'eicon-text-align-left',
311 ],
312 'center' => [
313 'title' => esc_html__( 'Center', 'elementor' ),
314 'icon' => 'eicon-text-align-center',
315 ],
316 'end' => [
317 'title' => esc_html__( 'Right', 'elementor' ),
318 'icon' => 'eicon-text-align-right',
319 ],
320 ],
321 'selectors_dictionary' => [
322 'start' => '--n-tabs-title-justify-content: flex-start; --n-tabs-title-align-items: flex-start;',
323 'center' => '--n-tabs-title-justify-content: center; --n-tabs-title-align-items: center;',
324 'end' => '--n-tabs-title-justify-content: flex-end; --n-tabs-title-align-items: flex-end;',
325 ],
326 'selectors' => [
327 '{{WRAPPER}}' => '{{VALUE}}',
328 ],
329 ] );
330
331 $this->end_controls_section();
332
333 $this->start_controls_section( 'section_tabs_responsive', [
334 'label' => esc_html__( 'Additional Settings', 'elementor' ),
335 ] );
336
337 $this->add_responsive_control(
338 'horizontal_scroll',
339 [
340 'label' => esc_html__( 'Horizontal Scroll', 'elementor' ),
341 'type' => Controls_Manager::SELECT,
342 'description' => esc_html__( 'Note: Scroll tabs if they don’t fit into their parent container.', 'elementor' ),
343 'options' => [
344 'disable' => esc_html__( 'Disable', 'elementor' ),
345 'enable' => esc_html__( 'Enable', 'elementor' ),
346 ],
347 'default' => 'disable',
348 'selectors_dictionary' => [
349 'disable' => '--n-tabs-heading-wrap: wrap; --n-tabs-heading-overflow-x: initial;',
350 'enable' => '--n-tabs-heading-wrap: nowrap; --n-tabs-heading-overflow-x: scroll;',
351 ],
352 'selectors' => [
353 '{{WRAPPER}}' => '{{VALUE}}',
354 ],
355 'frontend_available' => true,
356 ]
357 );
358
359 $dropdown_options = [
360 'none' => esc_html__( 'None', 'elementor' ),
361 ];
362 $excluded_breakpoints = [
363 'laptop',
364 'tablet_extra',
365 'widescreen',
366 ];
367
368 foreach ( Plugin::$instance->breakpoints->get_active_breakpoints() as $breakpoint_key => $breakpoint_instance ) {
369 // Exclude the larger breakpoints from the dropdown selector.
370 if ( in_array( $breakpoint_key, $excluded_breakpoints, true ) ) {
371 continue;
372 }
373
374 $dropdown_options[ $breakpoint_key ] = sprintf(
375 /* translators: 1: Breakpoint label, 2: `>` character, 3: Breakpoint value. */
376 esc_html__( '%1$s (%2$s %3$dpx)', 'elementor' ),
377 $breakpoint_instance->get_label(),
378 '>',
379 $breakpoint_instance->get_value()
380 );
381 }
382
383 $this->add_control(
384 'breakpoint_selector',
385 [
386 'label' => esc_html__( 'Breakpoint', 'elementor' ),
387 'type' => Controls_Manager::SELECT,
388 'description' => esc_html__( 'Note: Choose at which breakpoint tabs will automatically switch to a vertical (“accordion”) layout.', 'elementor' ),
389 'options' => $dropdown_options,
390 'default' => 'mobile',
391 'prefix_class' => 'e-n-tabs-',
392 ]
393 );
394
395 $this->end_controls_section();
396
397 $this->start_controls_section( 'section_tabs_style', [
398 'label' => esc_html__( 'Tabs', 'elementor' ),
399 'tab' => Controls_Manager::TAB_STYLE,
400 ] );
401
402 $this->add_responsive_control( 'tabs_title_space_between', [
403 'label' => esc_html__( 'Gap between tabs', 'elementor' ),
404 'type' => Controls_Manager::SLIDER,
405 'range' => [
406 'px' => [
407 'min' => 0,
408 'max' => 400,
409 ],
410 ],
411 'size_units' => [ 'px' ],
412 'selectors' => [
413 '{{WRAPPER}}' => '--n-tabs-title-gap: {{SIZE}}{{UNIT}}',
414 ],
415 ] );
416
417 $this->add_responsive_control( 'tabs_title_spacing', [
418 'label' => esc_html__( 'Distance from content', 'elementor' ),
419 'type' => Controls_Manager::SLIDER,
420 'range' => [
421 'px' => [
422 'min' => 0,
423 'max' => 400,
424 ],
425 ],
426 'size_units' => [ 'px' ],
427 'selectors' => [
428 '{{WRAPPER}}' => '--n-tabs-gap: {{SIZE}}{{UNIT}}',
429 ],
430 ] );
431
432 $this->start_controls_tabs( 'tabs_title_style' );
433
434 $this->start_controls_tab(
435 'tabs_title_normal',
436 [
437 'label' => esc_html__( 'Normal', 'elementor' ),
438 ]
439 );
440
441 $this->add_group_control(
442 Group_Control_Background::get_type(),
443 [
444 'name' => 'tabs_title_background_color',
445 'types' => [ 'classic', 'gradient' ],
446 'exclude' => [ 'image' ],
447 '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 ) )',
448 'fields_options' => [
449 'color' => [
450 'label' => esc_html__( 'Background Color', 'elementor' ),
451 'selectors' => [
452 '{{SELECTOR}}' => 'background: {{VALUE}}',
453 ],
454 ],
455 ],
456 ]
457 );
458
459 $this->add_group_control(
460 Group_Control_Border::get_type(),
461 [
462 'name' => 'tabs_title_border',
463 'selector' => "{$nested_tabs_heading_selector_class} > .e-n-tab-title:not( .e-active ):not( :hover )",
464 'fields_options' => [
465 'color' => [
466 'label' => esc_html__( 'Border Color', 'elementor' ),
467 ],
468 'width' => [
469 'label' => esc_html__( 'Border Width', 'elementor' ),
470 ],
471 ],
472 ]
473 );
474
475 $this->add_group_control(
476 Group_Control_Box_Shadow::get_type(),
477 [
478 'name' => 'tabs_title_box_shadow',
479 'label' => esc_html__( 'Shadow', 'elementor' ),
480 'separator' => 'after',
481 'selector' => "{$nested_tabs_heading_selector_class} > .e-n-tab-title:not( .e-active ):not( :hover )",
482 ]
483 );
484
485 $this->end_controls_tab();
486
487 $this->start_controls_tab(
488 'tabs_title_hover',
489 [
490 'label' => esc_html__( 'Hover', 'elementor' ),
491 ]
492 );
493
494 $this->add_group_control(
495 Group_Control_Background::get_type(),
496 [
497 'name' => 'tabs_title_background_color_hover',
498 'types' => [ 'classic', 'gradient' ],
499 'exclude' => [ 'image' ],
500 'selector' => "{$nested_tabs_heading_selector_class} > .e-normal:not( .e-active ):hover",
501 'fields_options' => [
502 'background' => [
503 'default' => 'classic',
504 ],
505 'color' => [
506 'global' => [
507 'default' => Global_Colors::COLOR_ACCENT,
508 ],
509 'label' => esc_html__( 'Background Color', 'elementor' ),
510 'selectors' => [
511 '{{SELECTOR}}' => 'background: {{VALUE}};',
512 ],
513 ],
514 ],
515 ]
516 );
517
518 $this->add_group_control(
519 Group_Control_Border::get_type(),
520 [
521 'name' => 'tabs_title_border_hover',
522 'selector' => "{$nested_tabs_heading_selector_class} > .e-normal:not( .e-active ):hover",
523 'fields_options' => [
524 'color' => [
525 'label' => esc_html__( 'Border Color', 'elementor' ),
526 ],
527 'width' => [
528 'label' => esc_html__( 'Border Width', 'elementor' ),
529 ],
530 ],
531 ]
532 );
533
534 $this->add_group_control(
535 Group_Control_Box_Shadow::get_type(),
536 [
537 'name' => 'tabs_title_box_shadow_hover',
538 'label' => esc_html__( 'Shadow', 'elementor' ),
539 'separator' => 'after',
540 'selector' => "{$nested_tabs_heading_selector_class} > .e-normal:not( .e-active ):hover",
541 ]
542 );
543
544 $this->add_control(
545 'hover_animation',
546 [
547 'label' => esc_html__( 'Hover Animation', 'elementor' ),
548 'type' => Controls_Manager::HOVER_ANIMATION,
549 ]
550 );
551
552 $this->add_control(
553 'tabs_title_transition_duration',
554 [
555 'label' => esc_html__( 'Transition Duration', 'elementor' ) . ' (s)',
556 'type' => Controls_Manager::SLIDER,
557 'selectors' => [
558 '{{WRAPPER}}' => '--n-tabs-title-transition: {{SIZE}}s',
559 ],
560 'range' => [
561 'px' => [
562 'max' => 3,
563 'step' => 0.1,
564 ],
565 ],
566 ]
567 );
568
569 $this->end_controls_tab();
570
571 $this->start_controls_tab(
572 'tabs_title_active',
573 [
574 'label' => esc_html__( 'Active', 'elementor' ),
575 ]
576 );
577
578 $this->add_group_control(
579 Group_Control_Background::get_type(),
580 [
581 'name' => 'tabs_title_background_color_active',
582 'types' => [ 'classic', 'gradient' ],
583 'exclude' => [ 'image' ],
584 'selector' => "{$nested_tabs_heading_selector_class} > .e-n-tab-title.e-active",
585 'fields_options' => [
586 'background' => [
587 'default' => 'classic',
588 ],
589 'color' => [
590 'global' => [
591 'default' => Global_Colors::COLOR_ACCENT,
592 ],
593 'label' => esc_html__( 'Background Color', 'elementor' ),
594 'selectors' => [
595 '{{SELECTOR}}' => 'background: {{VALUE}};',
596 ],
597 ],
598 ],
599 ]
600 );
601
602 $this->add_group_control(
603 Group_Control_Border::get_type(),
604 [
605 'name' => 'tabs_title_border_active',
606 'selector' => "{$nested_tabs_heading_selector_class} > .e-n-tab-title.e-active",
607 'fields_options' => [
608 'color' => [
609 'label' => esc_html__( 'Border Color', 'elementor' ),
610 ],
611 'width' => [
612 'label' => esc_html__( 'Border Width', 'elementor' ),
613 ],
614 ],
615 ]
616 );
617
618 $this->add_group_control(
619 Group_Control_Box_Shadow::get_type(),
620 [
621 'name' => 'tabs_title_box_shadow_active',
622 'label' => esc_html__( 'Shadow', 'elementor' ),
623 'selector' => "{$nested_tabs_heading_selector_class} > .e-n-tab-title.e-active",
624 ]
625 );
626
627 $this->end_controls_tab();
628
629 $this->end_controls_tabs();
630
631 $this->add_responsive_control(
632 'tabs_title_border_radius',
633 [
634 'label' => esc_html__( 'Border Radius', 'elementor' ),
635 'type' => Controls_Manager::DIMENSIONS,
636 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ],
637 'separator' => 'before',
638 'selectors' => [
639 '{{WRAPPER}}' => '--n-tabs-title-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
640 ],
641 ]
642 );
643
644 $this->add_responsive_control(
645 'padding',
646 [
647 'label' => esc_html__( 'Padding', 'elementor' ),
648 'type' => Controls_Manager::DIMENSIONS,
649 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
650 'selectors' => [
651 '{{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}};',
652 ],
653 ]
654 );
655
656 $this->end_controls_section();
657
658 $this->start_controls_section( 'section_title_style', [
659 'label' => esc_html__( 'Titles', 'elementor' ),
660 'tab' => Controls_Manager::TAB_STYLE,
661 ] );
662
663 $this->add_group_control( Group_Control_Typography::get_type(), [
664 'name' => 'title_typography',
665 'global' => [
666 'default' => Global_Typography::TYPOGRAPHY_ACCENT,
667 ],
668 'selector' => "{$nested_tabs_heading_selector_class} > :is( .e-n-tab-title > .e-n-tab-title-text, .e-n-tab-title )",
669 'fields_options' => [
670 'font_size' => [
671 'selectors' => [
672 '{{WRAPPER}}' => '--n-tabs-title-font-size: {{SIZE}}{{UNIT}}',
673 ],
674 ],
675 ],
676 ] );
677
678 $this->start_controls_tabs( 'title_style' );
679
680 $this->start_controls_tab(
681 'title_normal',
682 [
683 'label' => esc_html__( 'Normal', 'elementor' ),
684 ]
685 );
686
687 $this->add_control(
688 'title_text_color',
689 [
690 'label' => esc_html__( 'Color', 'elementor' ),
691 'type' => Controls_Manager::COLOR,
692 'selectors' => [
693 '{{WRAPPER}}' => '--n-tabs-title-color: {{VALUE}}',
694 ],
695 ]
696 );
697
698 $this->add_group_control(
699 Group_Control_Text_Shadow::get_type(),
700 [
701 'name' => 'title_text_shadow',
702 'selector' => "{$nested_tabs_heading_selector_class} > .e-n-tab-title:not( .e-active ):not( :hover )",
703 'fields_options' => [
704 'text_shadow_type' => [
705 'label' => esc_html_x( 'Shadow', 'Text Shadow Control', 'elementor' ),
706 ],
707 ],
708 ]
709 );
710
711 $this->add_group_control(
712 Group_Control_Text_Stroke::get_type(),
713 [
714 'name' => 'title_text_stroke',
715 'selector' => "{$nested_tabs_heading_selector_class} > .e-n-tab-title:not( .e-active ):not( :hover ) :is( span, a, i )",
716 'fields_options' => [
717 'text_stroke_type' => [
718 'label' => esc_html__( 'Stroke', 'elementor' ),
719 ],
720 ],
721 ]
722 );
723
724 $this->end_controls_tab();
725
726 $this->start_controls_tab(
727 'title_hover',
728 [
729 'label' => esc_html__( 'Hover', 'elementor' ),
730 ]
731 );
732
733 $this->add_control(
734 'title_text_color_hover',
735 [
736 'label' => esc_html__( 'Color', 'elementor' ),
737 'type' => Controls_Manager::COLOR,
738 'selectors' => [
739 '{{WRAPPER}} .e-normal:not( .e-active ):hover' => '--n-tabs-title-color-hover: {{VALUE}}',
740 ],
741 ]
742 );
743
744 $this->add_group_control(
745 Group_Control_Text_Shadow::get_type(),
746 [
747 'name' => 'title_text_shadow_hover',
748 'selector' => "{$nested_tabs_heading_selector_class} > .e-normal:not( .e-active ):hover",
749 'fields_options' => [
750 'text_shadow_type' => [
751 'label' => esc_html_x( 'Shadow', 'Text Shadow Control', 'elementor' ),
752 ],
753 ],
754
755 ]
756 );
757
758 $this->add_group_control(
759 Group_Control_Text_Stroke::get_type(),
760 [
761 'name' => 'title_text_stroke_hover',
762 'selector' => "{$nested_tabs_heading_selector_class} > .e-normal:not( .e-active ):hover :is( span, a, i )",
763 'fields_options' => [
764 'text_stroke_type' => [
765 'label' => esc_html__( 'Stroke', 'elementor' ),
766 ],
767 ],
768 ]
769 );
770
771 $this->end_controls_tab();
772
773 $this->start_controls_tab(
774 'title_active',
775 [
776 'label' => esc_html__( 'Active', 'elementor' ),
777 ]
778 );
779
780 $this->add_control(
781 'title_text_color_active',
782 [
783 'label' => esc_html__( 'Color', 'elementor' ),
784 'type' => Controls_Manager::COLOR,
785 'selectors' => [
786 '{{WRAPPER}}' => '--n-tabs-title-color-active: {{VALUE}}',
787 ],
788 ]
789 );
790
791 $this->add_group_control(
792 Group_Control_Text_Shadow::get_type(),
793 [
794 'name' => 'title_text_shadow_active',
795 'selector' => "{$nested_tabs_heading_selector_class} > .e-n-tab-title.e-active",
796 'fields_options' => [
797 'text_shadow_type' => [
798 'label' => esc_html_x( 'Shadow', 'Text Shadow Control', 'elementor' ),
799 ],
800 ],
801 ]
802 );
803
804 $this->add_group_control(
805 Group_Control_Text_Stroke::get_type(),
806 [
807 'name' => 'title_text_stroke_active',
808 'selector' => "{$nested_tabs_heading_selector_class} > .e-n-tab-title.e-active :is( span, a, i )",
809 'fields_options' => [
810 'text_stroke_type' => [
811 'label' => esc_html__( 'Stroke', 'elementor' ),
812 ],
813 ],
814 ]
815 );
816
817 $this->end_controls_tab();
818
819 $this->end_controls_tabs();
820
821 $this->end_controls_section();
822
823 $this->start_controls_section( 'icon_section_style', [
824 'label' => esc_html__( 'Icon', 'elementor' ),
825 'tab' => Controls_Manager::TAB_STYLE,
826 ] );
827
828 $this->add_responsive_control( 'icon_position', [
829 'label' => esc_html__( 'Position', 'elementor' ),
830 'type' => Controls_Manager::CHOOSE,
831 'options' => [
832 'top' => [
833 'title' => esc_html__( 'Top', 'elementor' ),
834 'icon' => 'eicon-v-align-top',
835 ],
836 'end' => [
837 'title' => $tooltip_end,
838 'icon' => 'eicon-h-align-' . $end,
839 ],
840 'bottom' => [
841 'title' => esc_html__( 'Bottom', 'elementor' ),
842 'icon' => 'eicon-v-align-bottom',
843 ],
844 'start' => [
845 'title' => $tooltip_start,
846 'icon' => 'eicon-h-align-' . $start,
847 ],
848 ],
849 'selectors_dictionary' => [
850 // The toggle variables for 'align items' and 'justify content' have been added to separate the styling of the two 'flex direction' modes.
851 '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;',
852 '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;',
853 '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;',
854 '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;',
855 ],
856 'selectors' => [
857 '{{WRAPPER}}' => '{{VALUE}}',
858 ],
859 ] );
860
861 $this->add_responsive_control( 'icon_size', [
862 'label' => esc_html__( 'Size', 'elementor' ),
863 'type' => Controls_Manager::SLIDER,
864 'range' => [
865 'px' => [
866 'min' => 0,
867 'max' => 100,
868 ],
869 'em' => [
870 'min' => 0,
871 'max' => 10,
872 'step' => 0.1,
873 ],
874 'rem' => [
875 'min' => 0,
876 'max' => 10,
877 'step' => 0.1,
878 ],
879 ],
880 'size_units' => [ 'px', 'em', 'rem', 'vw', 'custom' ],
881 'selectors' => [
882 '{{WRAPPER}}' => '--n-tabs-icon-size: {{SIZE}}{{UNIT}}',
883 ],
884 ] );
885
886 $this->add_responsive_control( 'icon_spacing', [
887 'label' => esc_html__( 'Spacing', 'elementor' ),
888 'type' => Controls_Manager::SLIDER,
889 'range' => [
890 'px' => [
891 'min' => 0,
892 'max' => 400,
893 ],
894 'vw' => [
895 'min' => 0,
896 'max' => 50,
897 'step' => 0.1,
898 ],
899 ],
900 'size_units' => [ 'px', 'em', 'rem', 'vw', 'custom' ],
901 'selectors' => [
902 '{{WRAPPER}}' => '--n-tabs-icon-gap: {{SIZE}}{{UNIT}}',
903 ],
904 ] );
905
906 $this->start_controls_tabs( 'icon_style_states' );
907
908 $this->start_controls_tab(
909 'icon_section_normal',
910 [
911 'label' => esc_html__( 'Normal', 'elementor' ),
912 ]
913 );
914
915 $this->add_control( 'icon_color', [
916 'label' => esc_html__( 'Color', 'elementor' ),
917 'type' => Controls_Manager::COLOR,
918 'selectors' => [
919 '{{WRAPPER}}' => '--n-tabs-icon-color: {{VALUE}};',
920 ],
921 ] );
922
923 $this->end_controls_tab();
924
925 $this->start_controls_tab(
926 'icon_section_hover',
927 [
928 'label' => esc_html__( 'Hover', 'elementor' ),
929 ]
930 );
931
932 $this->add_control( 'icon_color_hover', [
933 'label' => esc_html__( 'Color', 'elementor' ),
934 'type' => Controls_Manager::COLOR,
935 'selectors' => [
936 '{{WRAPPER}} .e-normal:not( .e-active ):hover' => '--n-tabs-icon-color-hover: {{VALUE}};',
937 ],
938 ] );
939
940 $this->end_controls_tab();
941
942 $this->start_controls_tab(
943 'icon_section_active',
944 [
945 'label' => esc_html__( 'Active', 'elementor' ),
946 ]
947 );
948
949 $this->add_control( 'icon_color_active', [
950 'label' => esc_html__( 'Color', 'elementor' ),
951 'type' => Controls_Manager::COLOR,
952 'selectors' => [
953 '{{WRAPPER}}' => '--n-tabs-icon-color-active: {{VALUE}};',
954 ],
955 ] );
956
957 $this->end_controls_tab();
958
959 $this->end_controls_tabs();
960
961 $this->end_controls_section();
962
963 $this->start_controls_section( 'section_box_style', [
964 'label' => esc_html__( 'Content', 'elementor' ),
965 'tab' => Controls_Manager::TAB_STYLE,
966 ] );
967
968 $this->add_group_control(
969 Group_Control_Background::get_type(),
970 [
971 'name' => 'box_background_color',
972 'types' => [ 'classic', 'gradient' ],
973 'exclude' => [ 'image' ],
974 'selector' => $nested_tabs_content_selector_class,
975 'fields_options' => [
976 'color' => [
977 'label' => esc_html__( 'Background Color', 'elementor' ),
978 ],
979 ],
980 ]
981 );
982
983 $this->add_group_control(
984 Group_Control_Border::get_type(),
985 [
986 'name' => 'box_border',
987 'selector' => $nested_tabs_content_selector_class,
988 'fields_options' => [
989 'color' => [
990 'label' => esc_html__( 'Border Color', 'elementor' ),
991 ],
992 'width' => [
993 'label' => esc_html__( 'Border Width', 'elementor' ),
994 ],
995 ],
996 ]
997 );
998
999 $this->add_responsive_control(
1000 'box_border_radius',
1001 [
1002 'label' => esc_html__( 'Border Radius', 'elementor' ),
1003 'type' => Controls_Manager::DIMENSIONS,
1004 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ],
1005 'selectors' => [
1006 $nested_tabs_content_selector_class => '--border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
1007 ],
1008 ]
1009 );
1010
1011 $this->add_group_control(
1012 Group_Control_Box_Shadow::get_type(),
1013 [
1014 'name' => 'box_shadow_box_shadow',
1015 'selector' => $nested_tabs_content_selector_class,
1016 'condition' => [
1017 'box_height!' => 'height',
1018 ],
1019 ]
1020 );
1021
1022 $this->add_responsive_control(
1023 'box_padding',
1024 [
1025 'label' => esc_html__( 'Padding', 'elementor' ),
1026 'type' => Controls_Manager::DIMENSIONS,
1027 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
1028 'selectors' => [
1029 $nested_tabs_content_selector_class => '--padding-top: {{TOP}}{{UNIT}}; --padding-right: {{RIGHT}}{{UNIT}}; --padding-bottom: {{BOTTOM}}{{UNIT}}; --padding-left: {{LEFT}}{{UNIT}};',
1030 ],
1031 ]
1032 );
1033
1034 $this->end_controls_section();
1035 }
1036
1037 protected function render() {
1038 // Copied from tabs.php
1039 $settings = $this->get_settings_for_display();
1040 $tabs = $settings['tabs'];
1041
1042 $id_int = substr( $this->get_id_int(), 0, 3 );
1043
1044 if ( ! empty( $settings['link'] ) ) {
1045 $this->add_link_attributes( 'elementor-tabs', $settings['link'] );
1046 }
1047
1048 $this->add_render_attribute( 'elementor-tabs', 'class', 'e-n-tabs' );
1049 $this->add_render_attribute( 'tab-title-text', 'class', 'e-n-tab-title-text' );
1050 $this->add_render_attribute( 'tab-icon', 'class', 'e-n-tab-icon' );
1051 $this->add_render_attribute( 'tab-icon-active', 'class', [ 'e-n-tab-icon', 'e-active' ] );
1052
1053 $tabs_title_html = '';
1054 $mobile_tabs_title_html = '';
1055
1056 foreach ( $tabs as $index => $item ) {
1057 // Tabs title.
1058 $tab_count = $index + 1;
1059 $tab_title_setting_key = $this->get_repeater_setting_key( 'tab_title', 'tabs', $index );
1060 $tab_title = $item['tab_title'];
1061 $tab_title_mobile_setting_key = $this->get_repeater_setting_key( 'tab_title_mobile', 'tabs', $tab_count );
1062 $tab_title_classes = [ 'e-n-tab-title', 'e-normal' ];
1063 $tab_title_mobile_classes = [ 'e-n-tab-title', 'e-collapse' ];
1064
1065 if ( $settings['hover_animation'] ) {
1066 array_push( $tab_title_classes, 'elementor-animation-' . $settings['hover_animation'] );
1067 array_push( $tab_title_mobile_classes, 'elementor-animation-' . $settings['hover_animation'] );
1068 }
1069
1070 $tab_id = empty( $item['element_id'] ) ? 'e-n-tabs-title-' . $id_int . $tab_count : $item['element_id'];
1071
1072 $this->add_render_attribute( $tab_title_setting_key, [
1073 'id' => $tab_id,
1074 'class' => $tab_title_classes,
1075 'aria-selected' => 1 === $tab_count ? 'true' : 'false',
1076 'data-tab' => $tab_count,
1077 'role' => 'tab',
1078 'tabindex' => 1 === $tab_count ? '0' : '-1',
1079 'aria-controls' => 'e-n-tab-content-' . $id_int . $tab_count,
1080 'aria-expanded' => 'false',
1081 ] );
1082
1083 $this->add_render_attribute( $tab_title_mobile_setting_key, [
1084 'class' => $tab_title_mobile_classes,
1085 'aria-selected' => 1 === $tab_count ? 'true' : 'false',
1086 'data-tab' => $tab_count,
1087 'role' => 'tab',
1088 'tabindex' => 1 === $tab_count ? '0' : '-1',
1089 'aria-controls' => 'e-n-tab-content-' . $id_int . $tab_count,
1090 'aria-expanded' => 'false',
1091 'id' => $tab_id . '-accordion',
1092 ] );
1093
1094 $title_render_attributes = $this->get_render_attribute_string( $tab_title_setting_key );
1095 $mobile_title_attributes = $this->get_render_attribute_string( $tab_title_mobile_setting_key );
1096 $tab_title_text_class = $this->get_render_attribute_string( 'tab-title-text' );
1097 $tab_icon_class = $this->get_render_attribute_string( 'tab-icon' );
1098
1099 $icon_html = Icons_Manager::try_get_icon_html( $item['tab_icon'], [ 'aria-hidden' => 'true' ] );
1100 $icon_active_html = $icon_html;
1101
1102 if ( $this->is_active_icon_exist( $item ) ) {
1103 $icon_active_html = Icons_Manager::try_get_icon_html( $item['tab_icon_active'], [ 'aria-hidden' => 'true' ] );
1104 }
1105
1106 $tabs_title_html .= "<div {$title_render_attributes}>";
1107 $tabs_title_html .= "\t<span {$tab_icon_class}>{$icon_html}{$icon_active_html}</span>";
1108 $tabs_title_html .= "\t<span {$tab_title_text_class}>{$tab_title}</span>";
1109 $tabs_title_html .= '</div>';
1110
1111 // Tabs content.
1112 ob_start();
1113 $this->print_child( $index );
1114 $tab_content = ob_get_clean();
1115
1116 $mobile_tabs_title_html .= "<div $mobile_title_attributes>";
1117 $mobile_tabs_title_html .= "\t<span {$tab_icon_class}>{$icon_html}{$icon_active_html}</span>";
1118 $mobile_tabs_title_html .= "\t<span {$tab_title_text_class}>{$tab_title}</span>";
1119 $mobile_tabs_title_html .= "</div>$tab_content";
1120 }
1121 ?>
1122 <div <?php $this->print_render_attribute_string( 'elementor-tabs' ); ?>>
1123 <div class="e-n-tabs-heading" role="tablist">
1124 <?php echo $tabs_title_html;// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
1125 </div>
1126 <div class="e-n-tabs-content" role="tablist" aria-orientation="vertical">
1127 <?php echo $mobile_tabs_title_html;// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
1128 </div>
1129 </div>
1130 <?php
1131 }
1132
1133 protected function content_template() {
1134 ?>
1135 <div class="e-n-tabs" role="tablist" aria-orientation="vertical">
1136 <# if ( settings['tabs'] ) {
1137 const elementUid = view.getIDInt().toString().substr( 0, 3 ); #>
1138 <div class="e-n-tabs-heading" role="tablist">
1139 <# _.each( settings['tabs'], function( item, index ) {
1140 const tabCount = index + 1,
1141 tabUid = elementUid + tabCount,
1142 tabWrapperKey = tabUid,
1143 tabTitleKey = 'tab-title-' + tabUid,
1144 tabIconKey = 'tab-icon-' + tabUid,
1145 tabIcon = elementor.helpers.renderIcon( view, item.tab_icon, { 'aria-hidden': true }, 'i' , 'object' ),
1146 hoverAnimationClass = settings['hover_animation'] ? `elementor-animation-${ settings['hover_animation'] }` : '';
1147
1148 let tabActiveIcon = tabIcon,
1149 tabId = 'e-n-tab-title-' + tabUid;
1150
1151 if ( '' !== item.tab_icon_active.value ) {
1152 tabActiveIcon = elementor.helpers.renderIcon( view, item.tab_icon_active, { 'aria-hidden': true }, 'i' , 'object' );
1153 }
1154
1155 if ( '' !== item.element_id ) {
1156 tabId = item.element_id;
1157 }
1158
1159 view.addRenderAttribute( tabWrapperKey, {
1160 'id': tabId,
1161 'class': [ 'e-n-tab-title','e-normal',hoverAnimationClass ],
1162 'data-tab': tabCount,
1163 'role': 'tab',
1164 'tabindex': 1 === tabCount ? '0' : '-1',
1165 'aria-controls': 'e-n-tab-content-' + tabUid,
1166 'aria-expanded': 'false',
1167 } );
1168
1169 view.addRenderAttribute( tabTitleKey, {
1170 'class': [ 'e-n-tab-title-text' ],
1171 'data-binding-type': 'repeater-item',
1172 'data-binding-repeater-name': 'tabs',
1173 'data-binding-setting': [ 'tab_title' ],
1174 'data-binding-index': tabCount,
1175 } );
1176
1177 view.addRenderAttribute( tabIconKey, {
1178 'class': [ 'e-n-tab-icon' ],
1179 'data-binding-type': 'repeater-item',
1180 'data-binding-repeater-name': 'tabs',
1181 'data-binding-setting': [ 'tab_icon.value', 'tab_icon_active.value' ],
1182 'data-binding-index': tabCount,
1183 } );
1184 #>
1185 <div {{{ view.getRenderAttributeString( tabWrapperKey ) }}}>
1186 <span {{{ view.getRenderAttributeString( tabIconKey ) }}}>{{{ tabIcon.value }}}{{{ tabActiveIcon.value }}}</span>
1187 <span {{{ view.getRenderAttributeString( tabTitleKey ) }}}>{{{ item.tab_title }}}</span>
1188 </div>
1189 <# } ); #>
1190 </div>
1191 <div class="e-n-tabs-content">
1192 </div>
1193 <# } #>
1194 </div>
1195 <?php
1196 }
1197
1198 /**
1199 * @param $item
1200 * @return bool
1201 */
1202 private function is_active_icon_exist( $item ) {
1203 return array_key_exists( 'tab_icon_active', $item ) && ! empty( $item['tab_icon_active'] ) && ! empty( $item['tab_icon_active']['value'] );
1204 }
1205 }
1206