PluginProbe
Elementor Website Builder – more than just a page builder / 3.13.2
Elementor Website Builder – more than just a page builder v3.13.2
4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 4.1.0-beta1 4.1.0-dev1 4.0.7 All 451 releases
elementor / includes / widgets / tabs.php

tabs.php in Elementor Website Builder – more than just a page builder 3.13.2, at includes/widgets/tabs.php

599 lines 16.2 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 use Elementor\Core\Kits\Documents\Tabs\Global_Colors;
9 use Elementor\Core\Kits\Documents\Tabs\Global_Typography;
10
11 /**
12 * Elementor tabs widget.
13 *
14 * Elementor widget that displays vertical or horizontal tabs with different
15 * pieces of content.
16 *
17 * @since 1.0.0
18 */
19 class Widget_Tabs extends Widget_Base {
20
21 /**
22 * Get widget name.
23 *
24 * Retrieve tabs widget name.
25 *
26 * @since 1.0.0
27 * @access public
28 *
29 * @return string Widget name.
30 */
31 public function get_name() {
32 return 'tabs';
33 }
34
35 /**
36 * Get widget title.
37 *
38 * Retrieve tabs widget title.
39 *
40 * @since 1.0.0
41 * @access public
42 *
43 * @return string Widget title.
44 */
45 public function get_title() {
46 return esc_html__( 'Tabs', 'elementor' );
47 }
48
49 /**
50 * Get widget icon.
51 *
52 * Retrieve tabs widget icon.
53 *
54 * @since 1.0.0
55 * @access public
56 *
57 * @return string Widget icon.
58 */
59 public function get_icon() {
60 return 'eicon-tabs';
61 }
62
63 /**
64 * Get widget keywords.
65 *
66 * Retrieve the list of keywords the widget belongs to.
67 *
68 * @since 2.1.0
69 * @access public
70 *
71 * @return array Widget keywords.
72 */
73 public function get_keywords() {
74 return [ 'tabs', 'accordion', 'toggle' ];
75 }
76
77 public function show_in_panel(): bool {
78 return ! Plugin::$instance->experiments->is_feature_active( 'nested-elements' );
79 }
80
81 /**
82 * Register tabs widget controls.
83 *
84 * Adds different input fields to allow the user to change and customize the widget settings.
85 *
86 * @since 3.1.0
87 * @access protected
88 */
89 protected function register_controls() {
90 $this->start_controls_section(
91 'section_tabs',
92 [
93 'label' => esc_html__( 'Tabs', 'elementor' ),
94 ]
95 );
96
97 $repeater = new Repeater();
98
99 $repeater->add_control(
100 'tab_title',
101 [
102 'label' => esc_html__( 'Title & Description', 'elementor' ),
103 'type' => Controls_Manager::TEXT,
104 'default' => esc_html__( 'Tab Title', 'elementor' ),
105 'placeholder' => esc_html__( 'Tab Title', 'elementor' ),
106 'label_block' => true,
107 'dynamic' => [
108 'active' => true,
109 ],
110 ]
111 );
112
113 $repeater->add_control(
114 'tab_content',
115 [
116 'label' => esc_html__( 'Content', 'elementor' ),
117 'default' => esc_html__( 'Tab Content', 'elementor' ),
118 'placeholder' => esc_html__( 'Tab Content', 'elementor' ),
119 'type' => Controls_Manager::WYSIWYG,
120 'show_label' => false,
121 ]
122 );
123
124 $is_nested_tabs_active = Plugin::$instance->widgets_manager->get_widget_types( 'nested-tabs' );
125
126 if ( $is_nested_tabs_active ) {
127 $this->add_deprecation_message(
128 '3.8.0',
129 esc_html__(
130 'You are currently editing a Tabs Widget in its old version. Any new tabs widget dragged into the canvas will be the new Tab widget, with the improved Nested capabilities.',
131 'elementor'
132 ),
133 'nested-tabs'
134 );
135 }
136
137 $this->add_control(
138 'tabs',
139 [
140 'label' => esc_html__( 'Tabs Items', 'elementor' ),
141 'type' => Controls_Manager::REPEATER,
142 'fields' => $repeater->get_controls(),
143 'default' => [
144 [
145 'tab_title' => esc_html__( 'Tab #1', 'elementor' ),
146 'tab_content' => esc_html__( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.', 'elementor' ),
147 ],
148 [
149 'tab_title' => esc_html__( 'Tab #2', 'elementor' ),
150 'tab_content' => esc_html__( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.', 'elementor' ),
151 ],
152 ],
153 'title_field' => '{{{ tab_title }}}',
154 ]
155 );
156
157 $this->add_control(
158 'view',
159 [
160 'label' => esc_html__( 'View', 'elementor' ),
161 'type' => Controls_Manager::HIDDEN,
162 'default' => 'traditional',
163 ]
164 );
165
166 $this->add_control(
167 'type',
168 [
169 'label' => esc_html__( 'Position', 'elementor' ),
170 'type' => Controls_Manager::SELECT,
171 'default' => 'horizontal',
172 'options' => [
173 'horizontal' => esc_html__( 'Horizontal', 'elementor' ),
174 'vertical' => esc_html__( 'Vertical', 'elementor' ),
175 ],
176 'prefix_class' => 'elementor-tabs-view-',
177 'separator' => 'before',
178 ]
179 );
180
181 $this->add_control(
182 'tabs_align_horizontal',
183 [
184 'label' => esc_html__( 'Alignment', 'elementor' ),
185 'type' => Controls_Manager::CHOOSE,
186 'options' => [
187 '' => [
188 'title' => esc_html__( 'Start', 'elementor' ),
189 'icon' => 'eicon-h-align-left',
190 ],
191 'center' => [
192 'title' => esc_html__( 'Center', 'elementor' ),
193 'icon' => 'eicon-h-align-center',
194 ],
195 'end' => [
196 'title' => esc_html__( 'End', 'elementor' ),
197 'icon' => 'eicon-h-align-right',
198 ],
199 'stretch' => [
200 'title' => esc_html__( 'Justified', 'elementor' ),
201 'icon' => 'eicon-h-align-stretch',
202 ],
203 ],
204 'prefix_class' => 'elementor-tabs-alignment-',
205 'condition' => [
206 'type' => 'horizontal',
207 ],
208 ]
209 );
210
211 $this->add_control(
212 'tabs_align_vertical',
213 [
214 'label' => esc_html__( 'Alignment', 'elementor' ),
215 'type' => Controls_Manager::CHOOSE,
216 'options' => [
217 '' => [
218 'title' => esc_html__( 'Start', 'elementor' ),
219 'icon' => 'eicon-v-align-top',
220 ],
221 'center' => [
222 'title' => esc_html__( 'Center', 'elementor' ),
223 'icon' => 'eicon-v-align-middle',
224 ],
225 'end' => [
226 'title' => esc_html__( 'End', 'elementor' ),
227 'icon' => 'eicon-v-align-bottom',
228 ],
229 'stretch' => [
230 'title' => esc_html__( 'Justified', 'elementor' ),
231 'icon' => 'eicon-v-align-stretch',
232 ],
233 ],
234 'prefix_class' => 'elementor-tabs-alignment-',
235 'condition' => [
236 'type' => 'vertical',
237 ],
238 ]
239 );
240
241 $this->end_controls_section();
242
243 $this->start_controls_section(
244 'section_tabs_style',
245 [
246 'label' => esc_html__( 'Tabs', 'elementor' ),
247 'tab' => Controls_Manager::TAB_STYLE,
248 ]
249 );
250
251 $this->add_control(
252 'navigation_width',
253 [
254 'label' => esc_html__( 'Navigation Width', 'elementor' ),
255 'type' => Controls_Manager::SLIDER,
256 'default' => [
257 'unit' => '%',
258 ],
259 'range' => [
260 '%' => [
261 'min' => 10,
262 'max' => 50,
263 ],
264 ],
265 'selectors' => [
266 '{{WRAPPER}} .elementor-tabs-wrapper' => 'width: {{SIZE}}{{UNIT}}',
267 ],
268 'condition' => [
269 'type' => 'vertical',
270 ],
271 ]
272 );
273
274 $this->add_control(
275 'border_width',
276 [
277 'label' => esc_html__( 'Border Width', 'elementor' ),
278 'type' => Controls_Manager::SLIDER,
279 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
280 'default' => [
281 'size' => 1,
282 ],
283 'range' => [
284 'px' => [
285 'max' => 20,
286 ],
287 'em' => [
288 'max' => 2,
289 ],
290 ],
291 'selectors' => [
292 '{{WRAPPER}} .elementor-tab-title, {{WRAPPER}} .elementor-tab-title:before, {{WRAPPER}} .elementor-tab-title:after, {{WRAPPER}} .elementor-tab-content, {{WRAPPER}} .elementor-tabs-content-wrapper' => 'border-width: {{SIZE}}{{UNIT}};',
293 ],
294 ]
295 );
296
297 $this->add_control(
298 'border_color',
299 [
300 'label' => esc_html__( 'Border Color', 'elementor' ),
301 'type' => Controls_Manager::COLOR,
302 'selectors' => [
303 '{{WRAPPER}} .elementor-tab-mobile-title, {{WRAPPER}} .elementor-tab-desktop-title.elementor-active, {{WRAPPER}} .elementor-tab-title:before, {{WRAPPER}} .elementor-tab-title:after, {{WRAPPER}} .elementor-tab-content, {{WRAPPER}} .elementor-tabs-content-wrapper' => 'border-color: {{VALUE}};',
304 ],
305 ]
306 );
307
308 $this->add_control(
309 'background_color',
310 [
311 'label' => esc_html__( 'Background Color', 'elementor' ),
312 'type' => Controls_Manager::COLOR,
313 'selectors' => [
314 '{{WRAPPER}} .elementor-tab-desktop-title.elementor-active' => 'background-color: {{VALUE}};',
315 '{{WRAPPER}} .elementor-tabs-content-wrapper' => 'background-color: {{VALUE}};',
316 ],
317 ]
318 );
319
320 $this->add_control(
321 'heading_title',
322 [
323 'label' => esc_html__( 'Title', 'elementor' ),
324 'type' => Controls_Manager::HEADING,
325 'separator' => 'before',
326 ]
327 );
328
329 $this->add_control(
330 'tab_color',
331 [
332 'label' => esc_html__( 'Color', 'elementor' ),
333 'type' => Controls_Manager::COLOR,
334 'selectors' => [
335 '{{WRAPPER}} .elementor-tab-title, {{WRAPPER}} .elementor-tab-title a' => 'color: {{VALUE}}',
336 ],
337 'global' => [
338 'default' => Global_Colors::COLOR_PRIMARY,
339 ],
340 ]
341 );
342
343 $this->add_control(
344 'tab_active_color',
345 [
346 'label' => esc_html__( 'Active Color', 'elementor' ),
347 'type' => Controls_Manager::COLOR,
348 'selectors' => [
349 '{{WRAPPER}} .elementor-tab-title.elementor-active,
350 {{WRAPPER}} .elementor-tab-title.elementor-active a' => 'color: {{VALUE}}',
351 ],
352 'global' => [
353 'default' => Global_Colors::COLOR_ACCENT,
354 ],
355 ]
356 );
357
358 $this->add_group_control(
359 Group_Control_Typography::get_type(),
360 [
361 'name' => 'tab_typography',
362 'selector' => '{{WRAPPER}} .elementor-tab-title',
363 'global' => [
364 'default' => Global_Typography::TYPOGRAPHY_PRIMARY,
365 ],
366 ]
367 );
368
369 $this->add_group_control(
370 Group_Control_Text_Stroke::get_type(),
371 [
372 'name' => 'text_stroke',
373 'selector' => '{{WRAPPER}} .elementor-tab-title',
374 ]
375 );
376
377 $this->add_group_control(
378 Group_Control_Text_Shadow::get_type(),
379 [
380 'name' => 'title_shadow',
381 'selector' => '{{WRAPPER}} .elementor-tab-title',
382 ]
383 );
384
385 $this->add_control(
386 'title_align',
387 [
388 'label' => esc_html__( 'Alignment', 'elementor' ),
389 'type' => Controls_Manager::CHOOSE,
390 'options' => [
391 'left' => [
392 'title' => esc_html__( 'Left', 'elementor' ),
393 'icon' => 'eicon-text-align-left',
394 ],
395 'center' => [
396 'title' => esc_html__( 'Center', 'elementor' ),
397 'icon' => 'eicon-text-align-center',
398 ],
399 'right' => [
400 'title' => esc_html__( 'Right', 'elementor' ),
401 'icon' => 'eicon-text-align-right',
402 ],
403 ],
404 'selectors' => [
405 '{{WRAPPER}} .elementor-tab-title' => 'text-align: {{VALUE}};',
406 ],
407 'condition' => [
408 'tabs_align' => 'stretch',
409 ],
410 ]
411 );
412
413 $this->add_control(
414 'heading_content',
415 [
416 'label' => esc_html__( 'Content', 'elementor' ),
417 'type' => Controls_Manager::HEADING,
418 'separator' => 'before',
419 ]
420 );
421
422 $this->add_control(
423 'content_color',
424 [
425 'label' => esc_html__( 'Color', 'elementor' ),
426 'type' => Controls_Manager::COLOR,
427 'selectors' => [
428 '{{WRAPPER}} .elementor-tab-content' => 'color: {{VALUE}};',
429 ],
430 'global' => [
431 'default' => Global_Colors::COLOR_TEXT,
432 ],
433 ]
434 );
435
436 $this->add_group_control(
437 Group_Control_Typography::get_type(),
438 [
439 'name' => 'content_typography',
440 'selector' => '{{WRAPPER}} .elementor-tab-content',
441 'global' => [
442 'default' => Global_Typography::TYPOGRAPHY_TEXT,
443 ],
444 ]
445 );
446
447 $this->add_group_control(
448 Group_Control_Text_Shadow::get_type(),
449 [
450 'name' => 'content_shadow',
451 'selector' => '{{WRAPPER}} .elementor-tab-content',
452 ]
453 );
454
455 $this->end_controls_section();
456 }
457
458 /**
459 * Render tabs widget output on the frontend.
460 *
461 * Written in PHP and used to generate the final HTML.
462 *
463 * @since 1.0.0
464 * @access protected
465 */
466 protected function render() {
467 $tabs = $this->get_settings_for_display( 'tabs' );
468
469 $id_int = substr( $this->get_id_int(), 0, 3 );
470
471 $a11y_improvements_experiment = Plugin::$instance->experiments->is_feature_active( 'a11y_improvements' );
472
473 $this->add_render_attribute( 'elementor-tabs', 'class', 'elementor-tabs' );
474
475 ?>
476 <div <?php $this->print_render_attribute_string( 'elementor-tabs' ); ?>>
477 <div class="elementor-tabs-wrapper" role="tablist" >
478 <?php
479 foreach ( $tabs as $index => $item ) :
480 $tab_count = $index + 1;
481 $tab_title_setting_key = $this->get_repeater_setting_key( 'tab_title', 'tabs', $index );
482 $tab_title = $a11y_improvements_experiment ? $item['tab_title'] : '<a href="">' . $item['tab_title'] . '</a>';
483
484 $this->add_render_attribute( $tab_title_setting_key, [
485 'id' => 'elementor-tab-title-' . $id_int . $tab_count,
486 'class' => [ 'elementor-tab-title', 'elementor-tab-desktop-title' ],
487 'aria-selected' => 1 === $tab_count ? 'true' : 'false',
488 'data-tab' => $tab_count,
489 'role' => 'tab',
490 'tabindex' => 1 === $tab_count ? '0' : '-1',
491 'aria-controls' => 'elementor-tab-content-' . $id_int . $tab_count,
492 'aria-expanded' => 'false',
493 ] );
494 ?>
495 <div <?php $this->print_render_attribute_string( $tab_title_setting_key ); ?>><?php
496 // PHPCS - the main text of a widget should not be escaped.
497 echo $tab_title; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
498 ?></div>
499 <?php endforeach; ?>
500 </div>
501 <div class="elementor-tabs-content-wrapper" role="tablist" aria-orientation="vertical">
502 <?php
503 foreach ( $tabs as $index => $item ) :
504 $tab_count = $index + 1;
505 $hidden = 1 === $tab_count ? 'false' : 'hidden';
506 $tab_content_setting_key = $this->get_repeater_setting_key( 'tab_content', 'tabs', $index );
507
508 $tab_title_mobile_setting_key = $this->get_repeater_setting_key( 'tab_title_mobile', 'tabs', $tab_count );
509
510 $this->add_render_attribute( $tab_content_setting_key, [
511 'id' => 'elementor-tab-content-' . $id_int . $tab_count,
512 'class' => [ 'elementor-tab-content', 'elementor-clearfix' ],
513 'data-tab' => $tab_count,
514 'role' => 'tabpanel',
515 'aria-labelledby' => 'elementor-tab-title-' . $id_int . $tab_count,
516 'tabindex' => '0',
517 'hidden' => $hidden,
518 ] );
519
520 $this->add_render_attribute( $tab_title_mobile_setting_key, [
521 'class' => [ 'elementor-tab-title', 'elementor-tab-mobile-title' ],
522 'aria-selected' => 1 === $tab_count ? 'true' : 'false',
523 'data-tab' => $tab_count,
524 'role' => 'tab',
525 'tabindex' => 1 === $tab_count ? '0' : '-1',
526 'aria-controls' => 'elementor-tab-content-' . $id_int . $tab_count,
527 'aria-expanded' => 'false',
528 ] );
529
530 $this->add_inline_editing_attributes( $tab_content_setting_key, 'advanced' );
531 ?>
532 <div <?php $this->print_render_attribute_string( $tab_title_mobile_setting_key ); ?>><?php
533 $this->print_unescaped_setting( 'tab_title', 'tabs', $index );
534 ?></div>
535 <div <?php $this->print_render_attribute_string( $tab_content_setting_key ); ?>><?php
536 $this->print_text_editor( $item['tab_content'] );
537 ?></div>
538 <?php endforeach; ?>
539 </div>
540 </div>
541 <?php
542 }
543
544 /**
545 * Render tabs widget output in the editor.
546 *
547 * Written as a Backbone JavaScript template and used to generate the live preview.
548 *
549 * @since 2.9.0
550 * @access protected
551 */
552 protected function content_template() {
553 ?>
554 <div class="elementor-tabs" role="tablist" aria-orientation="vertical">
555 <# if ( settings.tabs ) {
556 var elementUid = view.getIDInt().toString().substr( 0, 3 ); #>
557 <div class="elementor-tabs-wrapper" role="tablist">
558 <# _.each( settings.tabs, function( item, index ) {
559 var tabCount = index + 1,
560 tabUid = elementUid + tabCount,
561 tabTitleKey = 'tab-title-' + tabUid;
562
563 view.addRenderAttribute( tabTitleKey, {
564 'id': 'elementor-tab-title-' + tabUid,
565 'class': [ 'elementor-tab-title','elementor-tab-desktop-title' ],
566 'data-tab': tabCount,
567 'role': 'tab',
568 'tabindex': 1 === tabCount ? '0' : '-1',
569 'aria-controls': 'elementor-tab-content-' + tabUid,
570 'aria-expanded': 'false',
571 } );
572 #>
573 <div {{{ view.getRenderAttributeString( tabTitleKey ) }}}>{{{ item.tab_title }}}</div>
574 <# } ); #>
575 </div>
576 <div class="elementor-tabs-content-wrapper">
577 <# _.each( settings.tabs, function( item, index ) {
578 var tabCount = index + 1,
579 tabContentKey = view.getRepeaterSettingKey( 'tab_content', 'tabs',index );
580
581 view.addRenderAttribute( tabContentKey, {
582 'id': 'elementor-tab-content-' + elementUid + tabCount,
583 'class': [ 'elementor-tab-content', 'elementor-clearfix', 'elementor-repeater-item-' + item._id ],
584 'data-tab': tabCount,
585 'role' : 'tabpanel',
586 'aria-labelledby' : 'elementor-tab-title-' + elementUid + tabCount
587 } );
588
589 view.addInlineEditingAttributes( tabContentKey, 'advanced' ); #>
590 <div class="elementor-tab-title elementor-tab-mobile-title" data-tab="{{ tabCount }}" role="tab">{{{ item.tab_title }}}</div>
591 <div {{{ view.getRenderAttributeString( tabContentKey ) }}}>{{{ item.tab_content }}}</div>
592 <# } ); #>
593 </div>
594 <# } #>
595 </div>
596 <?php
597 }
598 }
599