PluginProbe
Elementor Website Builder – more than just a page builder / 3.20.0-dev2
Elementor Website Builder – more than just a page builder v3.20.0-dev2
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.20.0-dev2, at includes/widgets/tabs.php

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