UNPKG

react-native-star-rating-widget

Version:
195 lines (194 loc) 6.98 kB
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } import React from 'react'; import { PanResponder, StyleSheet, View, Animated, Easing, I18nManager, AccessibilityInfo } from 'react-native'; import StarIcon from './StarIcon'; import { getStars } from './utils'; const defaultColor = '#fdd835'; const defaultAnimationConfig = { easing: Easing.elastic(2), duration: 300, scale: 1.2, delay: 300 }; const StarRating = _ref => { let { rating, maxStars = 5, starSize = 32, onChange, color = defaultColor, emptyColor = color, enableHalfStar = true, enableSwiping = true, onRatingStart, onRatingEnd, animationConfig = defaultAnimationConfig, style, starStyle, StarIconComponent = StarIcon, testID, accessibilityLabel = 'star rating. %value% stars. use custom actions to set rating.', accessabilityIncrementLabel = 'increment', accessabilityDecrementLabel = 'decrement', accessabilityActivateLabel = 'activate (default)', accessibilityAdjustmentLabel = '%value% stars' } = _ref; const width = React.useRef(); const [isInteracting, setInteracting] = React.useState(false); const [stagedRating, setStagedRating] = React.useState(rating); const panResponder = React.useMemo(() => { const calculateRating = function (x) { let isRTL = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : I18nManager.isRTL; if (!width.current) return rating; if (isRTL) { return calculateRating(width.current - x, false); } const newRating = Math.max(0, Math.min(Math.round(x / width.current * maxStars * 2 + 0.2) / 2, maxStars)); return enableHalfStar ? newRating : Math.ceil(newRating); }; const handleChange = newRating => { if (newRating !== rating) { onChange(newRating); } }; return PanResponder.create({ onStartShouldSetPanResponder: () => true, onStartShouldSetPanResponderCapture: () => true, onMoveShouldSetPanResponder: () => true, onMoveShouldSetPanResponderCapture: () => true, onPanResponderMove: e => { if (enableSwiping) { const newRating = calculateRating(e.nativeEvent.locationX); handleChange(newRating); } }, onPanResponderStart: e => { const newRating = calculateRating(e.nativeEvent.locationX); onRatingStart === null || onRatingStart === void 0 ? void 0 : onRatingStart(newRating); handleChange(newRating); setInteracting(true); }, onPanResponderEnd: e => { const newRating = calculateRating(e.nativeEvent.locationX); handleChange(newRating); onRatingEnd === null || onRatingEnd === void 0 ? void 0 : onRatingEnd(newRating); setTimeout(() => { setInteracting(false); }, animationConfig.delay || defaultAnimationConfig.delay); }, onPanResponderTerminate: () => { // called when user drags outside of the component setTimeout(() => { setInteracting(false); }, animationConfig.delay || defaultAnimationConfig.delay); } }); }, [rating, maxStars, enableHalfStar, onChange, enableSwiping, onRatingStart, onRatingEnd, animationConfig.delay]); return /*#__PURE__*/React.createElement(View, { style: style }, /*#__PURE__*/React.createElement(View, _extends({ style: styles.starRating }, panResponder.panHandlers, { onLayout: e => { width.current = e.nativeEvent.layout.width; }, testID: testID, accessible: true, accessibilityRole: "adjustable", accessibilityLabel: accessibilityLabel.replace(/%value%/g, stagedRating.toString()), accessibilityValue: { min: 0, max: enableHalfStar ? maxStars * 2 : maxStars, now: enableHalfStar ? rating * 2 : rating // this has to be an integer }, accessibilityActions: [{ name: 'increment', label: accessabilityIncrementLabel }, { name: 'decrement', label: accessabilityDecrementLabel }, { name: 'activate', label: accessabilityActivateLabel }], onAccessibilityAction: event => { const incrementor = enableHalfStar ? 0.5 : 1; switch (event.nativeEvent.actionName) { case 'increment': if (stagedRating >= maxStars) { AccessibilityInfo.announceForAccessibility(accessibilityAdjustmentLabel.replace(/%value%/g, `${maxStars}`)); } else { AccessibilityInfo.announceForAccessibility(accessibilityAdjustmentLabel.replace(/%value%/g, `${stagedRating + incrementor}`)); setStagedRating(stagedRating + incrementor); } break; case 'decrement': if (stagedRating <= 0) { AccessibilityInfo.announceForAccessibility(accessibilityAdjustmentLabel.replace(/%value%/g, `${0}`)); } else { AccessibilityInfo.announceForAccessibility(accessibilityAdjustmentLabel.replace(/%value%/g, `${stagedRating - incrementor}`)); setStagedRating(stagedRating - incrementor); } break; case 'activate': onChange(stagedRating); break; } } }), getStars(rating, maxStars).map((starType, i) => { return /*#__PURE__*/React.createElement(AnimatedIcon, { key: i, active: isInteracting && rating - i >= 0.5, animationConfig: animationConfig, style: starStyle }, /*#__PURE__*/React.createElement(StarIconComponent, { index: i, type: starType, size: starSize, color: starType === 'empty' ? emptyColor : color })); }))); }; const AnimatedIcon = _ref2 => { let { active, animationConfig, children, style } = _ref2; const { scale = defaultAnimationConfig.scale, easing = defaultAnimationConfig.easing, duration = defaultAnimationConfig.duration } = animationConfig; 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 /*#__PURE__*/React.createElement(Animated.View, { pointerEvents: "none", style: [styles.star, style, { transform: [{ scale: animatedSize.current }] }] }, children); }; const styles = StyleSheet.create({ starRating: { flexDirection: 'row', alignSelf: 'flex-start' }, star: { marginHorizontal: 5 } }); export default StarRating; //# sourceMappingURL=StarRating.js.map