1+ import React , { useState } from 'react' ;
2+
3+ const styles = {
4+ container : {
5+ display : 'inline-flex' ,
6+ fontSize : '32px' ,
7+ cursor : 'pointer' ,
8+ outline : 'none' ,
9+ } ,
10+ starWrapper : {
11+ position : 'relative' ,
12+ color : '#ddd' ,
13+ } ,
14+ starOverlay : {
15+ position : 'absolute' ,
16+ top : 0 ,
17+ left : 0 ,
18+ overflow : 'hidden' ,
19+ color : '#ffc107' ,
20+ pointerEvents : 'none' ,
21+ width : '0%' ,
22+ transition : 'width 0.2s'
23+ }
24+ } ;
25+
26+ const StarRating = ( { max = 5 , initialValue = 0 , onChange } ) => {
27+ const [ rating , setRating ] = useState ( initialValue ) ;
28+ const [ hover , setHover ] = useState ( null ) ;
29+ console . log ( rating , hover )
30+ const displayValue = hover !== null ? hover : rating ;
31+
32+ const handleMouseMove = ( e , index ) => {
33+ const { left, width } = e . currentTarget . getBoundingClientRect ( ) ;
34+ const percent = ( e . clientX - left ) / width ;
35+
36+ const newValue = index + ( percent < 0.5 ? 0.5 : 1 ) ;
37+ setHover ( newValue ) ;
38+ } ;
39+
40+ const handleClick = ( ) => {
41+ const newValue = hover === rating ? 0 : hover ;
42+
43+ setRating ( newValue ) ;
44+ if ( onChange ) onChange ( newValue ) ;
45+ } ;
46+
47+ const handleKeyDown = ( e ) => {
48+ let newValue = rating ;
49+ if ( e . key === 'ArrowRight' || e . key === 'ArrowUp' ) {
50+ newValue = Math . min ( max , rating + 0.5 ) ;
51+ e . preventDefault ( ) ;
52+ } else if ( e . key === 'ArrowLeft' || e . key === 'ArrowDown' ) {
53+ newValue = Math . max ( 0 , rating - 0.5 ) ;
54+ e . preventDefault ( ) ;
55+ }
56+
57+ if ( newValue !== rating ) {
58+ setRating ( newValue ) ;
59+ if ( onChange ) onChange ( newValue ) ;
60+ }
61+ } ;
62+
63+ return (
64+ < div
65+ style = { styles . container }
66+ role = "slider"
67+ tabIndex = { 0 }
68+ aria-valuemin = { 0 }
69+ aria-valuemax = { max }
70+ aria-valuenow = { rating }
71+ aria-label = "Star Rating"
72+ onKeyDown = { handleKeyDown }
73+ onMouseLeave = { ( ) => setHover ( null ) }
74+ >
75+ { [ ...Array ( max ) ] . map ( ( _ , i ) => {
76+ let width = '0%' ;
77+ if ( displayValue > i ) {
78+ const val = displayValue - i ;
79+ width = val >= 1 ? '100%' : `${ val * 100 } %` ;
80+ }
81+
82+ return (
83+ < span
84+ key = { i }
85+ style = { styles . starWrapper }
86+ onMouseMove = { ( e ) => handleMouseMove ( e , i ) }
87+ onClick = { handleClick }
88+ >
89+ ★
90+ < span style = { { ...styles . starOverlay , width } } >
91+ ★
92+ </ span >
93+ </ span >
94+ ) ;
95+ } ) }
96+ </ div >
97+ ) ;
98+ } ;
99+
100+ export default StarRating ;
0 commit comments