56 lines
		
	
	
	
		
			1.6 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			56 lines
		
	
	
	
		
			1.6 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import React, { useCallback, useState } from 'react';
 | 
						|
import ShortNumber from './short_number';
 | 
						|
import { TransitionMotion, spring } from 'react-motion';
 | 
						|
import { reduceMotion } from '../initial_state';
 | 
						|
 | 
						|
const obfuscatedCount = (count: number) => {
 | 
						|
  if (count < 0) {
 | 
						|
    return 0;
 | 
						|
  } else if (count <= 1) {
 | 
						|
    return count;
 | 
						|
  } else {
 | 
						|
    return '1+';
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
type Props = {
 | 
						|
  value: number;
 | 
						|
  obfuscate?: boolean;
 | 
						|
}
 | 
						|
export const AnimatedNumber: React.FC<Props> = ({
 | 
						|
  value,
 | 
						|
  obfuscate,
 | 
						|
})=> {
 | 
						|
  const [previousValue, setPreviousValue] = useState(value);
 | 
						|
  const [direction, setDirection] = useState<1|-1>(1);
 | 
						|
 | 
						|
  if (previousValue !== value) {
 | 
						|
    setPreviousValue(value);
 | 
						|
    setDirection(value > previousValue ? 1 : -1);
 | 
						|
  }
 | 
						|
 | 
						|
  const willEnter = useCallback(() => ({ y: -1 * direction }), [direction]);
 | 
						|
  const willLeave = useCallback(() => ({ y: spring(1 * direction, { damping: 35, stiffness: 400 }) }), [direction]);
 | 
						|
 | 
						|
  if (reduceMotion) {
 | 
						|
    return obfuscate ? <>{obfuscatedCount(value)}</> : <ShortNumber value={value} />;
 | 
						|
  }
 | 
						|
 | 
						|
  const styles = [{
 | 
						|
    key: `${value}`,
 | 
						|
    data: value,
 | 
						|
    style: { y: spring(0, { damping: 35, stiffness: 400 }) },
 | 
						|
  }];
 | 
						|
 | 
						|
  return (
 | 
						|
    <TransitionMotion styles={styles} willEnter={willEnter} willLeave={willLeave}>
 | 
						|
      {items => (
 | 
						|
        <span className='animated-number'>
 | 
						|
          {items.map(({ key, data, style }) => (
 | 
						|
            <span key={key} style={{ position: (direction * style.y) > 0 ? 'absolute' : 'static', transform: `translateY(${style.y * 100}%)` }}>{obfuscate ? obfuscatedCount(data) : <ShortNumber value={data} />}</span>
 | 
						|
          ))}
 | 
						|
        </span>
 | 
						|
      )}
 | 
						|
    </TransitionMotion>
 | 
						|
  );
 | 
						|
};
 |