Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions components/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export { default as Portal } from './portal/index'
export { default as Progress } from './progress/index'
export { default as Provider } from './provider/index'
export { default as Radio } from './radio/index'
export { default as Rate } from './rate/index'
export { default as Result } from './result/index'
export { default as SearchBar } from './search-bar/index'
export { default as Slider } from './slider/index'
Expand Down
51 changes: 51 additions & 0 deletions components/rate/AnimatedIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from 'react'
import { Animated } from 'react-native'

import { AnimatedIconProps } from './PropsType'
import { defaultAnimationConfig } from './constants'

const AnimatedIcon: React.FC<AnimatedIconProps> = ({
active,
config,
children,
style,
}) => {
const {
scale = defaultAnimationConfig.scale,
easing = defaultAnimationConfig.easing,
duration = defaultAnimationConfig.duration,
} = config

const animatedSize = React.useRef(new Animated.Value(active ? scale : 1))

React.useEffect(() => {
const animation = Animated.timing(animatedSize.current, {
toValue: active ? scale : 1,
useNativeDriver: true,
easing,
duration,
})

animation.start()
return animation.stop
}, [active, scale, easing, duration])

return (
<Animated.View
pointerEvents="none"
style={[
style,
{
transform: [
{
scale: animatedSize.current,
},
],
},
]}>
{children}
</Animated.View>
)
}

export default AnimatedIcon
58 changes: 58 additions & 0 deletions components/rate/PropsType.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import {
FillGlyphMapType,
OutlineGlyphMapType,
} from '@ant-design/icons-react-native'
import React from 'react'
import { StyleProp, ViewStyle } from 'react-native'

type IconName = FillGlyphMapType & OutlineGlyphMapType

export interface RateProps {
value?: number
defaultValue?: number
count?: number
readOnly?: boolean
allowClear?: boolean
allowHalf?: boolean
allowSwiping?: boolean
style?: ViewStyle
color?: string
emptyColor?: string
iconName?: IconName
iconFill?: boolean
iconType?: 'fill' | 'outline'
iconSize?: number
iconStyle?: ViewStyle
animationConfig?: boolean | AnimationConfig
onChange?: (value: number) => void
onRatingStart?: (value: number) => void
onRatingEnd?: (value: number) => void
}

export type RateIconProps = {
size: number
name: IconName
color?: string
emptyColor?: string
type: 'full' | 'half' | 'empty'
isFill?: boolean
}

export type AnimationConfig = {
easing?: (value: number) => number
duration?: number
delay?: number
scale?: number
}

export type AnimationOptions = {
need: boolean
config: Required<AnimationConfig>
}

export type AnimatedIconProps = {
active: boolean
children: React.ReactElement
config: AnimationConfig
style?: StyleProp<ViewStyle>
}
116 changes: 116 additions & 0 deletions components/rate/RateIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { IconFill, IconOutline } from '@ant-design/icons-react-native'
import React from 'react'
import { I18nManager, StyleSheet, View, ViewStyle } from 'react-native'
import { RateIconProps } from './PropsType'

const Icon = ({
size,
name,
color,
emptyColor,
isFill,
}: Omit<RateIconProps, 'type'>) => {
const IconComponent: any = isFill ? IconFill : IconOutline
return (
<IconComponent
size={size}
color={color}
name={name}
emptyColor={emptyColor}
/>
)
}

const EmptyIcon = ({
size,
name = 'star',
emptyColor,
isFill,
}: Omit<RateIconProps, 'type' | 'color'>) => (
<Icon name={name} size={size} color={emptyColor} isFill={isFill} />
)

const FullIcon = ({
size,
color,
name = 'star',
isFill,
}: Omit<RateIconProps, 'type' | 'emptyColor'>) => (
<Icon name={name} size={size} color={color} isFill={isFill} />
)

const RTL_TRANSFORM: ViewStyle = {
transform: [{ rotateY: '180deg' }],
}

const HalfIcon = ({
size,
name = 'star',
color,
emptyColor,
isFill,
}: Omit<RateIconProps, 'type'>) => (
<View
style={[
styles.container,
{ width: size, height: size },
I18nManager.isRTL ? RTL_TRANSFORM : undefined,
]}>
<View
style={[
styles.half,
{
width: size / 2,
height: size,
},
]}>
<Icon name={name} size={size} color={color} isFill={isFill} />
</View>

<View
style={[
styles.half,
{
width: size / 2,
height: size,
},
RTL_TRANSFORM,
]}>
<Icon name={name} size={size} color={emptyColor} isFill={isFill} />
</View>
</View>
)

const RateIcon = ({
type,
name = 'star',
size,
color,
emptyColor,
isFill,
}: RateIconProps) => {
const Component =
type === 'full' ? FullIcon : type === 'half' ? HalfIcon : EmptyIcon
return (
<Component
name={name}
size={size}
color={color}
emptyColor={emptyColor}
isFill={isFill}
/>
)
}

const styles = StyleSheet.create({
container: {
flexDirection: 'row',
position: 'relative',
},
half: {
overflow: 'hidden',
position: 'relative',
},
})

export default RateIcon
Loading