diff --git a/showcase/package.json b/showcase/package.json index c15749d..3e12952 100644 --- a/showcase/package.json +++ b/showcase/package.json @@ -32,6 +32,7 @@ "dependencies": { "@reach/router": "^1.2.1", "mo-js": "^0.288.2", + "number-to-words": "^1.2.4", "prop-types": "^15.7.2", "react": "^16.10.2", "react-dom": "^16.10.2", diff --git a/showcase/src/patterns/index.css b/showcase/src/patterns/index.css index 75eed52..87537d4 100644 --- a/showcase/src/patterns/index.css +++ b/showcase/src/patterns/index.css @@ -78,4 +78,12 @@ fill: #27ae60; stroke: #fff; stroke-width: 1px; +} + +/* Clap Info */ +.info { + position: absolute; + left: -20px; + right: -20px; + bottom: -50px; } \ No newline at end of file diff --git a/showcase/src/patterns/index.js b/showcase/src/patterns/index.js index a4a5ae5..7bfd280 100644 --- a/showcase/src/patterns/index.js +++ b/showcase/src/patterns/index.js @@ -1,23 +1,40 @@ -import React, { Component, useState } from 'react' +import React, { + useState, + useCallback, + useLayoutEffect, + useContext, + useMemo, + createContext +} from 'react' + import mojs from 'mo-js' +import wordConverter from 'number-to-words' import { generateRandomNumber } from '../utils/generateRandomNumber' import styles from './index.css' /** ==================================== - * 🔰HOC -Higher Order Component for Animation + * 🔰Hook + Hook for Animation ==================================== **/ -const withClapAnimation = WrappedComponent => { - class WithClapAnimation extends Component { - state = { - animationTimeline: new mojs.Timeline() - } - componentDidMount () { - const tlDuration = 300 +const useClapAnimation = ({ + duration: tlDuration, + bounceEl, + fadeEl, + burstEl +}) => { + const [animationTimeline, setAnimationTimeline] = useState( + new mojs.Timeline() + ) + + useLayoutEffect( + () => { + if (!bounceEl || !fadeEl || !burstEl) { + return + } const triangleBurst = new mojs.Burst({ - parent: '#clap', + parent: burstEl, radius: { 50: 95 }, count: 5, angle: 30, @@ -36,7 +53,7 @@ const withClapAnimation = WrappedComponent => { }) const circleBurst = new mojs.Burst({ - parent: '#clap', + parent: burstEl, radius: { 50: 75 }, angle: 25, duration: tlDuration, @@ -51,7 +68,7 @@ const withClapAnimation = WrappedComponent => { }) const countAnimation = new mojs.Html({ - el: '#clapCount', + el: bounceEl, isShowStart: false, isShowEnd: true, y: { 0: -30 }, @@ -64,7 +81,7 @@ const withClapAnimation = WrappedComponent => { }) const countTotalAnimation = new mojs.Html({ - el: '#clapCountTotal', + el: fadeEl, isShowStart: false, isShowEnd: true, opacity: { 0: 1 }, @@ -74,44 +91,35 @@ const withClapAnimation = WrappedComponent => { }) const scaleButton = new mojs.Html({ - el: '#clap', + el: burstEl, duration: tlDuration, scale: { 1.3: 1 }, easing: mojs.easing.out }) - const clap = document.getElementById('clap') - clap.style.transform = 'scale(1, 1)' - this.state.animationTimeline.add([ + if (typeof burstEl === 'string') { + clap.style.transform = 'scale(1, 1)' + const el = document.getElementById(id) + el.style.transform = 'scale(1, 1)' + } else { + burstEl.style.transform = 'scale(1, 1)' + } + + const updatedAnimationTimeline = animationTimeline.add([ countAnimation, countTotalAnimation, scaleButton, circleBurst, triangleBurst ]) - } - - render () { - return ( - - ) - } - } - WithClapAnimation.displayName = `WithClapAnimation(${getDisplayName( - WrappedComponent - )})` - - return WithClapAnimation -} + setAnimationTimeline(updatedAnimationTimeline) + }, + [tlDuration, animationTimeline, bounceEl, fadeEl, burstEl] + ) -function getDisplayName (WrappedComponent) { - return WrappedComponent.displayName || WrappedComponent.name || 'Component' + return animationTimeline } - /** ==================================== * 🔰 MediumClap ==================================== **/ @@ -121,13 +129,33 @@ const initialState = { isClicked: false } -const MediumClap = ({ animationTimeline }) => { +const MediumClapContext = createContext() +const { Provider } = MediumClapContext + +const MediumClap = ({ children }) => { const MAXIMUM_USER_CLAP = 50 const [clapState, setClapState] = useState(initialState) const { count, countTotal, isClicked } = clapState + const [{ clapRef, clapCountRef, clapTotalRef }, setRefState] = useState({}) + + const setRef = useCallback(node => { + if (node !== null) { + setRefState(prevRefState => ({ + ...prevRefState, + [node.dataset.refkey]: node + })) + } + }, []) + + const animationTimeline = useClapAnimation({ + duration: 300, + bounceEl: clapCountRef, + fadeEl: clapTotalRef, + burstEl: clapRef + }) + const handleClapClick = () => { - // 👉 prop from HOC animationTimeline.replay() setClapState({ @@ -137,12 +165,27 @@ const MediumClap = ({ animationTimeline }) => { }) } + const memoizedValue = useMemo( + () => ({ + count, + countTotal, + isClicked, + setRef + }), + [count, countTotal, isClicked, setRef] + ) + return ( - + + + ) } @@ -151,7 +194,8 @@ const MediumClap = ({ animationTimeline }) => { Smaller Component used by ==================================== **/ -const ClapIcon = ({ isClicked }) => { +const ClapIcon = () => { + const { isClicked } = useContext(MediumClapContext) return ( { ) } -const ClapCount = ({ count }) => { +const ClapCount = () => { + const { count, setRef } = useContext(MediumClapContext) return ( - + +{count} ) } -const CountTotal = ({ countTotal }) => { +const CountTotal = () => { + const { countTotal, setRef } = useContext(MediumClapContext) return ( - + {countTotal} ) } +const ClapInfo = () => { + const { countTotal } = useContext(MediumClapContext) + return ( +
+ {wordConverter.toWords(countTotal)} claps! +
+ ) +} + +MediumClap.Icon = ClapIcon +MediumClap.Count = ClapCount +MediumClap.Total = CountTotal +MediumClap.Info = ClapInfo + /** ==================================== * 🔰USAGE Below's how a potential user @@ -188,8 +248,14 @@ const CountTotal = ({ countTotal }) => { ==================================== **/ const Usage = () => { - const AnimatedMediumClap = withClapAnimation(MediumClap) - return + return ( + + + + + + + ) } export default Usage diff --git a/showcase/yarn.lock b/showcase/yarn.lock index a3b8374..aa4c406 100644 --- a/showcase/yarn.lock +++ b/showcase/yarn.lock @@ -4467,6 +4467,11 @@ number-is-nan@^1.0.0: resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= +number-to-words@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/number-to-words/-/number-to-words-1.2.4.tgz#e0f124de9628f8d86c4eeb89bac6c07699264501" + integrity sha512-/fYevVkXRcyBiZDg6yzZbm0RuaD6i0qRfn8yr+6D0KgBMOndFPxuW10qCHpzs50nN8qKuv78k8MuotZhcVX6Pw== + object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"