star-rating.php (12145B)
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 star rating widget. 13 * 14 * Elementor widget that displays star rating. 15 * 16 * @since 2.3.0 17 */ 18 class Widget_Star_Rating extends Widget_Base { 19 20 /** 21 * Get widget name. 22 * 23 * Retrieve star rating widget name. 24 * 25 * @since 2.3.0 26 * @access public 27 * 28 * @return string Widget name. 29 */ 30 public function get_name() { 31 return 'star-rating'; 32 } 33 34 /** 35 * Get widget title. 36 * 37 * Retrieve star rating widget title. 38 * 39 * @since 2.3.0 40 * @access public 41 * 42 * @return string Widget title. 43 */ 44 public function get_title() { 45 return esc_html__( 'Star Rating', 'elementor' ); 46 } 47 48 /** 49 * Get widget icon. 50 * 51 * Retrieve star rating widget icon. 52 * 53 * @since 2.3.0 54 * @access public 55 * 56 * @return string Widget icon. 57 */ 58 public function get_icon() { 59 return 'eicon-rating'; 60 } 61 62 /** 63 * Get widget keywords. 64 * 65 * Retrieve the list of keywords the widget belongs to. 66 * 67 * @since 2.3.0 68 * @access public 69 * 70 * @return array Widget keywords. 71 */ 72 public function get_keywords() { 73 return [ 'star', 'rating', 'rate', 'review' ]; 74 } 75 76 /** 77 * Register star rating 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_rating', 87 [ 88 'label' => esc_html__( 'Rating', 'elementor' ), 89 ] 90 ); 91 92 $this->add_control( 93 'rating_scale', 94 [ 95 'label' => esc_html__( 'Rating Scale', 'elementor' ), 96 'type' => Controls_Manager::SELECT, 97 'options' => [ 98 '5' => '0-5', 99 '10' => '0-10', 100 ], 101 'default' => '5', 102 ] 103 ); 104 105 $this->add_control( 106 'rating', 107 [ 108 'label' => esc_html__( 'Rating', 'elementor' ), 109 'type' => Controls_Manager::NUMBER, 110 'min' => 0, 111 'max' => 10, 112 'step' => 0.1, 113 'default' => 5, 114 'dynamic' => [ 115 'active' => true, 116 ], 117 ] 118 ); 119 120 $this->add_control( 121 'star_style', 122 [ 123 'label' => esc_html__( 'Icon', 'elementor' ), 124 'type' => Controls_Manager::SELECT, 125 'options' => [ 126 'star_fontawesome' => 'Font Awesome', 127 'star_unicode' => 'Unicode', 128 ], 129 'default' => 'star_fontawesome', 130 'render_type' => 'template', 131 'prefix_class' => 'elementor--star-style-', 132 'separator' => 'before', 133 ] 134 ); 135 136 $this->add_control( 137 'unmarked_star_style', 138 [ 139 'label' => esc_html__( 'Unmarked Style', 'elementor' ), 140 'type' => Controls_Manager::CHOOSE, 141 'options' => [ 142 'solid' => [ 143 'title' => esc_html__( 'Solid', 'elementor' ), 144 'icon' => 'eicon-star', 145 ], 146 'outline' => [ 147 'title' => esc_html__( 'Outline', 'elementor' ), 148 'icon' => 'eicon-star-o', 149 ], 150 ], 151 'default' => 'solid', 152 ] 153 ); 154 155 $this->add_control( 156 'title', 157 [ 158 'label' => esc_html__( 'Title', 'elementor' ), 159 'type' => Controls_Manager::TEXT, 160 'separator' => 'before', 161 'dynamic' => [ 162 'active' => true, 163 ], 164 ] 165 ); 166 167 $this->add_responsive_control( 168 'align', 169 [ 170 'label' => esc_html__( 'Alignment', 'elementor' ), 171 'type' => Controls_Manager::CHOOSE, 172 'options' => [ 173 'left' => [ 174 'title' => esc_html__( 'Left', 'elementor' ), 175 'icon' => 'eicon-text-align-left', 176 ], 177 'center' => [ 178 'title' => esc_html__( 'Center', 'elementor' ), 179 'icon' => 'eicon-text-align-center', 180 ], 181 'right' => [ 182 'title' => esc_html__( 'Right', 'elementor' ), 183 'icon' => 'eicon-text-align-right', 184 ], 185 'justify' => [ 186 'title' => esc_html__( 'Justified', 'elementor' ), 187 'icon' => 'eicon-text-align-justify', 188 ], 189 ], 190 'prefix_class' => 'elementor-star-rating%s--align-', 191 'selectors' => [ 192 '{{WRAPPER}}' => 'text-align: {{VALUE}}', 193 ], 194 ] 195 ); 196 197 $this->end_controls_section(); 198 199 $this->start_controls_section( 200 'section_title_style', 201 [ 202 'label' => esc_html__( 'Title', 'elementor' ), 203 'tab' => Controls_Manager::TAB_STYLE, 204 'condition' => [ 205 'title!' => '', 206 ], 207 ] 208 ); 209 210 $this->add_control( 211 'title_color', 212 [ 213 'label' => esc_html__( 'Text Color', 'elementor' ), 214 'type' => Controls_Manager::COLOR, 215 'global' => [ 216 'default' => Global_Colors::COLOR_TEXT, 217 ], 218 'selectors' => [ 219 '{{WRAPPER}} .elementor-star-rating__title' => 'color: {{VALUE}}', 220 ], 221 ] 222 ); 223 224 $this->add_group_control( 225 Group_Control_Typography::get_type(), 226 [ 227 'name' => 'title_typography', 228 'selector' => '{{WRAPPER}} .elementor-star-rating__title', 229 'global' => [ 230 'default' => Global_Typography::TYPOGRAPHY_TEXT, 231 ], 232 ] 233 ); 234 235 $this->add_group_control( 236 Group_Control_Text_Shadow::get_type(), 237 [ 238 'name' => 'title_shadow', 239 'selector' => '{{WRAPPER}} .elementor-star-rating__title', 240 ] 241 ); 242 243 $this->add_responsive_control( 244 'title_gap', 245 [ 246 'label' => esc_html__( 'Gap', 'elementor' ), 247 'type' => Controls_Manager::SLIDER, 248 'range' => [ 249 'px' => [ 250 'min' => 0, 251 'max' => 50, 252 ], 253 ], 254 'selectors' => [ 255 'body:not(.rtl) {{WRAPPER}}:not(.elementor-star-rating--align-justify) .elementor-star-rating__title' => 'margin-right: {{SIZE}}{{UNIT}}', 256 'body.rtl {{WRAPPER}}:not(.elementor-star-rating--align-justify) .elementor-star-rating__title' => 'margin-left: {{SIZE}}{{UNIT}}', 257 ], 258 ] 259 ); 260 261 $this->end_controls_section(); 262 263 $this->start_controls_section( 264 'section_stars_style', 265 [ 266 'label' => esc_html__( 'Stars', 'elementor' ), 267 'tab' => Controls_Manager::TAB_STYLE, 268 ] 269 ); 270 271 $this->add_responsive_control( 272 'icon_size', 273 [ 274 'label' => esc_html__( 'Size', 'elementor' ), 275 'type' => Controls_Manager::SLIDER, 276 'range' => [ 277 'px' => [ 278 'min' => 0, 279 'max' => 100, 280 ], 281 ], 282 'selectors' => [ 283 '{{WRAPPER}} .elementor-star-rating' => 'font-size: {{SIZE}}{{UNIT}}', 284 ], 285 ] 286 ); 287 288 $this->add_responsive_control( 289 'icon_space', 290 [ 291 'label' => esc_html__( 'Spacing', 'elementor' ), 292 'type' => Controls_Manager::SLIDER, 293 'range' => [ 294 'px' => [ 295 'min' => 0, 296 'max' => 50, 297 ], 298 ], 299 'selectors' => [ 300 'body:not(.rtl) {{WRAPPER}} .elementor-star-rating i:not(:last-of-type)' => 'margin-right: {{SIZE}}{{UNIT}}', 301 'body.rtl {{WRAPPER}} .elementor-star-rating i:not(:last-of-type)' => 'margin-left: {{SIZE}}{{UNIT}}', 302 ], 303 ] 304 ); 305 306 $this->add_control( 307 'stars_color', 308 [ 309 'label' => esc_html__( 'Color', 'elementor' ), 310 'type' => Controls_Manager::COLOR, 311 'selectors' => [ 312 '{{WRAPPER}} .elementor-star-rating i:before' => 'color: {{VALUE}}', 313 ], 314 'separator' => 'before', 315 ] 316 ); 317 318 $this->add_control( 319 'stars_unmarked_color', 320 [ 321 'label' => esc_html__( 'Unmarked Color', 'elementor' ), 322 'type' => Controls_Manager::COLOR, 323 'selectors' => [ 324 '{{WRAPPER}} .elementor-star-rating i' => 'color: {{VALUE}}', 325 ], 326 ] 327 ); 328 329 $this->end_controls_section(); 330 } 331 332 /** 333 * @since 2.3.0 334 * @access protected 335 */ 336 protected function get_rating() { 337 $settings = $this->get_settings_for_display(); 338 $rating_scale = (int) $settings['rating_scale']; 339 $rating = (float) $settings['rating'] > $rating_scale ? $rating_scale : $settings['rating']; 340 341 return [ $rating, $rating_scale ]; 342 } 343 344 /** 345 * Print the actual stars and calculate their filling. 346 * 347 * Rating type is float to allow stars-count to be a fraction. 348 * Floored-rating type is int, to represent the rounded-down stars count. 349 * In the `for` loop, the index type is float to allow comparing with the rating value. 350 * 351 * @since 2.3.0 352 * @access protected 353 */ 354 protected function render_stars( $icon ) { 355 $rating_data = $this->get_rating(); 356 $rating = (float) $rating_data[0]; 357 $floored_rating = floor( $rating ); 358 $stars_html = ''; 359 360 for ( $stars = 1.0; $stars <= $rating_data[1]; $stars++ ) { 361 if ( $stars <= $floored_rating ) { 362 $stars_html .= '<i class="elementor-star-full">' . $icon . '</i>'; 363 } elseif ( $floored_rating + 1 === $stars && $rating !== $floored_rating ) { 364 $stars_html .= '<i class="elementor-star-' . ( $rating - $floored_rating ) * 10 . '">' . $icon . '</i>'; 365 } else { 366 $stars_html .= '<i class="elementor-star-empty">' . $icon . '</i>'; 367 } 368 } 369 370 return $stars_html; 371 } 372 373 /** 374 * @since 2.3.0 375 * @access protected 376 */ 377 protected function render() { 378 $settings = $this->get_settings_for_display(); 379 $rating_data = $this->get_rating(); 380 $textual_rating = $rating_data[0] . '/' . $rating_data[1]; 381 $icon = ''; 382 383 if ( 'star_fontawesome' === $settings['star_style'] ) { 384 if ( 'outline' === $settings['unmarked_star_style'] ) { 385 $icon = ''; 386 } 387 } elseif ( 'star_unicode' === $settings['star_style'] ) { 388 $icon = '★'; 389 390 if ( 'outline' === $settings['unmarked_star_style'] ) { 391 $icon = '☆'; 392 } 393 } 394 395 $this->add_render_attribute( 'icon_wrapper', [ 396 'class' => 'elementor-star-rating', 397 'title' => $textual_rating, 398 'itemtype' => 'http://schema.org/Rating', 399 'itemscope' => '', 400 'itemprop' => 'reviewRating', 401 ] ); 402 403 $schema_rating = '<span itemprop="ratingValue" class="elementor-screen-only">' . $textual_rating . '</span>'; 404 $stars_element = '<div ' . $this->get_render_attribute_string( 'icon_wrapper' ) . '>' . $this->render_stars( $icon ) . ' ' . $schema_rating . '</div>'; 405 ?> 406 407 <div class="elementor-star-rating__wrapper"> 408 <?php if ( ! Utils::is_empty( $settings['title'] ) ) : ?> 409 <div class="elementor-star-rating__title"><?php echo esc_html( $settings['title'] ); ?></div> 410 <?php endif; 411 // PHPCS - $stars_element contains an HTML string that cannot be escaped. ?> 412 <?php echo $stars_element; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> 413 </div> 414 <?php 415 } 416 417 /** 418 * @since 2.9.0 419 * @access protected 420 */ 421 protected function content_template() { 422 ?> 423 <# 424 var getRating = function() { 425 var ratingScale = parseInt( settings.rating_scale, 10 ), 426 rating = settings.rating > ratingScale ? ratingScale : settings.rating; 427 428 return [ rating, ratingScale ]; 429 }, 430 ratingData = getRating(), 431 rating = ratingData[0], 432 textualRating = ratingData[0] + '/' + ratingData[1], 433 renderStars = function( icon ) { 434 var starsHtml = '', 435 flooredRating = Math.floor( rating ); 436 437 for ( var stars = 1; stars <= ratingData[1]; stars++ ) { 438 if ( stars <= flooredRating ) { 439 starsHtml += '<i class="elementor-star-full">' + icon + '</i>'; 440 } else if ( flooredRating + 1 === stars && rating !== flooredRating ) { 441 starsHtml += '<i class="elementor-star-' + ( rating - flooredRating ).toFixed( 1 ) * 10 + '">' + icon + '</i>'; 442 } else { 443 starsHtml += '<i class="elementor-star-empty">' + icon + '</i>'; 444 } 445 } 446 447 return starsHtml; 448 }, 449 icon = ''; 450 451 if ( 'star_fontawesome' === settings.star_style ) { 452 if ( 'outline' === settings.unmarked_star_style ) { 453 icon = ''; 454 } 455 } else if ( 'star_unicode' === settings.star_style ) { 456 icon = '★'; 457 458 if ( 'outline' === settings.unmarked_star_style ) { 459 icon = '☆'; 460 } 461 } 462 463 view.addRenderAttribute( 'iconWrapper', 'class', 'elementor-star-rating' ); 464 view.addRenderAttribute( 'iconWrapper', 'itemtype', 'http://schema.org/Rating' ); 465 view.addRenderAttribute( 'iconWrapper', 'title', textualRating ); 466 view.addRenderAttribute( 'iconWrapper', 'itemscope', '' ); 467 view.addRenderAttribute( 'iconWrapper', 'itemprop', 'reviewRating' ); 468 469 var stars = renderStars( icon ); 470 #> 471 472 <div class="elementor-star-rating__wrapper"> 473 <# if ( ! _.isEmpty( settings.title ) ) { #> 474 <div class="elementor-star-rating__title">{{ settings.title }}</div> 475 <# } #> 476 <div {{{ view.getRenderAttributeString( 'iconWrapper' ) }}} > 477 {{{ stars }}} 478 <span itemprop="ratingValue" class="elementor-screen-only">{{ textualRating }}</span> 479 </div> 480 </div> 481 482 <?php 483 } 484 }