11import React from 'react' ;
2+ import KeyCode from 'rc-util/lib/KeyCode' ;
3+ import classNames from 'classnames' ;
24
35export interface StarProps {
46 value ?: number ;
@@ -17,70 +19,86 @@ export interface StarProps {
1719 count ?: number ;
1820}
1921
20- export default class Star extends React . Component < StarProps > {
21- onHover : React . MouseEventHandler < HTMLDivElement > = e => {
22- const { onHover, index } = this . props ;
22+ function Star ( props : StarProps , ref : React . Ref < HTMLLIElement > ) {
23+ const {
24+ disabled,
25+ prefixCls,
26+ character,
27+ characterRender,
28+ index,
29+ count,
30+ value,
31+ allowHalf,
32+ focused,
33+ onHover,
34+ onClick,
35+ } = props ;
36+
37+ // =========================== Events ===========================
38+ const onInternalHover : React . MouseEventHandler < HTMLDivElement > = ( e ) => {
2339 onHover ( e , index ) ;
2440 } ;
2541
26- onClick = e => {
27- const { onClick, index } = this . props ;
42+ const onInternalClick : React . MouseEventHandler < HTMLDivElement > = ( e ) => {
2843 onClick ( e , index ) ;
2944 } ;
3045
31- onKeyDown : React . KeyboardEventHandler < HTMLDivElement > = e => {
32- const { onClick, index } = this . props ;
33- if ( e . keyCode === 13 ) {
46+ const onInternalKeyDown : React . KeyboardEventHandler < HTMLDivElement > = ( e ) => {
47+ if ( e . keyCode === KeyCode . ENTER ) {
3448 onClick ( e , index ) ;
3549 }
3650 } ;
3751
38- getClassName ( ) {
39- const { prefixCls, index, value, allowHalf, focused } = this . props ;
40- const starValue = index + 1 ;
41- let className = prefixCls ;
42- if ( value === 0 && index === 0 && focused ) {
43- className += ` ${ prefixCls } -focused` ;
44- } else if ( allowHalf && value + 0.5 >= starValue && value < starValue ) {
45- className += ` ${ prefixCls } -half ${ prefixCls } -active` ;
46- if ( focused ) {
47- className += ` ${ prefixCls } -focused` ;
48- }
52+ // =========================== Render ===========================
53+ // >>>>> ClassName
54+ const starValue = index + 1 ;
55+ const classNameList = new Set ( [ prefixCls ] ) ;
56+
57+ // TODO: Current we just refactor from CC to FC. This logic seems can be optimized.
58+ if ( value === 0 && index === 0 && focused ) {
59+ classNameList . add ( `${ prefixCls } -focused` ) ;
60+ } else if ( allowHalf && value + 0.5 >= starValue && value < starValue ) {
61+ classNameList . add ( `${ prefixCls } -half` ) ;
62+ classNameList . add ( `${ prefixCls } -active` ) ;
63+ if ( focused ) {
64+ classNameList . add ( `${ prefixCls } -focused` ) ;
65+ }
66+ } else {
67+ if ( starValue <= value ) {
68+ classNameList . add ( `${ prefixCls } -full` ) ;
4969 } else {
50- className += starValue <= value ? ` ${ prefixCls } -full` : ` ${ prefixCls } - zero`;
51- if ( starValue === value && focused ) {
52- className += ` ${ prefixCls } -focused` ;
53- }
70+ classNameList . add ( ` ${ prefixCls } -zero`) ;
71+ }
72+ if ( starValue === value && focused ) {
73+ classNameList . add ( ` ${ prefixCls } -focused` ) ;
5474 }
55- return className ;
5675 }
5776
58- render ( ) {
59- const { onHover, onClick, onKeyDown } = this ;
60- const { disabled, prefixCls, character, characterRender, index, count, value } = this . props ;
61- const characterNode = typeof character === 'function' ? character ( this . props ) : character ;
62- let start : React . ReactNode = (
63- < li className = { this . getClassName ( ) } >
64- < div
65- onClick = { disabled ? null : onClick }
66- onKeyDown = { disabled ? null : onKeyDown }
67- onMouseMove = { disabled ? null : onHover }
68- role = "radio"
69- aria-checked = { value > index ? 'true' : 'false' }
70- aria-posinset = { index + 1 }
71- aria-setsize = { count }
72- tabIndex = { disabled ? - 1 : 0 }
73- >
74- < div className = { `${ prefixCls } -first` } > { characterNode } </ div >
75- < div className = { `${ prefixCls } -second` } > { characterNode } </ div >
76- </ div >
77- </ li >
78- ) ;
77+ // >>>>> Node
78+ const characterNode = typeof character === 'function' ? character ( props ) : character ;
79+ let start : React . ReactNode = (
80+ < li className = { classNames ( Array . from ( classNameList ) ) } ref = { ref } >
81+ < div
82+ onClick = { disabled ? null : onInternalClick }
83+ onKeyDown = { disabled ? null : onInternalKeyDown }
84+ onMouseMove = { disabled ? null : onInternalHover }
85+ role = "radio"
86+ aria-checked = { value > index ? 'true' : 'false' }
87+ aria-posinset = { index + 1 }
88+ aria-setsize = { count }
89+ tabIndex = { disabled ? - 1 : 0 }
90+ >
91+ < div className = { `${ prefixCls } -first` } > { characterNode } </ div >
92+ < div className = { `${ prefixCls } -second` } > { characterNode } </ div >
93+ </ div >
94+ </ li >
95+ ) ;
7996
80- if ( characterRender ) {
81- start = characterRender ( start as React . ReactElement , this . props ) ;
82- }
83-
84- return start ;
97+ if ( characterRender ) {
98+ start = characterRender ( start as React . ReactElement , props ) ;
8599 }
100+
101+ return start as React . ReactElement ;
86102}
103+
104+ export default React . forwardRef ( Star ) ;
0 commit comments