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