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