accordion.php (18879B)
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 * Elementor accordion widget. 12 * 13 * Elementor widget that displays a collapsible display of content in an 14 * accordion style, showing only one item at a time. 15 * 16 * @since 1.0.0 17 */ 18 class Widget_Accordion extends Widget_Base { 19 20 /** 21 * Get widget name. 22 * 23 * Retrieve accordion widget name. 24 * 25 * @since 1.0.0 26 * @access public 27 * 28 * @return string Widget name. 29 */ 30 public function get_name() { 31 return 'accordion'; 32 } 33 34 /** 35 * Get widget title. 36 * 37 * Retrieve accordion widget title. 38 * 39 * @since 1.0.0 40 * @access public 41 * 42 * @return string Widget title. 43 */ 44 public function get_title() { 45 return esc_html__( 'Accordion', 'elementor' ); 46 } 47 48 /** 49 * Get widget icon. 50 * 51 * Retrieve accordion widget icon. 52 * 53 * @since 1.0.0 54 * @access public 55 * 56 * @return string Widget icon. 57 */ 58 public function get_icon() { 59 return 'eicon-accordion'; 60 } 61 62 /** 63 * Get widget keywords. 64 * 65 * Retrieve the list of keywords the widget belongs to. 66 * 67 * @since 2.1.0 68 * @access public 69 * 70 * @return array Widget keywords. 71 */ 72 public function get_keywords() { 73 return [ 'accordion', 'tabs', 'toggle' ]; 74 } 75 76 /** 77 * Register accordion widget controls. 78 * 79 * Adds different input fields to allow the user to change and customize the widget settings. 80 * 81 * @since 3.1.0 82 * @access protected 83 */ 84 protected function register_controls() { 85 $this->start_controls_section( 86 'section_title', 87 [ 88 'label' => esc_html__( 'Accordion', 'elementor' ), 89 ] 90 ); 91 92 $repeater = new Repeater(); 93 94 $repeater->add_control( 95 'tab_title', 96 [ 97 'label' => esc_html__( 'Title & Description', 'elementor' ), 98 'type' => Controls_Manager::TEXT, 99 'default' => esc_html__( 'Accordion Title', 'elementor' ), 100 'dynamic' => [ 101 'active' => true, 102 ], 103 'label_block' => true, 104 ] 105 ); 106 107 $repeater->add_control( 108 'tab_content', 109 [ 110 'label' => esc_html__( 'Content', 'elementor' ), 111 'type' => Controls_Manager::WYSIWYG, 112 'default' => esc_html__( 'Accordion Content', 'elementor' ), 113 'show_label' => false, 114 ] 115 ); 116 117 $this->add_control( 118 'tabs', 119 [ 120 'label' => esc_html__( 'Accordion Items', 'elementor' ), 121 'type' => Controls_Manager::REPEATER, 122 'fields' => $repeater->get_controls(), 123 'default' => [ 124 [ 125 'tab_title' => esc_html__( 'Accordion #1', 'elementor' ), 126 'tab_content' => esc_html__( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.', 'elementor' ), 127 ], 128 [ 129 'tab_title' => esc_html__( 'Accordion #2', 'elementor' ), 130 'tab_content' => esc_html__( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.', 'elementor' ), 131 ], 132 ], 133 'title_field' => '{{{ tab_title }}}', 134 ] 135 ); 136 137 $this->add_control( 138 'view', 139 [ 140 'label' => esc_html__( 'View', 'elementor' ), 141 'type' => Controls_Manager::HIDDEN, 142 'default' => 'traditional', 143 ] 144 ); 145 146 $this->add_control( 147 'selected_icon', 148 [ 149 'label' => esc_html__( 'Icon', 'elementor' ), 150 'type' => Controls_Manager::ICONS, 151 'separator' => 'before', 152 'fa4compatibility' => 'icon', 153 'default' => [ 154 'value' => 'fas fa-plus', 155 'library' => 'fa-solid', 156 ], 157 'recommended' => [ 158 'fa-solid' => [ 159 'chevron-down', 160 'angle-down', 161 'angle-double-down', 162 'caret-down', 163 'caret-square-down', 164 ], 165 'fa-regular' => [ 166 'caret-square-down', 167 ], 168 ], 169 'skin' => 'inline', 170 'label_block' => false, 171 ] 172 ); 173 174 $this->add_control( 175 'selected_active_icon', 176 [ 177 'label' => esc_html__( 'Active Icon', 'elementor' ), 178 'type' => Controls_Manager::ICONS, 179 'fa4compatibility' => 'icon_active', 180 'default' => [ 181 'value' => 'fas fa-minus', 182 'library' => 'fa-solid', 183 ], 184 'recommended' => [ 185 'fa-solid' => [ 186 'chevron-up', 187 'angle-up', 188 'angle-double-up', 189 'caret-up', 190 'caret-square-up', 191 ], 192 'fa-regular' => [ 193 'caret-square-up', 194 ], 195 ], 196 'skin' => 'inline', 197 'label_block' => false, 198 'condition' => [ 199 'selected_icon[value]!' => '', 200 ], 201 ] 202 ); 203 204 $this->add_control( 205 'title_html_tag', 206 [ 207 'label' => esc_html__( 'Title HTML Tag', 'elementor' ), 208 'type' => Controls_Manager::SELECT, 209 'options' => [ 210 'h1' => 'H1', 211 'h2' => 'H2', 212 'h3' => 'H3', 213 'h4' => 'H4', 214 'h5' => 'H5', 215 'h6' => 'H6', 216 'div' => 'div', 217 ], 218 'default' => 'div', 219 'separator' => 'before', 220 ] 221 ); 222 223 $this->add_control( 224 'faq_schema', 225 [ 226 'label' => esc_html__( 'FAQ Schema', 'elementor' ), 227 'type' => Controls_Manager::SWITCHER, 228 'separator' => 'before', 229 ] 230 ); 231 232 $this->end_controls_section(); 233 234 $this->start_controls_section( 235 'section_title_style', 236 [ 237 'label' => esc_html__( 'Accordion', 'elementor' ), 238 'tab' => Controls_Manager::TAB_STYLE, 239 ] 240 ); 241 242 $this->add_control( 243 'border_width', 244 [ 245 'label' => esc_html__( 'Border Width', 'elementor' ), 246 'type' => Controls_Manager::SLIDER, 247 'range' => [ 248 'px' => [ 249 'min' => 0, 250 'max' => 10, 251 ], 252 ], 253 'selectors' => [ 254 '{{WRAPPER}} .elementor-accordion-item' => 'border-width: {{SIZE}}{{UNIT}};', 255 '{{WRAPPER}} .elementor-accordion-item .elementor-tab-content' => 'border-width: {{SIZE}}{{UNIT}};', 256 '{{WRAPPER}} .elementor-accordion-item .elementor-tab-title.elementor-active' => 'border-width: {{SIZE}}{{UNIT}};', 257 ], 258 ] 259 ); 260 261 $this->add_control( 262 'border_color', 263 [ 264 'label' => esc_html__( 'Border Color', 'elementor' ), 265 'type' => Controls_Manager::COLOR, 266 'selectors' => [ 267 '{{WRAPPER}} .elementor-accordion-item' => 'border-color: {{VALUE}};', 268 '{{WRAPPER}} .elementor-accordion-item .elementor-tab-content' => 'border-top-color: {{VALUE}};', 269 '{{WRAPPER}} .elementor-accordion-item .elementor-tab-title.elementor-active' => 'border-bottom-color: {{VALUE}};', 270 ], 271 ] 272 ); 273 274 $this->end_controls_section(); 275 276 $this->start_controls_section( 277 'section_toggle_style_title', 278 [ 279 'label' => esc_html__( 'Title', 'elementor' ), 280 'tab' => Controls_Manager::TAB_STYLE, 281 ] 282 ); 283 284 $this->add_control( 285 'title_background', 286 [ 287 'label' => esc_html__( 'Background', 'elementor' ), 288 'type' => Controls_Manager::COLOR, 289 'selectors' => [ 290 '{{WRAPPER}} .elementor-tab-title' => 'background-color: {{VALUE}};', 291 ], 292 ] 293 ); 294 295 $this->add_control( 296 'title_color', 297 [ 298 'label' => esc_html__( 'Color', 'elementor' ), 299 'type' => Controls_Manager::COLOR, 300 'selectors' => [ 301 '{{WRAPPER}} .elementor-accordion-icon, {{WRAPPER}} .elementor-accordion-title' => 'color: {{VALUE}};', 302 '{{WRAPPER}} .elementor-accordion-icon svg' => 'fill: {{VALUE}};', 303 ], 304 'global' => [ 305 'default' => Global_Colors::COLOR_PRIMARY, 306 ], 307 ] 308 ); 309 310 $this->add_control( 311 'tab_active_color', 312 [ 313 'label' => esc_html__( 'Active Color', 'elementor' ), 314 'type' => Controls_Manager::COLOR, 315 'selectors' => [ 316 '{{WRAPPER}} .elementor-active .elementor-accordion-icon, {{WRAPPER}} .elementor-active .elementor-accordion-title' => 'color: {{VALUE}};', 317 '{{WRAPPER}} .elementor-active .elementor-accordion-icon svg' => 'fill: {{VALUE}};', 318 ], 319 'global' => [ 320 'default' => Global_Colors::COLOR_ACCENT, 321 ], 322 ] 323 ); 324 325 $this->add_group_control( 326 Group_Control_Typography::get_type(), 327 [ 328 'name' => 'title_typography', 329 'selector' => '{{WRAPPER}} .elementor-accordion-title', 330 'global' => [ 331 'default' => Global_Typography::TYPOGRAPHY_PRIMARY, 332 ], 333 ] 334 ); 335 336 $this->add_group_control( 337 Group_Control_Text_Shadow::get_type(), 338 [ 339 'name' => 'title_shadow', 340 'selector' => '{{WRAPPER}} .elementor-accordion-title', 341 ] 342 ); 343 344 $this->add_responsive_control( 345 'title_padding', 346 [ 347 'label' => esc_html__( 'Padding', 'elementor' ), 348 'type' => Controls_Manager::DIMENSIONS, 349 'size_units' => [ 'px', 'em', '%' ], 350 'selectors' => [ 351 '{{WRAPPER}} .elementor-tab-title' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', 352 ], 353 ] 354 ); 355 356 $this->end_controls_section(); 357 358 $this->start_controls_section( 359 'section_toggle_style_icon', 360 [ 361 'label' => esc_html__( 'Icon', 'elementor' ), 362 'tab' => Controls_Manager::TAB_STYLE, 363 'condition' => [ 364 'selected_icon[value]!' => '', 365 ], 366 ] 367 ); 368 369 $this->add_control( 370 'icon_align', 371 [ 372 'label' => esc_html__( 'Alignment', 'elementor' ), 373 'type' => Controls_Manager::CHOOSE, 374 'options' => [ 375 'left' => [ 376 'title' => esc_html__( 'Start', 'elementor' ), 377 'icon' => 'eicon-h-align-left', 378 ], 379 'right' => [ 380 'title' => esc_html__( 'End', 'elementor' ), 381 'icon' => 'eicon-h-align-right', 382 ], 383 ], 384 'default' => is_rtl() ? 'right' : 'left', 385 'toggle' => false, 386 ] 387 ); 388 389 $this->add_control( 390 'icon_color', 391 [ 392 'label' => esc_html__( 'Color', 'elementor' ), 393 'type' => Controls_Manager::COLOR, 394 'selectors' => [ 395 '{{WRAPPER}} .elementor-tab-title .elementor-accordion-icon i:before' => 'color: {{VALUE}};', 396 '{{WRAPPER}} .elementor-tab-title .elementor-accordion-icon svg' => 'fill: {{VALUE}};', 397 ], 398 ] 399 ); 400 401 $this->add_control( 402 'icon_active_color', 403 [ 404 'label' => esc_html__( 'Active Color', 'elementor' ), 405 'type' => Controls_Manager::COLOR, 406 'selectors' => [ 407 '{{WRAPPER}} .elementor-tab-title.elementor-active .elementor-accordion-icon i:before' => 'color: {{VALUE}};', 408 '{{WRAPPER}} .elementor-tab-title.elementor-active .elementor-accordion-icon svg' => 'fill: {{VALUE}};', 409 ], 410 ] 411 ); 412 413 $this->add_responsive_control( 414 'icon_space', 415 [ 416 'label' => esc_html__( 'Spacing', 'elementor' ), 417 'type' => Controls_Manager::SLIDER, 418 'range' => [ 419 'px' => [ 420 'min' => 0, 421 'max' => 100, 422 ], 423 ], 424 'selectors' => [ 425 '{{WRAPPER}} .elementor-accordion-icon.elementor-accordion-icon-left' => 'margin-right: {{SIZE}}{{UNIT}};', 426 '{{WRAPPER}} .elementor-accordion-icon.elementor-accordion-icon-right' => 'margin-left: {{SIZE}}{{UNIT}};', 427 ], 428 ] 429 ); 430 431 $this->end_controls_section(); 432 433 $this->start_controls_section( 434 'section_toggle_style_content', 435 [ 436 'label' => esc_html__( 'Content', 'elementor' ), 437 'tab' => Controls_Manager::TAB_STYLE, 438 ] 439 ); 440 441 $this->add_control( 442 'content_background_color', 443 [ 444 'label' => esc_html__( 'Background', 'elementor' ), 445 'type' => Controls_Manager::COLOR, 446 'selectors' => [ 447 '{{WRAPPER}} .elementor-tab-content' => 'background-color: {{VALUE}};', 448 ], 449 ] 450 ); 451 452 $this->add_control( 453 'content_color', 454 [ 455 'label' => esc_html__( 'Color', 'elementor' ), 456 'type' => Controls_Manager::COLOR, 457 'selectors' => [ 458 '{{WRAPPER}} .elementor-tab-content' => 'color: {{VALUE}};', 459 ], 460 'global' => [ 461 'default' => Global_Colors::COLOR_TEXT, 462 ], 463 ] 464 ); 465 466 $this->add_group_control( 467 Group_Control_Typography::get_type(), 468 [ 469 'name' => 'content_typography', 470 'selector' => '{{WRAPPER}} .elementor-tab-content', 471 'global' => [ 472 'default' => Global_Typography::TYPOGRAPHY_TEXT, 473 ], 474 ] 475 ); 476 477 $this->add_group_control( 478 Group_Control_Text_Shadow::get_type(), 479 [ 480 'name' => 'content_shadow', 481 'selector' => '{{WRAPPER}} .elementor-tab-content', 482 ] 483 ); 484 485 $this->add_responsive_control( 486 'content_padding', 487 [ 488 'label' => esc_html__( 'Padding', 'elementor' ), 489 'type' => Controls_Manager::DIMENSIONS, 490 'size_units' => [ 'px', 'em', '%' ], 491 'selectors' => [ 492 '{{WRAPPER}} .elementor-tab-content' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', 493 ], 494 ] 495 ); 496 497 $this->end_controls_section(); 498 } 499 500 /** 501 * Render accordion widget output on the frontend. 502 * 503 * Written in PHP and used to generate the final HTML. 504 * 505 * @since 1.0.0 506 * @access protected 507 */ 508 protected function render() { 509 $settings = $this->get_settings_for_display(); 510 $migrated = isset( $settings['__fa4_migrated']['selected_icon'] ); 511 512 if ( ! isset( $settings['icon'] ) && ! Icons_Manager::is_migration_allowed() ) { 513 // @todo: remove when deprecated 514 // added as bc in 2.6 515 // add old default 516 $settings['icon'] = 'fa fa-plus'; 517 $settings['icon_active'] = 'fa fa-minus'; 518 $settings['icon_align'] = $this->get_settings( 'icon_align' ); 519 } 520 521 $is_new = empty( $settings['icon'] ) && Icons_Manager::is_migration_allowed(); 522 $has_icon = ( ! $is_new || ! empty( $settings['selected_icon']['value'] ) ); 523 $id_int = substr( $this->get_id_int(), 0, 3 ); 524 ?> 525 <div class="elementor-accordion" role="tablist"> 526 <?php 527 foreach ( $settings['tabs'] as $index => $item ) : 528 $tab_count = $index + 1; 529 530 $tab_title_setting_key = $this->get_repeater_setting_key( 'tab_title', 'tabs', $index ); 531 532 $tab_content_setting_key = $this->get_repeater_setting_key( 'tab_content', 'tabs', $index ); 533 534 $this->add_render_attribute( $tab_title_setting_key, [ 535 'id' => 'elementor-tab-title-' . $id_int . $tab_count, 536 'class' => [ 'elementor-tab-title' ], 537 'data-tab' => $tab_count, 538 'role' => 'tab', 539 'aria-controls' => 'elementor-tab-content-' . $id_int . $tab_count, 540 'aria-expanded' => 'false', 541 ] ); 542 543 $this->add_render_attribute( $tab_content_setting_key, [ 544 'id' => 'elementor-tab-content-' . $id_int . $tab_count, 545 'class' => [ 'elementor-tab-content', 'elementor-clearfix' ], 546 'data-tab' => $tab_count, 547 'role' => 'tabpanel', 548 'aria-labelledby' => 'elementor-tab-title-' . $id_int . $tab_count, 549 ] ); 550 551 $this->add_inline_editing_attributes( $tab_content_setting_key, 'advanced' ); 552 ?> 553 <div class="elementor-accordion-item"> 554 <<?php Utils::print_validated_html_tag( $settings['title_html_tag'] ); ?> <?php $this->print_render_attribute_string( $tab_title_setting_key ); ?>> 555 <?php if ( $has_icon ) : ?> 556 <span class="elementor-accordion-icon elementor-accordion-icon-<?php echo esc_attr( $settings['icon_align'] ); ?>" aria-hidden="true"> 557 <?php 558 if ( $is_new || $migrated ) { ?> 559 <span class="elementor-accordion-icon-closed"><?php Icons_Manager::render_icon( $settings['selected_icon'] ); ?></span> 560 <span class="elementor-accordion-icon-opened"><?php Icons_Manager::render_icon( $settings['selected_active_icon'] ); ?></span> 561 <?php } else { ?> 562 <i class="elementor-accordion-icon-closed <?php echo esc_attr( $settings['icon'] ); ?>"></i> 563 <i class="elementor-accordion-icon-opened <?php echo esc_attr( $settings['icon_active'] ); ?>"></i> 564 <?php } ?> 565 </span> 566 <?php endif; ?> 567 <a class="elementor-accordion-title" href=""><?php 568 $this->print_unescaped_setting( 'tab_title', 'tabs', $index ); 569 ?></a> 570 </<?php Utils::print_validated_html_tag( $settings['title_html_tag'] ); ?>> 571 <div <?php $this->print_render_attribute_string( $tab_content_setting_key ); ?>><?php 572 $this->print_text_editor( $item['tab_content'] ); 573 ?></div> 574 </div> 575 <?php endforeach; ?> 576 <?php 577 if ( isset( $settings['faq_schema'] ) && 'yes' === $settings['faq_schema'] ) { 578 $json = [ 579 '@context' => 'https://schema.org', 580 '@type' => 'FAQPage', 581 'mainEntity' => [], 582 ]; 583 584 foreach ( $settings['tabs'] as $index => $item ) { 585 $json['mainEntity'][] = [ 586 '@type' => 'Question', 587 'name' => wp_strip_all_tags( $item['tab_title'] ), 588 'acceptedAnswer' => [ 589 '@type' => 'Answer', 590 'text' => $this->parse_text_editor( $item['tab_content'] ), 591 ], 592 ]; 593 } 594 ?> 595 <script type="application/ld+json"><?php echo wp_json_encode( $json ); ?></script> 596 <?php } ?> 597 </div> 598 <?php 599 } 600 601 /** 602 * Render accordion widget output in the editor. 603 * 604 * Written as a Backbone JavaScript template and used to generate the live preview. 605 * 606 * @since 2.9.0 607 * @access protected 608 */ 609 protected function content_template() { 610 ?> 611 <div class="elementor-accordion" role="tablist"> 612 <# 613 if ( settings.tabs ) { 614 var tabindex = view.getIDInt().toString().substr( 0, 3 ), 615 iconHTML = elementor.helpers.renderIcon( view, settings.selected_icon, {}, 'i' , 'object' ), 616 iconActiveHTML = elementor.helpers.renderIcon( view, settings.selected_active_icon, {}, 'i' , 'object' ), 617 migrated = elementor.helpers.isIconMigrated( settings, 'selected_icon' ); 618 619 _.each( settings.tabs, function( item, index ) { 620 var tabCount = index + 1, 621 tabTitleKey = view.getRepeaterSettingKey( 'tab_title', 'tabs', index ), 622 tabContentKey = view.getRepeaterSettingKey( 'tab_content', 'tabs', index ); 623 624 view.addRenderAttribute( tabTitleKey, { 625 'id': 'elementor-tab-title-' + tabindex + tabCount, 626 'class': [ 'elementor-tab-title' ], 627 'tabindex': tabindex + tabCount, 628 'data-tab': tabCount, 629 'role': 'tab', 630 'aria-controls': 'elementor-tab-content-' + tabindex + tabCount, 631 'aria-expanded': 'false', 632 } ); 633 634 view.addRenderAttribute( tabContentKey, { 635 'id': 'elementor-tab-content-' + tabindex + tabCount, 636 'class': [ 'elementor-tab-content', 'elementor-clearfix' ], 637 'data-tab': tabCount, 638 'role': 'tabpanel', 639 'aria-labelledby': 'elementor-tab-title-' + tabindex + tabCount 640 } ); 641 642 view.addInlineEditingAttributes( tabContentKey, 'advanced' ); 643 644 var titleHTMLTag = elementor.helpers.validateHTMLTag( settings.title_html_tag ); 645 #> 646 <div class="elementor-accordion-item"> 647 <{{{ titleHTMLTag }}} {{{ view.getRenderAttributeString( tabTitleKey ) }}}> 648 <# if ( settings.icon || settings.selected_icon ) { #> 649 <span class="elementor-accordion-icon elementor-accordion-icon-{{ settings.icon_align }}" aria-hidden="true"> 650 <# if ( iconHTML && iconHTML.rendered && ( ! settings.icon || migrated ) ) { #> 651 <span class="elementor-accordion-icon-closed">{{{ iconHTML.value }}}</span> 652 <span class="elementor-accordion-icon-opened">{{{ iconActiveHTML.value }}}</span> 653 <# } else { #> 654 <i class="elementor-accordion-icon-closed {{ settings.icon }}"></i> 655 <i class="elementor-accordion-icon-opened {{ settings.icon_active }}"></i> 656 <# } #> 657 </span> 658 <# } #> 659 <a class="elementor-accordion-title" href="">{{{ item.tab_title }}}</a> 660 </{{{ titleHTMLTag }}}> 661 <div {{{ view.getRenderAttributeString( tabContentKey ) }}}>{{{ item.tab_content }}}</div> 662 </div> 663 <# 664 } ); 665 } #> 666 </div> 667 <?php 668 } 669 }