From 37423602bb43843d2b6c213cbc556901adc4dfef Mon Sep 17 00:00:00 2001 From: Ohans Emmanuel Date: 2019年12月18日 06:20:22 +0100 Subject: [PATCH 01/41] cleanups --- showcase/src/constants/displayBoxNotes.js | 5 +- showcase/src/patterns/02.js | 241 ------------ showcase/src/patterns/03.js | 243 ------------ showcase/src/patterns/04.js | 309 ---------------- showcase/src/patterns/05.js | 342 ----------------- showcase/src/patterns/06.js | 409 --------------------- showcase/src/patterns/07.js | 323 ---------------- showcase/src/patterns/08.js | 345 ----------------- showcase/src/patterns/09.js | 400 -------------------- showcase/src/patterns/10.js | 427 ---------------------- 10 files changed, 1 insertion(+), 3043 deletions(-) delete mode 100644 showcase/src/patterns/02.js delete mode 100644 showcase/src/patterns/03.js delete mode 100644 showcase/src/patterns/04.js delete mode 100644 showcase/src/patterns/05.js delete mode 100644 showcase/src/patterns/06.js delete mode 100644 showcase/src/patterns/07.js delete mode 100644 showcase/src/patterns/08.js delete mode 100644 showcase/src/patterns/09.js delete mode 100644 showcase/src/patterns/10.js diff --git a/showcase/src/constants/displayBoxNotes.js b/showcase/src/constants/displayBoxNotes.js index 6553856..61c4eb1 100644 --- a/showcase/src/constants/displayBoxNotes.js +++ b/showcase/src/constants/displayBoxNotes.js @@ -1,4 +1 @@ -export const NOTES = { - '1': 'Animated via an HOC', - '2': 'Animated via a hook 💪' -} +export const NOTES = {} diff --git a/showcase/src/patterns/02.js b/showcase/src/patterns/02.js deleted file mode 100644 index fb18d7d..0000000 --- a/showcase/src/patterns/02.js +++ /dev/null @@ -1,241 +0,0 @@ -import React, { - useState, - useCallback, - useLayoutEffect, - useContext, - useMemo, - createContext -} from 'react' - -import mojs from 'mo-js' -import { generateRandomNumber } from '../utils/generateRandomNumber' -import styles from './index.css' - -/** ==================================== - * 🔰Hook - Hook for Animation - ==================================== **/ - -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: burstEl, - radius: { 50: 95 }, - count: 5, - angle: 30, - children: { - shape: 'polygon', - radius: { 6: 0 }, - scale: 1, - stroke: 'rgba(211,84,0 ,0.5)', - strokeWidth: 2, - angle: 210, - delay: 30, - speed: 0.2, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1), - duration: tlDuration - } - }) - - const circleBurst = new mojs.Burst({ - parent: burstEl, - radius: { 50: 75 }, - angle: 25, - duration: tlDuration, - children: { - shape: 'circle', - fill: 'rgba(149,165,166 ,0.5)', - delay: 30, - speed: 0.2, - radius: { 3: 0 }, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1) - } - }) - - const countAnimation = new mojs.Html({ - el: bounceEl, - isShowStart: false, - isShowEnd: true, - y: { 0: -30 }, - opacity: { 0: 1 }, - duration: tlDuration - }).then({ - opacity: { 1: 0 }, - y: -80, - delay: tlDuration / 2 - }) - - const countTotalAnimation = new mojs.Html({ - el: fadeEl, - isShowStart: false, - isShowEnd: true, - opacity: { 0: 1 }, - delay: (3 * tlDuration) / 2, - duration: tlDuration, - y: { 0: -3 } - }) - - const scaleButton = new mojs.Html({ - el: burstEl, - duration: tlDuration, - scale: { 1.3: 1 }, - easing: mojs.easing.out - }) - - 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 - ]) - - setAnimationTimeline(updatedAnimationTimeline) - }, - [tlDuration, animationTimeline, bounceEl, fadeEl, burstEl] - ) - - return animationTimeline -} -/** ==================================== - * 🔰 MediumClap - ==================================== **/ -const initialState = { - count: 0, - countTotal: generateRandomNumber(500, 10000), - isClicked: false -} - -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 = () => { - animationTimeline.replay() - - setClapState({ - count: Math.min(count + 1, MAXIMUM_USER_CLAP), - countTotal: count < MAXIMUM_USER_CLAP ? countTotal + 1 : countTotal, - isClicked: true - }) - } - - const memoizedValue = useMemo( - () => ({ - count, - countTotal, - isClicked, - setRef - }), - [count, countTotal, isClicked, setRef] - ) - - return ( - - - - ) -} - -/** ==================================== - * 🔰SubComponents - Smaller Component used by - ==================================== **/ - -const ClapIcon = () => { - const { isClicked } = useContext(MediumClapContext) - return ( - - - - - - - ) -} -const ClapCount = () => { - const { count, setRef } = useContext(MediumClapContext) - return ( - - +{count} - - ) -} -const CountTotal = () => { - const { countTotal, setRef } = useContext(MediumClapContext) - return ( - - {countTotal} - - ) -} - -/** ==================================== - * 🔰USAGE - Below's how a potential user - may consume the component API - ==================================== **/ - -const Usage = () => { - return -} - -export default Usage diff --git a/showcase/src/patterns/03.js b/showcase/src/patterns/03.js deleted file mode 100644 index 2809f8f..0000000 --- a/showcase/src/patterns/03.js +++ /dev/null @@ -1,243 +0,0 @@ -import React, { - useState, - useEffect, - useContext, - useMemo, - useRef, - createContext -} from 'react' - -import mojs from 'mo-js' -import wordConverter from 'number-to-words' -import { generateRandomNumber } from '../utils/generateRandomNumber' -import styles from './index.css' - -/** ==================================== - * 🔰Hook - Hook for Animation -==================================== **/ - -const useClapAnimation = ({ duration: tlDuration }) => { - const [animationTimeline, setAnimationTimeline] = useState( - new mojs.Timeline() - ) - - useEffect( - () => { - const triangleBurst = new mojs.Burst({ - parent: '#clap', - radius: { 50: 95 }, - count: 5, - angle: 30, - children: { - shape: 'polygon', - radius: { 6: 0 }, - scale: 1, - stroke: 'rgba(211,84,0 ,0.5)', - strokeWidth: 2, - angle: 210, - delay: 30, - speed: 0.2, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1), - duration: tlDuration - } - }) - - const circleBurst = new mojs.Burst({ - parent: '#clap', - radius: { 50: 75 }, - angle: 25, - duration: tlDuration, - children: { - shape: 'circle', - fill: 'rgba(149,165,166 ,0.5)', - delay: 30, - speed: 0.2, - radius: { 3: 0 }, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1) - } - }) - - const countAnimation = new mojs.Html({ - el: '#clapCount', - isShowStart: false, - isShowEnd: true, - y: { 0: -30 }, - opacity: { 0: 1 }, - duration: tlDuration - }).then({ - opacity: { 1: 0 }, - y: -80, - delay: tlDuration / 2 - }) - - const countTotalAnimation = new mojs.Html({ - el: '#clapCountTotal', - isShowStart: false, - isShowEnd: true, - opacity: { 0: 1 }, - delay: (3 * tlDuration) / 2, - duration: tlDuration, - y: { 0: -3 } - }) - - const scaleButton = new mojs.Html({ - el: '#clap', - duration: tlDuration, - scale: { 1.3: 1 }, - easing: mojs.easing.out - }) - - const clap = document.getElementById('clap') - clap.style.transform = 'scale(1, 1)' - - const updatedAnimationTimeline = animationTimeline.add([ - countAnimation, - countTotalAnimation, - scaleButton, - circleBurst, - triangleBurst - ]) - - setAnimationTimeline(updatedAnimationTimeline) - }, - [tlDuration, animationTimeline] - ) - - return animationTimeline -} -/** ==================================== - * 🔰 MediumClap -==================================== **/ -const initialState = { - count: 0, - countTotal: generateRandomNumber(500, 10000), - isClicked: false -} - -const MediumClapContext = createContext() -const { Provider } = MediumClapContext - -const MediumClap = ({ children, onClap }) => { - const MAXIMUM_USER_CLAP = 50 - const [clapState, setClapState] = useState(initialState) - const { count, countTotal, isClicked } = clapState - - const animationTimeline = useClapAnimation({ duration: 300 }) - const handleClapClick = () => { - // 👉 prop from HOC - animationTimeline.replay() - - setClapState({ - count: Math.min(count + 1, MAXIMUM_USER_CLAP), - countTotal: count < MAXIMUM_USER_CLAP ? countTotal + 1 : countTotal, - isClicked: true - }) - } - - const componentJustMounted = useRef(true) - - useEffect( - () => { - if (!componentJustMounted.current) { - onClap(clapState) - } - componentJustMounted.current = false - }, - [count, onClap] - ) - - const memoizedValue = useMemo( - () => ({ - count, - countTotal, - isClicked - }), - [count, countTotal, isClicked] - ) - - return ( - - - - ) -} - -/** ==================================== - * 🔰SubComponents -Smaller Component used by -==================================== **/ - -const ClapIcon = () => { - const { isClicked } = useContext(MediumClapContext) - return ( - - - - - - - ) -} -const ClapCount = () => { - const { count } = useContext(MediumClapContext) - return ( - - +{count} - - ) -} -const CountTotal = () => { - const { countTotal } = useContext(MediumClapContext) - return ( - - {countTotal} - - ) -} - -const ClapInfo = ({ info }) => { - const { countTotal } = useContext(MediumClapContext) - return ( -
- {info || wordConverter.toWords(countTotal)} claps! -
- ) -} - -MediumClap.Icon = ClapIcon -MediumClap.Count = ClapCount -MediumClap.Total = CountTotal -MediumClap.Info = ClapInfo - -/** ==================================== - * 🔰USAGE - Below's how a potential user - may consume the component API -==================================== **/ - -const Usage = () => { - const [total, setTotal] = useState(0) - - const onClap = ({ countTotal }) => { - setTotal(countTotal) - } - - return ( - - - - - - - ) -} - -export default Usage diff --git a/showcase/src/patterns/04.js b/showcase/src/patterns/04.js deleted file mode 100644 index 4dabdcc..0000000 --- a/showcase/src/patterns/04.js +++ /dev/null @@ -1,309 +0,0 @@ -import React, { - useState, - useEffect, - useCallback, - useLayoutEffect, - useContext, - useMemo, - useRef, - createContext -} from 'react' - -import mojs from 'mo-js' -import wordConverter from 'number-to-words' -import { generateRandomNumber } from '../utils/generateRandomNumber' -import styles from './index.css' -import userStyles from './usage.css' - -/** ==================================== - * 🔰Hook - Hook for Animation -==================================== **/ - -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: burstEl, - radius: { 50: 95 }, - count: 5, - angle: 30, - children: { - shape: 'polygon', - radius: { 6: 0 }, - scale: 1, - stroke: 'rgba(211,84,0 ,0.5)', - strokeWidth: 2, - angle: 210, - delay: 30, - speed: 0.2, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1), - duration: tlDuration - } - }) - - const circleBurst = new mojs.Burst({ - parent: burstEl, - radius: { 50: 75 }, - angle: 25, - duration: tlDuration, - children: { - shape: 'circle', - fill: 'rgba(149,165,166 ,0.5)', - delay: 30, - speed: 0.2, - radius: { 3: 0 }, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1) - } - }) - - const countAnimation = new mojs.Html({ - el: bounceEl, - isShowStart: false, - isShowEnd: true, - y: { 0: -30 }, - opacity: { 0: 1 }, - duration: tlDuration - }).then({ - opacity: { 1: 0 }, - y: -80, - delay: tlDuration / 2 - }) - - const countTotalAnimation = new mojs.Html({ - el: fadeEl, - isShowStart: false, - isShowEnd: true, - opacity: { 0: 1 }, - delay: (3 * tlDuration) / 2, - duration: tlDuration, - y: { 0: -3 } - }) - - const scaleButton = new mojs.Html({ - el: burstEl, - duration: tlDuration, - scale: { 1.3: 1 }, - easing: mojs.easing.out - }) - - 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 - ]) - - setAnimationTimeline(updatedAnimationTimeline) - }, - [tlDuration, animationTimeline, bounceEl, fadeEl, burstEl] - ) - - return animationTimeline -} -/** ==================================== - * 🔰 MediumClap -==================================== **/ -const initialState = { - count: 0, - countTotal: generateRandomNumber(500, 10000), - isClicked: false -} - -const MediumClapContext = createContext() -const { Provider } = MediumClapContext - -const MediumClap = ({ - children, - onClap, - className = '', - style: userStyles = {} -}) => { - 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 = () => { - animationTimeline.replay() - - setClapState({ - count: Math.min(count + 1, MAXIMUM_USER_CLAP), - countTotal: count < MAXIMUM_USER_CLAP ? countTotal + 1 : countTotal, - isClicked: true - }) - } - - const componentJustMounted = useRef(true) - - useEffect( - () => { - if (!componentJustMounted.current) { - onClap(clapState) - } - componentJustMounted.current = false - }, - [count, onClap] - ) - - const memoizedValue = useMemo( - () => ({ - count, - countTotal, - isClicked, - setRef - }), - [count, countTotal, isClicked, setRef] - ) - - const classNames = [styles.clap, className].join(' ').trim() - - return ( - - - - ) -} - -/** ==================================== - * 🔰SubComponents -Smaller Component used by -==================================== **/ - -const ClapIcon = ({ className = '', style: userStyles = {} }) => { - const { isClicked } = useContext(MediumClapContext) - const classNames = [styles.icon, isClicked ? styles.checked : '', className] - .join(' ') - .trim() - - return ( - - - - - - - ) -} -const ClapCount = ({ className = '', style: userStyles = {} }) => { - const { count, setRef } = useContext(MediumClapContext) - const classNames = [styles.count, className].join(' ').trim() - - return ( - - +{count} - - ) -} -const CountTotal = ({ className = '', style: userStyles = {} }) => { - const { countTotal, setRef } = useContext(MediumClapContext) - const classNames = [styles.total, className].join(' ').trim() - - return ( - - {countTotal} - - ) -} - -const ClapInfo = ({ info }) => { - const { countTotal } = useContext(MediumClapContext) - return ( -
- {info || wordConverter.toWords(countTotal)} claps! -
- ) -} - -MediumClap.Icon = ClapIcon -MediumClap.Count = ClapCount -MediumClap.Total = CountTotal -MediumClap.Info = ClapInfo - -/** ==================================== - * 🔰USAGE - Below's how a potential user - may consume the component API -==================================== **/ - -const Usage = () => { - const [total, setTotal] = useState(0) - - const onClap = ({ countTotal }) => { - setTotal(countTotal) - } - - return ( - - - - - - - ) -} - -export default Usage diff --git a/showcase/src/patterns/05.js b/showcase/src/patterns/05.js deleted file mode 100644 index 09737b7..0000000 --- a/showcase/src/patterns/05.js +++ /dev/null @@ -1,342 +0,0 @@ -import React, { - useState, - useEffect, - useCallback, - useLayoutEffect, - useContext, - useMemo, - useRef, - createContext -} from 'react' - -import mojs from 'mo-js' -import wordConverter from 'number-to-words' -import { generateRandomNumber } from '../utils/generateRandomNumber' -import styles from './index.css' -import userStyles from './usage.css' - -/** ==================================== - * 🔰Hook - Hook for Animation - ==================================== **/ - -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: burstEl, - radius: { 50: 95 }, - count: 5, - angle: 30, - children: { - shape: 'polygon', - radius: { 6: 0 }, - scale: 1, - stroke: 'rgba(211,84,0 ,0.5)', - strokeWidth: 2, - angle: 210, - delay: 30, - speed: 0.2, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1), - duration: tlDuration - } - }) - - const circleBurst = new mojs.Burst({ - parent: burstEl, - radius: { 50: 75 }, - angle: 25, - duration: tlDuration, - children: { - shape: 'circle', - fill: 'rgba(149,165,166 ,0.5)', - delay: 30, - speed: 0.2, - radius: { 3: 0 }, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1) - } - }) - - const countAnimation = new mojs.Html({ - el: bounceEl, - isShowStart: false, - isShowEnd: true, - y: { 0: -30 }, - opacity: { 0: 1 }, - duration: tlDuration - }).then({ - opacity: { 1: 0 }, - y: -80, - delay: tlDuration / 2 - }) - - const countTotalAnimation = new mojs.Html({ - el: fadeEl, - isShowStart: false, - isShowEnd: true, - opacity: { 0: 1 }, - delay: (3 * tlDuration) / 2, - duration: tlDuration, - y: { 0: -3 } - }) - - const scaleButton = new mojs.Html({ - el: burstEl, - duration: tlDuration, - scale: { 1.3: 1 }, - easing: mojs.easing.out - }) - - if (typeof burstEl === 'string') { - const id = burstEl.slice(1, burstEl.length) - 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 - ]) - - setAnimationTimeline(updatedAnimationTimeline) - }, - [tlDuration, animationTimeline, bounceEl, fadeEl, burstEl] - ) - - return animationTimeline -} -/** ==================================== - * 🔰 MediumClap - ==================================== **/ -const initialState = { - count: 0, - countTotal: generateRandomNumber(500, 10000), - isClicked: false -} - -const MediumClapContext = createContext() -const { Provider } = MediumClapContext - -const MediumClap = ({ - children, - onClap = () => {}, - values = null, - className = '', - style: userStyles = {} -}) => { - 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 - }) - - // Controlled Component ? isControlled = value !== undefined - const isControlled = !!values - - const handleClapClick = () => { - // 👉 prop from HOC - animationTimeline.replay() - isControlled - ? onClap() - : setClapState({ - count: Math.min(count + 1, MAXIMUM_USER_CLAP), - countTotal: count < MAXIMUM_USER_CLAP ? countTotal + 1 : countTotal, - isClicked: true - }) - } - - const componentJustMounted = useRef(true) - - useEffect( - () => { - if (!componentJustMounted.current && !isControlled) { - onClap(clapState) - } - componentJustMounted.current = false - }, - [count, onClap, isControlled] - ) - - const getState = useCallback(() => (isControlled ? values : clapState), [ - isControlled, - clapState, - values - ]) - - const memoizedValue = useMemo( - () => { - return { - ...getState(), - setRef - } - }, - [getState, setRef] - ) - - const classNames = [styles.clap, className].join(' ').trim() - - return ( - - - - ) -} - -/** ==================================== - * 🔰SubComponents - Smaller Component used by - ==================================== **/ - -const ClapIcon = ({ className = '', style: userStyles = {} }) => { - const { isClicked } = useContext(MediumClapContext) - const classNames = [styles.icon, isClicked ? styles.checked : '', className] - .join(' ') - .trim() - - return ( - - - - - - - ) -} -const ClapCount = ({ className = '', style: userStyles = {} }) => { - const { count, setRef } = useContext(MediumClapContext) - const classNames = [styles.count, className].join(' ').trim() - - return ( - - +{count} - - ) -} -const CountTotal = ({ className = '', style: userStyles = {} }) => { - const { countTotal, setRef } = useContext(MediumClapContext) - const classNames = [styles.total, className].join(' ').trim() - - return ( - - {countTotal} - - ) -} - -const ClapInfo = ({ info }) => { - const { countTotal } = useContext(MediumClapContext) - return ( -
- {info || wordConverter.toWords(countTotal)} claps! -
- ) -} - -MediumClap.Icon = ClapIcon -MediumClap.Count = ClapCount -MediumClap.Total = CountTotal -MediumClap.Info = ClapInfo - -/** ==================================== - * 🔰USAGE - Below's how a potential user - may consume the component API - ==================================== **/ - -const MAXIMUM_USER_CLAP = 10 -const INITIAL_STATE = { - count: 0, - countTotal: 1206, - isClicked: false -} - -const Usage = () => { - const [clapValues, setClapValues] = useState(INITIAL_STATE) - - const onClap = () => { - setClapValues(({ count, countTotal }) => ({ - count: Math.min(count + 1, MAXIMUM_USER_CLAP), - countTotal: count < MAXIMUM_USER_CLAP ? countTotal + 1 : countTotal, - isClicked: true - })) - } - - return ( -
- - - - - - - - - - - -
- ) -} - -export default Usage diff --git a/showcase/src/patterns/06.js b/showcase/src/patterns/06.js deleted file mode 100644 index 6e745df..0000000 --- a/showcase/src/patterns/06.js +++ /dev/null @@ -1,409 +0,0 @@ -import React, { - useState, - useEffect, - useCallback, - useLayoutEffect, - forwardRef, - useRef - } from 'react' - - import mojs from 'mo-js' - import { generateRandomNumber } from '../utils/generateRandomNumber' - import styles from './index.css' - import userStyles from './usage.css' - - /** ==================================== - * 🔰Hook - Hook for Animation - ==================================== **/ - - 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: burstEl, - radius: { 50: 95 }, - count: 5, - angle: 30, - children: { - shape: 'polygon', - radius: { 6: 0 }, - scale: 1, - stroke: 'rgba(211,84,0 ,0.5)', - strokeWidth: 2, - angle: 210, - delay: 30, - speed: 0.2, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1), - duration: tlDuration - } - }) - - const circleBurst = new mojs.Burst({ - parent: burstEl, - radius: { 50: 75 }, - angle: 25, - duration: tlDuration, - children: { - shape: 'circle', - fill: 'rgba(149,165,166 ,0.5)', - delay: 30, - speed: 0.2, - radius: { 3: 0 }, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1) - } - }) - - const countAnimation = new mojs.Html({ - el: bounceEl, - isShowStart: false, - isShowEnd: true, - y: { 0: -30 }, - opacity: { 0: 1 }, - duration: tlDuration - }).then({ - opacity: { 1: 0 }, - y: -80, - delay: tlDuration / 2 - }) - - const countTotalAnimation = new mojs.Html({ - el: fadeEl, - isShowStart: false, - isShowEnd: true, - opacity: { 0: 1 }, - delay: (3 * tlDuration) / 2, - duration: tlDuration, - y: { 0: -3 } - }) - - const scaleButton = new mojs.Html({ - el: burstEl, - duration: tlDuration, - scale: { 1.3: 1 }, - easing: mojs.easing.out - }) - - if (typeof burstEl === 'string') { - const id = burstEl.slice(1, burstEl.length) - 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 - ]) - - setAnimationTimeline(updatedAnimationTimeline) - }, - [tlDuration, animationTimeline, bounceEl, fadeEl, burstEl] - ) - - return animationTimeline - } - - /** ==================================== - * 🔰Hook - Hook for Clap State - ==================================== **/ - const MAX_CLAP = 50 - const INIT_STATE = { - count: 0, - countTotal: generateRandomNumber(500, 10000), - isClicked: false - } - - const useClapState = ({ initialState = INIT_STATE } = {}) => { - const [clapState, setClapState] = useState(initialState) - const { count, countTotal } = clapState - - const handleClapClick = useCallback( - () => { - setClapState({ - count: Math.min(count + 1, MAX_CLAP), - countTotal: count < MAX_CLAP ? countTotal + 1 : countTotal, - isClicked: true - }) - }, - [count, countTotal] - ) - - return { - clapState, - handleClapClick - } - } - - /** ==================================== - * 🔰Hook - useEffectAfterMount - ==================================== **/ - - function useEffectAfterMount (cb, deps) { - const componentJustMounted = useRef(true) - useEffect(() => { - if (!componentJustMounted.current) { - return cb() - } - componentJustMounted.current = false - // eslint-disable-next-line react-hooks/exhaustive-deps - }, deps) - } - - /** ==================================== - * 🔰Hook - useDOMRef - ==================================== **/ - const useDOMRef = () => { - const [DOMRef, setDOMRef] = useState({}) - const setRef = useCallback(node => { - if (node !== null) { - setDOMRef(prevDOMRefs => ({ - ...prevDOMRefs, - [node.dataset.refkey]: node - })) - } - }, []) - - return [DOMRef, setRef] - } - - /** ==================================== - * 🔰 MediumClap - ==================================== **/ - - const MediumClap = () => { - const { clapState, handleClapClick } = useClapState() - const { count, countTotal, isClicked } = clapState - - const [ - { clapContainerRef, clapCountRef, countTotalRef }, - setRef - ] = useDOMRef() - - const animationTimeline = useClapAnimation({ - duration: 300, - bounceEl: clapCountRef, - fadeEl: countTotalRef, - burstEl: clapContainerRef - }) - - useEffectAfterMount( - () => { - animationTimeline.replay() - }, - [count] - ) - - return ( - - - - - - ) - } - - /** ==================================== - * 🔰SubComponents - Smaller Component used by - ==================================== **/ - - const ClapContainer = forwardRef( - ( - { children, handleClick, className, style: userStyles = {}, ...restProps }, - ref - ) => { - const classNames = [styles.clap, className].join(' ').trim() - - return ( - - ) - } - ) - - const ClapIcon = ({ className = '', style: userStyles = {}, isClicked }) => { - const classNames = [styles.icon, isClicked ? styles.checked : '', className] - .join(' ') - .trim() - - return ( - - - - - - - ) - } - - const ClapCount = forwardRef( - ({ count, className = '', style: userStyles = {}, ...restProps }, ref) => { - const classNames = [styles.count, className].join(' ').trim() - - return ( - - +{count} - - ) - } - ) - - const CountTotal = forwardRef( - ( - { countTotal, className = '', style: userStyles = {}, ...restProps }, - ref - ) => { - const classNames = [styles.total, className].join(' ').trim() - - return ( - - {countTotal} - - ) - } - ) - - /** ==================================== - * 🔰USAGE - Below's how a potential user - may consume the component API - ==================================== **/ - - const CupBowl = () => { - // Credit: Created by Kieu Thi Kim Cuong from the Noun Project - return ( - - - - - - ) - } - - const CupHandle = () => { - // Credit: Created by Kieu Thi Kim Cuong from the Noun Project - return ( - - - - - - ) - } - - const Stream = () => { - // Credit: Created by Kieu Thi Kim Cuong from the Noun Project - return ( - - - - - - ) - } - - const CupBase = () => { - // Credit: Created by Kieu Thi Kim Cuong from the Noun Project - return ( - - - - - - ) - } - - const Usage = () => { - const animationTimeline = useClapAnimation({ - duration: 300, - bounceEl: '#stream', - fadeEl: '#cupHandle', - burstEl: '#coffee' - }) - - const handleClick = () => { - animationTimeline.replay() - } - - return ( -
-
- -
-
- coffee -
-
- - -
-
- -
-
- -
-
- ) - } - - export default Usage \ No newline at end of file diff --git a/showcase/src/patterns/07.js b/showcase/src/patterns/07.js deleted file mode 100644 index e1ea07e..0000000 --- a/showcase/src/patterns/07.js +++ /dev/null @@ -1,323 +0,0 @@ -import React, { - useState, - useEffect, - useCallback, - useLayoutEffect, - forwardRef, - useRef -} from 'react' - -import mojs from 'mo-js' -import { generateRandomNumber } from '../utils/generateRandomNumber' -import styles from './index.css' -import userStyles from './usage.css' - -/** ==================================== - * 🔰Hook - Hook for Animation - ==================================== **/ - -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: burstEl, - radius: { 50: 95 }, - count: 5, - angle: 30, - children: { - shape: 'polygon', - radius: { 6: 0 }, - scale: 1, - stroke: 'rgba(211,84,0 ,0.5)', - strokeWidth: 2, - angle: 210, - delay: 30, - speed: 0.2, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1), - duration: tlDuration - } - }) - - const circleBurst = new mojs.Burst({ - parent: burstEl, - radius: { 50: 75 }, - angle: 25, - duration: tlDuration, - children: { - shape: 'circle', - fill: 'rgba(149,165,166 ,0.5)', - delay: 30, - speed: 0.2, - radius: { 3: 0 }, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1) - } - }) - - const countAnimation = new mojs.Html({ - el: bounceEl, - isShowStart: false, - isShowEnd: true, - y: { 0: -30 }, - opacity: { 0: 1 }, - duration: tlDuration - }).then({ - opacity: { 1: 0 }, - y: -80, - delay: tlDuration / 2 - }) - - const countTotalAnimation = new mojs.Html({ - el: fadeEl, - isShowStart: false, - isShowEnd: true, - opacity: { 0: 1 }, - delay: (3 * tlDuration) / 2, - duration: tlDuration, - y: { 0: -3 } - }) - - const scaleButton = new mojs.Html({ - el: burstEl, - duration: tlDuration, - scale: { 1.3: 1 }, - easing: mojs.easing.out - }) - - if (typeof burstEl === 'string') { - const id = burstEl.slice(1, burstEl.length) - 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 - ]) - - setAnimationTimeline(updatedAnimationTimeline) - }, - [tlDuration, animationTimeline, bounceEl, fadeEl, burstEl] - ) - - return animationTimeline -} - -/** ==================================== - * 🔰Hook - Hook for Clap State - ==================================== **/ -const MAX_CLAP = 50 -const INIT_STATE = { - count: 0, - countTotal: generateRandomNumber(500, 10000), - isClicked: false -} - -const useClapState = ({ initialState = INIT_STATE } = {}) => { - const [clapState, setClapState] = useState(initialState) - const { count, countTotal } = clapState - - const handleClapClick = useCallback( - () => { - setClapState({ - count: Math.min(count + 1, MAX_CLAP), - countTotal: count < MAX_CLAP ? countTotal + 1 : countTotal, - isClicked: true - }) - }, - [count, countTotal] - ) - - const togglerProps = { - onClick: handleClapClick, - 'aria-pressed': clapState.isClicked - } - - const counterProps = { - count, - 'aria-valuemax': MAX_CLAP, - 'aria-valuemin': 0, - 'aria-valuenow': count - } - - return { - clapState, - togglerProps, - counterProps - } -} - -/** ==================================== - * 🔰Hook - useEffectAfterMount - ==================================== **/ - -function useEffectAfterMount (cb, deps) { - const componentJustMounted = useRef(true) - useEffect(() => { - if (!componentJustMounted.current) { - return cb() - } - componentJustMounted.current = false - // eslint-disable-next-line react-hooks/exhaustive-deps - }, deps) -} - -/** ==================================== - * 🔰Hook - useDOMRef - ==================================== **/ -const useDOMRef = () => { - const [DOMRef, setDOMRef] = useState({}) - const setRef = useCallback(node => { - if (node !== null) { - setDOMRef(prevDOMRefs => ({ - ...prevDOMRefs, - [node.dataset.refkey]: node - })) - } - }, []) - - return [DOMRef, setRef] -} - -/** ==================================== - * 🔰SubComponents - Smaller Component used by - ==================================== **/ - -const ClapContainer = forwardRef( - ( - { children, handleClick, className, style: userStyles = {}, ...restProps }, - ref - ) => { - const classNames = [styles.clap, className].join(' ').trim() - - return ( - - ) - } -) - -const ClapIcon = ({ className = '', style: userStyles = {}, isClicked }) => { - const classNames = [styles.icon, isClicked ? styles.checked : '', className] - .join(' ') - .trim() - - return ( - - - - - - - ) -} - -const ClapCount = forwardRef( - ({ count, className = '', style: userStyles = {}, ...restProps }, ref) => { - const classNames = [styles.count, className].join(' ').trim() - - return ( - - +{count} - - ) - } -) - -const CountTotal = forwardRef( - ( - { countTotal, className = '', style: userStyles = {}, ...restProps }, - ref - ) => { - const classNames = [styles.total, className].join(' ').trim() - - return ( - - {countTotal} - - ) - } -) - -/** ==================================== - * 🔰USAGE - Below's how a potential user - may consume the component API - ==================================== **/ - -const Usage = () => { - const { clapState, togglerProps, counterProps } = useClapState() - const { count, countTotal, isClicked } = clapState - - const [ - { clapContainerRef, clapCountRef, countTotalRef }, - setRef - ] = useDOMRef() - - const animationTimeline = useClapAnimation({ - duration: 300, - bounceEl: clapCountRef, - fadeEl: countTotalRef, - burstEl: clapContainerRef - }) - - useEffectAfterMount( - () => { - animationTimeline.replay() - }, - [count] - ) - - return ( - - - - - - ) -} - -export default Usage diff --git a/showcase/src/patterns/08.js b/showcase/src/patterns/08.js deleted file mode 100644 index be75d84..0000000 --- a/showcase/src/patterns/08.js +++ /dev/null @@ -1,345 +0,0 @@ -import React, { - useState, - useEffect, - useCallback, - useLayoutEffect, - forwardRef, - useRef -} from 'react' - -import mojs from 'mo-js' -import { generateRandomNumber } from '../utils/generateRandomNumber' -import styles from './index.css' -import userStyles from './usage.css' - -/** ==================================== - * 🔰Hook - Hook for Animation - ==================================== **/ - -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: burstEl, - radius: { 50: 95 }, - count: 5, - angle: 30, - children: { - shape: 'polygon', - radius: { 6: 0 }, - scale: 1, - stroke: 'rgba(211,84,0 ,0.5)', - strokeWidth: 2, - angle: 210, - delay: 30, - speed: 0.2, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1), - duration: tlDuration - } - }) - - const circleBurst = new mojs.Burst({ - parent: burstEl, - radius: { 50: 75 }, - angle: 25, - duration: tlDuration, - children: { - shape: 'circle', - fill: 'rgba(149,165,166 ,0.5)', - delay: 30, - speed: 0.2, - radius: { 3: 0 }, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1) - } - }) - - const countAnimation = new mojs.Html({ - el: bounceEl, - isShowStart: false, - isShowEnd: true, - y: { 0: -30 }, - opacity: { 0: 1 }, - duration: tlDuration - }).then({ - opacity: { 1: 0 }, - y: -80, - delay: tlDuration / 2 - }) - - const countTotalAnimation = new mojs.Html({ - el: fadeEl, - isShowStart: false, - isShowEnd: true, - opacity: { 0: 1 }, - delay: (3 * tlDuration) / 2, - duration: tlDuration, - y: { 0: -3 } - }) - - const scaleButton = new mojs.Html({ - el: burstEl, - duration: tlDuration, - scale: { 1.3: 1 }, - easing: mojs.easing.out - }) - - if (typeof burstEl === 'string') { - const id = burstEl.slice(1, burstEl.length) - 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 - ]) - - setAnimationTimeline(updatedAnimationTimeline) - }, - [tlDuration, animationTimeline, bounceEl, fadeEl, burstEl] - ) - - return animationTimeline -} - -/** ==================================== - * 🔰Hook - Hook for Clap State - ==================================== **/ -const MAX_CLAP = 50 -const INIT_STATE = { - count: 0, - countTotal: generateRandomNumber(500, 10000), - isClicked: false -} - -const callFnsInSequence = (...fns) => (...args) => - fns.forEach(fn => fn && fn(...args)) - -const useClapState = ({ initialState = INIT_STATE } = {}) => { - const [clapState, setClapState] = useState(initialState) - const { count, countTotal } = clapState - - const handleClapClick = useCallback( - () => { - setClapState({ - count: Math.min(count + 1, MAX_CLAP), - countTotal: count < MAX_CLAP ? countTotal + 1 : countTotal, - isClicked: true - }) - }, - [count, countTotal] - ) - - const getTogglerProps = ({ onClick, ...otherProps } = {}) => ({ - onClick: callFnsInSequence(handleClapClick, onClick), - 'aria-pressed': clapState.isClicked, - ...otherProps - }) - - const getCounterProps = ({ ...otherProps }) => ({ - count, - 'aria-valuemax': MAX_CLAP, - 'aria-valuemin': 0, - 'aria-valuenow': count, - ...otherProps - }) - - return { - clapState, - getTogglerProps, - getCounterProps - } -} - -/** ==================================== - * 🔰Hook - useEffectAfterMount - ==================================== **/ - -function useEffectAfterMount (cb, deps) { - const componentJustMounted = useRef(true) - useEffect(() => { - if (!componentJustMounted.current) { - return cb() - } - componentJustMounted.current = false - // eslint-disable-next-line react-hooks/exhaustive-deps - }, deps) -} - -/** ==================================== - * 🔰Hook - useDOMRef - ==================================== **/ -const useDOMRef = () => { - const [DOMRef, setDOMRef] = useState({}) - const setRef = useCallback(node => { - if (node !== null) { - setDOMRef(prevDOMRefs => ({ - ...prevDOMRefs, - [node.dataset.refkey]: node - })) - } - }, []) - - return [DOMRef, setRef] -} - -/** ==================================== - * 🔰SubComponents - Smaller Component used by - ==================================== **/ - -const ClapContainer = forwardRef( - ( - { children, handleClick, className, style: userStyles = {}, ...restProps }, - ref - ) => { - const classNames = [styles.clap, className].join(' ').trim() - - return ( - - ) - } -) - -const ClapIcon = ({ className = '', style: userStyles = {}, isClicked }) => { - const classNames = [styles.icon, isClicked ? styles.checked : '', className] - .join(' ') - .trim() - - return ( - - - - - - - ) -} - -const ClapCount = forwardRef( - ({ count, className = '', style: userStyles = {}, ...restProps }, ref) => { - const classNames = [styles.count, className].join(' ').trim() - - return ( - - +{count} - - ) - } -) - -const CountTotal = forwardRef( - ( - { countTotal, className = '', style: userStyles = {}, ...restProps }, - ref - ) => { - const classNames = [styles.total, className].join(' ').trim() - - return ( - - {countTotal} - - ) - } -) - -/** ==================================== - * 🔰USAGE - Below's how a potential user - may consume the component API - ==================================== **/ - -const Usage = () => { - const { clapState, getTogglerProps, getCounterProps } = useClapState() - const { count, countTotal, isClicked } = clapState - - const [ - { clapContainerRef, clapCountRef, countTotalRef }, - setRef - ] = useDOMRef() - - const animationTimeline = useClapAnimation({ - duration: 300, - bounceEl: clapCountRef, - fadeEl: countTotalRef, - burstEl: clapContainerRef - }) - - const onClick = () => { - animationTimeline.replay() - } - - return ( -
- - - - - -
- {!isClicked ? 'No recommendations :(' : `Recommended ${count} times 🔥`} -
-
- ) -} - -export default Usage diff --git a/showcase/src/patterns/09.js b/showcase/src/patterns/09.js deleted file mode 100644 index b515497..0000000 --- a/showcase/src/patterns/09.js +++ /dev/null @@ -1,400 +0,0 @@ -import React, { - useState, - useEffect, - useCallback, - useLayoutEffect, - forwardRef, - useRef -} from 'react' - -import mojs from 'mo-js' -import { generateRandomNumber } from '../utils/generateRandomNumber' -import styles from './index.css' -import userStyles from './usage.css' - -/** ==================================== - * 🔰Hook - Hook for Holding Previous Vals - ==================================== **/ -function usePrevious (value) { - const ref = useRef() - useEffect(() => { - ref.current = value - }) - return ref.current === undefined ? null : ref.current -} - -/** ==================================== - * 🔰Hook - Hook for Animation - ==================================== **/ - -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: burstEl, - radius: { 50: 95 }, - count: 5, - angle: 30, - children: { - shape: 'polygon', - radius: { 6: 0 }, - scale: 1, - stroke: 'rgba(211,84,0 ,0.5)', - strokeWidth: 2, - angle: 210, - delay: 30, - speed: 0.2, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1), - duration: tlDuration - } - }) - - const circleBurst = new mojs.Burst({ - parent: burstEl, - radius: { 50: 75 }, - angle: 25, - duration: tlDuration, - children: { - shape: 'circle', - fill: 'rgba(149,165,166 ,0.5)', - delay: 30, - speed: 0.2, - radius: { 3: 0 }, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1) - } - }) - - const countAnimation = new mojs.Html({ - el: bounceEl, - isShowStart: false, - isShowEnd: true, - y: { 0: -30 }, - opacity: { 0: 1 }, - duration: tlDuration - }).then({ - opacity: { 1: 0 }, - y: -80, - delay: tlDuration / 2 - }) - - const countTotalAnimation = new mojs.Html({ - el: fadeEl, - isShowStart: false, - isShowEnd: true, - opacity: { 0: 1 }, - delay: (3 * tlDuration) / 2, - duration: tlDuration, - y: { 0: -3 } - }) - - const scaleButton = new mojs.Html({ - el: burstEl, - duration: tlDuration, - scale: { 1.3: 1 }, - easing: mojs.easing.out - }) - - if (typeof burstEl === 'string') { - const id = burstEl.slice(1, burstEl.length) - 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 - ]) - - setAnimationTimeline(updatedAnimationTimeline) - }, - [tlDuration, animationTimeline, bounceEl, fadeEl, burstEl] - ) - - return animationTimeline -} - -/** ==================================== - * 🔰Hook - Hook for Clap State - ==================================== **/ -const MAX_CLAP = 50 -const INIT_STATE = { - count: 0, - countTotal: generateRandomNumber(500, 10000), - isClicked: false -} - -const callFnsInSequence = (...fns) => (...args) => - fns.forEach(fn => fn && fn(...args)) - -const useClapState = ({ initialState = INIT_STATE } = {}) => { - const initialStateRef = useRef(initialState) - const [clapState, setClapState] = useState(initialStateRef.current) - const { count, countTotal } = clapState - - const handleClapClick = useCallback( - () => { - setClapState({ - count: Math.min(count + 1, MAX_CLAP), - countTotal: count < MAX_CLAP ? countTotal + 1 : countTotal, - isClicked: true - }) - }, - [count, countTotal] - ) - - const resetRef = useRef(0) - // reset only if there's a change. It's possible to check changes to other state values e.g. countTotal & isClicked - const prevCount = usePrevious(count) - const reset = useCallback( - () => { - if (prevCount !== count) { - setClapState(initialStateRef.current) - ++resetRef.current - } - }, - [prevCount, count] - ) - - const getTogglerProps = ({ onClick, ...otherProps } = {}) => ({ - onClick: callFnsInSequence(handleClapClick, onClick), - 'aria-pressed': clapState.isClicked, - ...otherProps - }) - - const getCounterProps = ({ ...otherProps }) => ({ - count, - 'aria-valuemax': MAX_CLAP, - 'aria-valuemin': 0, - 'aria-valuenow': count, - ...otherProps - }) - - return { - clapState, - getTogglerProps, - getCounterProps, - reset, - resetDep: resetRef.current - } -} - -/** ==================================== - * 🔰Hook - useEffectAfterMount - ==================================== **/ - -function useEffectAfterMount (cb, deps) { - const componentJustMounted = useRef(true) - useEffect(() => { - if (!componentJustMounted.current) { - return cb() - } - componentJustMounted.current = false - // eslint-disable-next-line react-hooks/exhaustive-deps - }, deps) -} - -/** ==================================== - * 🔰Hook - useDOMRef - ==================================== **/ -const useDOMRef = () => { - const [DOMRef, setDOMRef] = useState({}) - const setRef = useCallback(node => { - if (node !== null) { - setDOMRef(prevDOMRefs => ({ - ...prevDOMRefs, - [node.dataset.refkey]: node - })) - } - }, []) - - return [DOMRef, setRef] -} - -/** ==================================== - * 🔰SubComponents - Smaller Component used by - ==================================== **/ - -const ClapContainer = forwardRef( - ( - { children, handleClick, className, style: userStyles = {}, ...restProps }, - ref - ) => { - const classNames = [styles.clap, className].join(' ').trim() - - return ( - - ) - } -) - -const ClapIcon = ({ className = '', style: userStyles = {}, isClicked }) => { - const classNames = [styles.icon, isClicked ? styles.checked : '', className] - .join(' ') - .trim() - - return ( - - - - - - - ) -} - -const ClapCount = forwardRef( - ({ count, className = '', style: userStyles = {}, ...restProps }, ref) => { - const classNames = [styles.count, className].join(' ').trim() - - return ( - - +{count} - - ) - } -) - -const CountTotal = forwardRef( - ( - { countTotal, className = '', style: userStyles = {}, ...restProps }, - ref - ) => { - const classNames = [styles.total, className].join(' ').trim() - - return ( - - {countTotal} - - ) - } -) - -/** ==================================== - * 🔰USAGE - Below's how a potential user - may consume the component API - ==================================== **/ - -const initialState = { count: 10, countTotal: 22, isClicked: false } -const Usage = () => { - const { - clapState, - getTogglerProps, - getCounterProps, - reset, - resetDep - } = useClapState({ initialState }) - const { count, countTotal, isClicked } = clapState - - const [ - { clapContainerRef, clapCountRef, countTotalRef }, - setRef - ] = useDOMRef() - - const animationTimeline = useClapAnimation({ - duration: 300, - bounceEl: clapCountRef, - fadeEl: countTotalRef, - burstEl: clapContainerRef - }) - - const onClick = () => { - animationTimeline.replay() - } - - // Side effect after reset has occured. - const [uploadingReset, setUpload] = useState(false) - useEffectAfterMount( - () => { - setUpload(true) - - const id = setTimeout(() => { - setUpload(false) - console.log('RESET COMPLETE!!!') - }, 3000) - - return () => clearTimeout(id) - }, - [resetDep] - ) - - return ( -
- - - - - -
- -
- {JSON.stringify({ count, countTotal, isClicked })}
- 
-
- {uploadingReset ? `uploading reset ${resetDep}...` : ''}
- 
-
-
- ) -} - -export default Usage diff --git a/showcase/src/patterns/10.js b/showcase/src/patterns/10.js deleted file mode 100644 index a2ffb36..0000000 --- a/showcase/src/patterns/10.js +++ /dev/null @@ -1,427 +0,0 @@ -import React, { - useReducer, - useState, - useEffect, - useCallback, - useLayoutEffect, - forwardRef, - useRef - } from 'react' - - import mojs from 'mo-js' - import { generateRandomNumber } from '../utils/generateRandomNumber' - import styles from './index.css' - import userStyles from './usage.css' - - /** ==================================== - * 🔰Hook - Hook for Holding Previous Vals - ==================================== **/ - function usePrevious (value) { - const ref = useRef() - useEffect(() => { - ref.current = value - }) - return ref.current === undefined ? null : ref.current - } - - /** ==================================== - * 🔰Hook - Hook for Animation - ==================================== **/ - - 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: burstEl, - radius: { 50: 95 }, - count: 5, - angle: 30, - children: { - shape: 'polygon', - radius: { 6: 0 }, - scale: 1, - stroke: 'rgba(211,84,0 ,0.5)', - strokeWidth: 2, - angle: 210, - delay: 30, - speed: 0.2, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1), - duration: tlDuration - } - }) - - const circleBurst = new mojs.Burst({ - parent: burstEl, - radius: { 50: 75 }, - angle: 25, - duration: tlDuration, - children: { - shape: 'circle', - fill: 'rgba(149,165,166 ,0.5)', - delay: 30, - speed: 0.2, - radius: { 3: 0 }, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1) - } - }) - - const countAnimation = new mojs.Html({ - el: bounceEl, - isShowStart: false, - isShowEnd: true, - y: { 0: -30 }, - opacity: { 0: 1 }, - duration: tlDuration - }).then({ - opacity: { 1: 0 }, - y: -80, - delay: tlDuration / 2 - }) - - const countTotalAnimation = new mojs.Html({ - el: fadeEl, - isShowStart: false, - isShowEnd: true, - opacity: { 0: 1 }, - delay: (3 * tlDuration) / 2, - duration: tlDuration, - y: { 0: -3 } - }) - - const scaleButton = new mojs.Html({ - el: burstEl, - duration: tlDuration, - scale: { 1.3: 1 }, - easing: mojs.easing.out - }) - - if (typeof burstEl === 'string') { - const id = burstEl.slice(1, burstEl.length) - 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 - ]) - - setAnimationTimeline(updatedAnimationTimeline) - }, - [tlDuration, animationTimeline, bounceEl, fadeEl, burstEl] - ) - - return animationTimeline - } - - /** ==================================== - * 🔰Hook - Hook for Clap State - ==================================== **/ - const MAX_CLAP = 50 - const INIT_STATE = { - count: 0, - countTotal: generateRandomNumber(500, 10000), - isClicked: false - } - - const callFnsInSequence = (...fns) => (...args) => - fns.forEach(fn => fn && fn(...args)) - - const clapReducer = (state, { type, payload }) => { - const { count, countTotal } = state - - switch (type) { - case useClapState.types.clap: - return { - count: count + 1, - countTotal: countTotal + 1, - isClicked: true - } - case useClapState.types.reset: - return payload - default: - return state - } - } - - const useClapState = ({ - initialState = INIT_STATE, - reducer = clapReducer - } = {}) => { - const initialStateRef = useRef(initialState) - const [clapState, dispatch] = useReducer(reducer, initialStateRef.current) - const { count } = clapState - - const handleClapClick = () => dispatch({ type: 'clap' }) - - const resetRef = useRef(0) - // reset only if there's a change. It's possible to check changes to other state values e.g. countTotal & isClicked - const reset = useCallback(() => { - dispatch({ type: 'reset', payload: initialStateRef.current }) - ++resetRef.current - }, []) - - const getTogglerProps = ({ onClick, ...otherProps } = {}) => ({ - onClick: callFnsInSequence(handleClapClick, onClick), - 'aria-pressed': clapState.isClicked, - ...otherProps - }) - - const getCounterProps = ({ ...otherProps }) => ({ - count, - 'aria-valuemax': MAX_CLAP, - 'aria-valuemin': 0, - 'aria-valuenow': count, - ...otherProps - }) - - return { - clapState, - getTogglerProps, - getCounterProps, - reset, - resetDep: resetRef.current - } - } - - useClapState.reducer = clapReducer - useClapState.types = { - clap: 'clap', - reset: 'reset' - } - - /** ==================================== - * 🔰Hook - useEffectAfterMount - ==================================== **/ - - function useEffectAfterMount (cb, deps) { - const componentJustMounted = useRef(true) - useEffect(() => { - if (!componentJustMounted.current) { - return cb() - } - componentJustMounted.current = false - // eslint-disable-next-line react-hooks/exhaustive-deps - }, deps) - } - - /** ==================================== - * 🔰Hook - useDOMRef - ==================================== **/ - const useDOMRef = () => { - const [DOMRef, setDOMRef] = useState({}) - const setRef = useCallback(node => { - if (node !== null) { - setDOMRef(prevDOMRefs => ({ - ...prevDOMRefs, - [node.dataset.refkey]: node - })) - } - }, []) - - return [DOMRef, setRef] - } - - /** ==================================== - * 🔰SubComponents - Smaller Component used by - ==================================== **/ - - const ClapContainer = forwardRef( - ( - { children, handleClick, className, style: userStyles = {}, ...restProps }, - ref - ) => { - const classNames = [styles.clap, className].join(' ').trim() - - return ( - - ) - } - ) - - const ClapIcon = ({ className = '', style: userStyles = {}, isClicked }) => { - const classNames = [styles.icon, isClicked ? styles.checked : '', className] - .join(' ') - .trim() - - return ( - - - - - - - ) - } - - const ClapCount = forwardRef( - ({ count, className = '', style: userStyles = {}, ...restProps }, ref) => { - const classNames = [styles.count, className].join(' ').trim() - - return ( - - +{count} - - ) - } - ) - - const CountTotal = forwardRef( - ( - { countTotal, className = '', style: userStyles = {}, ...restProps }, - ref - ) => { - const classNames = [styles.total, className].join(' ').trim() - - return ( - - {countTotal} - - ) - } - ) - - /** ==================================== - * 🔰USAGE - Below's how a potential user - may consume the component API - ==================================== **/ - - const initialState = { count: 10, countTotal: 22, isClicked: false } - const Usage = () => { - const [timesClapped, setTimeClapped] = useState(0) - const clappedTooMuch = timesClapped>= 7 - - const reducer = (state, action) => { - if (action.type === useClapState.types.clap && clappedTooMuch) { - return state - } - return useClapState.reducer(state, action) - } - - const { - clapState, - getTogglerProps, - getCounterProps, - reset, - resetDep - } = useClapState({ initialState, reducer }) - const { count, countTotal, isClicked } = clapState - - const [ - { clapContainerRef, clapCountRef, countTotalRef }, - setRef - ] = useDOMRef() - - const animationTimeline = useClapAnimation({ - duration: 300, - bounceEl: clapCountRef, - fadeEl: countTotalRef, - burstEl: clapContainerRef - }) - - const onClick = () => { - setTimeClapped(t => t + 1) - !clappedTooMuch && animationTimeline.replay() - } - - // Side effect after reset has occured. - const [uploadingReset, setUpload] = useState(false) - useEffectAfterMount( - () => { - setTimeClapped(0) - setUpload(true) - - const id = setTimeout(() => { - setUpload(false) - console.log('RESET COMPLETE!!!') - }, 3000) - - return () => clearTimeout(id) - }, - [resetDep] - ) - - return ( -
- - - - - -
- -
- {JSON.stringify({ timesClapped, count, countTotal })}
- 
-
- {uploadingReset ? `uploading reset ${resetDep}...` : ''}
- 
-
-
- {clappedTooMuch ? `You clapped too much. Don't be so generous!` : ''}
- 
-
- ) - } - - export default Usage \ No newline at end of file From 425ab0415557da06b2fd305a48d1fd00dba09efa Mon Sep 17 00:00:00 2001 From: Ohans Emmanuel Date: 2019年12月18日 06:20:22 +0100 Subject: [PATCH 02/41] cleanups --- showcase/src/constants/displayBoxNotes.js | 5 +- showcase/src/patterns/02.js | 241 ------------ showcase/src/patterns/03.js | 243 ------------ showcase/src/patterns/04.js | 309 ---------------- showcase/src/patterns/05.js | 342 ----------------- showcase/src/patterns/06.js | 409 --------------------- showcase/src/patterns/07.js | 323 ---------------- showcase/src/patterns/08.js | 345 ----------------- showcase/src/patterns/09.js | 400 -------------------- showcase/src/patterns/10.js | 427 ---------------------- 10 files changed, 1 insertion(+), 3043 deletions(-) delete mode 100644 showcase/src/patterns/02.js delete mode 100644 showcase/src/patterns/03.js delete mode 100644 showcase/src/patterns/04.js delete mode 100644 showcase/src/patterns/05.js delete mode 100644 showcase/src/patterns/06.js delete mode 100644 showcase/src/patterns/07.js delete mode 100644 showcase/src/patterns/08.js delete mode 100644 showcase/src/patterns/09.js delete mode 100644 showcase/src/patterns/10.js diff --git a/showcase/src/constants/displayBoxNotes.js b/showcase/src/constants/displayBoxNotes.js index 6553856..61c4eb1 100644 --- a/showcase/src/constants/displayBoxNotes.js +++ b/showcase/src/constants/displayBoxNotes.js @@ -1,4 +1 @@ -export const NOTES = { - '1': 'Animated via an HOC', - '2': 'Animated via a hook 💪' -} +export const NOTES = {} diff --git a/showcase/src/patterns/02.js b/showcase/src/patterns/02.js deleted file mode 100644 index fb18d7d..0000000 --- a/showcase/src/patterns/02.js +++ /dev/null @@ -1,241 +0,0 @@ -import React, { - useState, - useCallback, - useLayoutEffect, - useContext, - useMemo, - createContext -} from 'react' - -import mojs from 'mo-js' -import { generateRandomNumber } from '../utils/generateRandomNumber' -import styles from './index.css' - -/** ==================================== - * 🔰Hook - Hook for Animation - ==================================== **/ - -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: burstEl, - radius: { 50: 95 }, - count: 5, - angle: 30, - children: { - shape: 'polygon', - radius: { 6: 0 }, - scale: 1, - stroke: 'rgba(211,84,0 ,0.5)', - strokeWidth: 2, - angle: 210, - delay: 30, - speed: 0.2, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1), - duration: tlDuration - } - }) - - const circleBurst = new mojs.Burst({ - parent: burstEl, - radius: { 50: 75 }, - angle: 25, - duration: tlDuration, - children: { - shape: 'circle', - fill: 'rgba(149,165,166 ,0.5)', - delay: 30, - speed: 0.2, - radius: { 3: 0 }, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1) - } - }) - - const countAnimation = new mojs.Html({ - el: bounceEl, - isShowStart: false, - isShowEnd: true, - y: { 0: -30 }, - opacity: { 0: 1 }, - duration: tlDuration - }).then({ - opacity: { 1: 0 }, - y: -80, - delay: tlDuration / 2 - }) - - const countTotalAnimation = new mojs.Html({ - el: fadeEl, - isShowStart: false, - isShowEnd: true, - opacity: { 0: 1 }, - delay: (3 * tlDuration) / 2, - duration: tlDuration, - y: { 0: -3 } - }) - - const scaleButton = new mojs.Html({ - el: burstEl, - duration: tlDuration, - scale: { 1.3: 1 }, - easing: mojs.easing.out - }) - - 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 - ]) - - setAnimationTimeline(updatedAnimationTimeline) - }, - [tlDuration, animationTimeline, bounceEl, fadeEl, burstEl] - ) - - return animationTimeline -} -/** ==================================== - * 🔰 MediumClap - ==================================== **/ -const initialState = { - count: 0, - countTotal: generateRandomNumber(500, 10000), - isClicked: false -} - -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 = () => { - animationTimeline.replay() - - setClapState({ - count: Math.min(count + 1, MAXIMUM_USER_CLAP), - countTotal: count < MAXIMUM_USER_CLAP ? countTotal + 1 : countTotal, - isClicked: true - }) - } - - const memoizedValue = useMemo( - () => ({ - count, - countTotal, - isClicked, - setRef - }), - [count, countTotal, isClicked, setRef] - ) - - return ( - - - - ) -} - -/** ==================================== - * 🔰SubComponents - Smaller Component used by - ==================================== **/ - -const ClapIcon = () => { - const { isClicked } = useContext(MediumClapContext) - return ( - - - - - - - ) -} -const ClapCount = () => { - const { count, setRef } = useContext(MediumClapContext) - return ( - - +{count} - - ) -} -const CountTotal = () => { - const { countTotal, setRef } = useContext(MediumClapContext) - return ( - - {countTotal} - - ) -} - -/** ==================================== - * 🔰USAGE - Below's how a potential user - may consume the component API - ==================================== **/ - -const Usage = () => { - return -} - -export default Usage diff --git a/showcase/src/patterns/03.js b/showcase/src/patterns/03.js deleted file mode 100644 index 2809f8f..0000000 --- a/showcase/src/patterns/03.js +++ /dev/null @@ -1,243 +0,0 @@ -import React, { - useState, - useEffect, - useContext, - useMemo, - useRef, - createContext -} from 'react' - -import mojs from 'mo-js' -import wordConverter from 'number-to-words' -import { generateRandomNumber } from '../utils/generateRandomNumber' -import styles from './index.css' - -/** ==================================== - * 🔰Hook - Hook for Animation -==================================== **/ - -const useClapAnimation = ({ duration: tlDuration }) => { - const [animationTimeline, setAnimationTimeline] = useState( - new mojs.Timeline() - ) - - useEffect( - () => { - const triangleBurst = new mojs.Burst({ - parent: '#clap', - radius: { 50: 95 }, - count: 5, - angle: 30, - children: { - shape: 'polygon', - radius: { 6: 0 }, - scale: 1, - stroke: 'rgba(211,84,0 ,0.5)', - strokeWidth: 2, - angle: 210, - delay: 30, - speed: 0.2, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1), - duration: tlDuration - } - }) - - const circleBurst = new mojs.Burst({ - parent: '#clap', - radius: { 50: 75 }, - angle: 25, - duration: tlDuration, - children: { - shape: 'circle', - fill: 'rgba(149,165,166 ,0.5)', - delay: 30, - speed: 0.2, - radius: { 3: 0 }, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1) - } - }) - - const countAnimation = new mojs.Html({ - el: '#clapCount', - isShowStart: false, - isShowEnd: true, - y: { 0: -30 }, - opacity: { 0: 1 }, - duration: tlDuration - }).then({ - opacity: { 1: 0 }, - y: -80, - delay: tlDuration / 2 - }) - - const countTotalAnimation = new mojs.Html({ - el: '#clapCountTotal', - isShowStart: false, - isShowEnd: true, - opacity: { 0: 1 }, - delay: (3 * tlDuration) / 2, - duration: tlDuration, - y: { 0: -3 } - }) - - const scaleButton = new mojs.Html({ - el: '#clap', - duration: tlDuration, - scale: { 1.3: 1 }, - easing: mojs.easing.out - }) - - const clap = document.getElementById('clap') - clap.style.transform = 'scale(1, 1)' - - const updatedAnimationTimeline = animationTimeline.add([ - countAnimation, - countTotalAnimation, - scaleButton, - circleBurst, - triangleBurst - ]) - - setAnimationTimeline(updatedAnimationTimeline) - }, - [tlDuration, animationTimeline] - ) - - return animationTimeline -} -/** ==================================== - * 🔰 MediumClap -==================================== **/ -const initialState = { - count: 0, - countTotal: generateRandomNumber(500, 10000), - isClicked: false -} - -const MediumClapContext = createContext() -const { Provider } = MediumClapContext - -const MediumClap = ({ children, onClap }) => { - const MAXIMUM_USER_CLAP = 50 - const [clapState, setClapState] = useState(initialState) - const { count, countTotal, isClicked } = clapState - - const animationTimeline = useClapAnimation({ duration: 300 }) - const handleClapClick = () => { - // 👉 prop from HOC - animationTimeline.replay() - - setClapState({ - count: Math.min(count + 1, MAXIMUM_USER_CLAP), - countTotal: count < MAXIMUM_USER_CLAP ? countTotal + 1 : countTotal, - isClicked: true - }) - } - - const componentJustMounted = useRef(true) - - useEffect( - () => { - if (!componentJustMounted.current) { - onClap(clapState) - } - componentJustMounted.current = false - }, - [count, onClap] - ) - - const memoizedValue = useMemo( - () => ({ - count, - countTotal, - isClicked - }), - [count, countTotal, isClicked] - ) - - return ( - - - - ) -} - -/** ==================================== - * 🔰SubComponents -Smaller Component used by -==================================== **/ - -const ClapIcon = () => { - const { isClicked } = useContext(MediumClapContext) - return ( - - - - - - - ) -} -const ClapCount = () => { - const { count } = useContext(MediumClapContext) - return ( - - +{count} - - ) -} -const CountTotal = () => { - const { countTotal } = useContext(MediumClapContext) - return ( - - {countTotal} - - ) -} - -const ClapInfo = ({ info }) => { - const { countTotal } = useContext(MediumClapContext) - return ( -
- {info || wordConverter.toWords(countTotal)} claps! -
- ) -} - -MediumClap.Icon = ClapIcon -MediumClap.Count = ClapCount -MediumClap.Total = CountTotal -MediumClap.Info = ClapInfo - -/** ==================================== - * 🔰USAGE - Below's how a potential user - may consume the component API -==================================== **/ - -const Usage = () => { - const [total, setTotal] = useState(0) - - const onClap = ({ countTotal }) => { - setTotal(countTotal) - } - - return ( - - - - - - - ) -} - -export default Usage diff --git a/showcase/src/patterns/04.js b/showcase/src/patterns/04.js deleted file mode 100644 index 4dabdcc..0000000 --- a/showcase/src/patterns/04.js +++ /dev/null @@ -1,309 +0,0 @@ -import React, { - useState, - useEffect, - useCallback, - useLayoutEffect, - useContext, - useMemo, - useRef, - createContext -} from 'react' - -import mojs from 'mo-js' -import wordConverter from 'number-to-words' -import { generateRandomNumber } from '../utils/generateRandomNumber' -import styles from './index.css' -import userStyles from './usage.css' - -/** ==================================== - * 🔰Hook - Hook for Animation -==================================== **/ - -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: burstEl, - radius: { 50: 95 }, - count: 5, - angle: 30, - children: { - shape: 'polygon', - radius: { 6: 0 }, - scale: 1, - stroke: 'rgba(211,84,0 ,0.5)', - strokeWidth: 2, - angle: 210, - delay: 30, - speed: 0.2, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1), - duration: tlDuration - } - }) - - const circleBurst = new mojs.Burst({ - parent: burstEl, - radius: { 50: 75 }, - angle: 25, - duration: tlDuration, - children: { - shape: 'circle', - fill: 'rgba(149,165,166 ,0.5)', - delay: 30, - speed: 0.2, - radius: { 3: 0 }, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1) - } - }) - - const countAnimation = new mojs.Html({ - el: bounceEl, - isShowStart: false, - isShowEnd: true, - y: { 0: -30 }, - opacity: { 0: 1 }, - duration: tlDuration - }).then({ - opacity: { 1: 0 }, - y: -80, - delay: tlDuration / 2 - }) - - const countTotalAnimation = new mojs.Html({ - el: fadeEl, - isShowStart: false, - isShowEnd: true, - opacity: { 0: 1 }, - delay: (3 * tlDuration) / 2, - duration: tlDuration, - y: { 0: -3 } - }) - - const scaleButton = new mojs.Html({ - el: burstEl, - duration: tlDuration, - scale: { 1.3: 1 }, - easing: mojs.easing.out - }) - - 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 - ]) - - setAnimationTimeline(updatedAnimationTimeline) - }, - [tlDuration, animationTimeline, bounceEl, fadeEl, burstEl] - ) - - return animationTimeline -} -/** ==================================== - * 🔰 MediumClap -==================================== **/ -const initialState = { - count: 0, - countTotal: generateRandomNumber(500, 10000), - isClicked: false -} - -const MediumClapContext = createContext() -const { Provider } = MediumClapContext - -const MediumClap = ({ - children, - onClap, - className = '', - style: userStyles = {} -}) => { - 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 = () => { - animationTimeline.replay() - - setClapState({ - count: Math.min(count + 1, MAXIMUM_USER_CLAP), - countTotal: count < MAXIMUM_USER_CLAP ? countTotal + 1 : countTotal, - isClicked: true - }) - } - - const componentJustMounted = useRef(true) - - useEffect( - () => { - if (!componentJustMounted.current) { - onClap(clapState) - } - componentJustMounted.current = false - }, - [count, onClap] - ) - - const memoizedValue = useMemo( - () => ({ - count, - countTotal, - isClicked, - setRef - }), - [count, countTotal, isClicked, setRef] - ) - - const classNames = [styles.clap, className].join(' ').trim() - - return ( - - - - ) -} - -/** ==================================== - * 🔰SubComponents -Smaller Component used by -==================================== **/ - -const ClapIcon = ({ className = '', style: userStyles = {} }) => { - const { isClicked } = useContext(MediumClapContext) - const classNames = [styles.icon, isClicked ? styles.checked : '', className] - .join(' ') - .trim() - - return ( - - - - - - - ) -} -const ClapCount = ({ className = '', style: userStyles = {} }) => { - const { count, setRef } = useContext(MediumClapContext) - const classNames = [styles.count, className].join(' ').trim() - - return ( - - +{count} - - ) -} -const CountTotal = ({ className = '', style: userStyles = {} }) => { - const { countTotal, setRef } = useContext(MediumClapContext) - const classNames = [styles.total, className].join(' ').trim() - - return ( - - {countTotal} - - ) -} - -const ClapInfo = ({ info }) => { - const { countTotal } = useContext(MediumClapContext) - return ( -
- {info || wordConverter.toWords(countTotal)} claps! -
- ) -} - -MediumClap.Icon = ClapIcon -MediumClap.Count = ClapCount -MediumClap.Total = CountTotal -MediumClap.Info = ClapInfo - -/** ==================================== - * 🔰USAGE - Below's how a potential user - may consume the component API -==================================== **/ - -const Usage = () => { - const [total, setTotal] = useState(0) - - const onClap = ({ countTotal }) => { - setTotal(countTotal) - } - - return ( - - - - - - - ) -} - -export default Usage diff --git a/showcase/src/patterns/05.js b/showcase/src/patterns/05.js deleted file mode 100644 index 09737b7..0000000 --- a/showcase/src/patterns/05.js +++ /dev/null @@ -1,342 +0,0 @@ -import React, { - useState, - useEffect, - useCallback, - useLayoutEffect, - useContext, - useMemo, - useRef, - createContext -} from 'react' - -import mojs from 'mo-js' -import wordConverter from 'number-to-words' -import { generateRandomNumber } from '../utils/generateRandomNumber' -import styles from './index.css' -import userStyles from './usage.css' - -/** ==================================== - * 🔰Hook - Hook for Animation - ==================================== **/ - -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: burstEl, - radius: { 50: 95 }, - count: 5, - angle: 30, - children: { - shape: 'polygon', - radius: { 6: 0 }, - scale: 1, - stroke: 'rgba(211,84,0 ,0.5)', - strokeWidth: 2, - angle: 210, - delay: 30, - speed: 0.2, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1), - duration: tlDuration - } - }) - - const circleBurst = new mojs.Burst({ - parent: burstEl, - radius: { 50: 75 }, - angle: 25, - duration: tlDuration, - children: { - shape: 'circle', - fill: 'rgba(149,165,166 ,0.5)', - delay: 30, - speed: 0.2, - radius: { 3: 0 }, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1) - } - }) - - const countAnimation = new mojs.Html({ - el: bounceEl, - isShowStart: false, - isShowEnd: true, - y: { 0: -30 }, - opacity: { 0: 1 }, - duration: tlDuration - }).then({ - opacity: { 1: 0 }, - y: -80, - delay: tlDuration / 2 - }) - - const countTotalAnimation = new mojs.Html({ - el: fadeEl, - isShowStart: false, - isShowEnd: true, - opacity: { 0: 1 }, - delay: (3 * tlDuration) / 2, - duration: tlDuration, - y: { 0: -3 } - }) - - const scaleButton = new mojs.Html({ - el: burstEl, - duration: tlDuration, - scale: { 1.3: 1 }, - easing: mojs.easing.out - }) - - if (typeof burstEl === 'string') { - const id = burstEl.slice(1, burstEl.length) - 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 - ]) - - setAnimationTimeline(updatedAnimationTimeline) - }, - [tlDuration, animationTimeline, bounceEl, fadeEl, burstEl] - ) - - return animationTimeline -} -/** ==================================== - * 🔰 MediumClap - ==================================== **/ -const initialState = { - count: 0, - countTotal: generateRandomNumber(500, 10000), - isClicked: false -} - -const MediumClapContext = createContext() -const { Provider } = MediumClapContext - -const MediumClap = ({ - children, - onClap = () => {}, - values = null, - className = '', - style: userStyles = {} -}) => { - 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 - }) - - // Controlled Component ? isControlled = value !== undefined - const isControlled = !!values - - const handleClapClick = () => { - // 👉 prop from HOC - animationTimeline.replay() - isControlled - ? onClap() - : setClapState({ - count: Math.min(count + 1, MAXIMUM_USER_CLAP), - countTotal: count < MAXIMUM_USER_CLAP ? countTotal + 1 : countTotal, - isClicked: true - }) - } - - const componentJustMounted = useRef(true) - - useEffect( - () => { - if (!componentJustMounted.current && !isControlled) { - onClap(clapState) - } - componentJustMounted.current = false - }, - [count, onClap, isControlled] - ) - - const getState = useCallback(() => (isControlled ? values : clapState), [ - isControlled, - clapState, - values - ]) - - const memoizedValue = useMemo( - () => { - return { - ...getState(), - setRef - } - }, - [getState, setRef] - ) - - const classNames = [styles.clap, className].join(' ').trim() - - return ( - - - - ) -} - -/** ==================================== - * 🔰SubComponents - Smaller Component used by - ==================================== **/ - -const ClapIcon = ({ className = '', style: userStyles = {} }) => { - const { isClicked } = useContext(MediumClapContext) - const classNames = [styles.icon, isClicked ? styles.checked : '', className] - .join(' ') - .trim() - - return ( - - - - - - - ) -} -const ClapCount = ({ className = '', style: userStyles = {} }) => { - const { count, setRef } = useContext(MediumClapContext) - const classNames = [styles.count, className].join(' ').trim() - - return ( - - +{count} - - ) -} -const CountTotal = ({ className = '', style: userStyles = {} }) => { - const { countTotal, setRef } = useContext(MediumClapContext) - const classNames = [styles.total, className].join(' ').trim() - - return ( - - {countTotal} - - ) -} - -const ClapInfo = ({ info }) => { - const { countTotal } = useContext(MediumClapContext) - return ( -
- {info || wordConverter.toWords(countTotal)} claps! -
- ) -} - -MediumClap.Icon = ClapIcon -MediumClap.Count = ClapCount -MediumClap.Total = CountTotal -MediumClap.Info = ClapInfo - -/** ==================================== - * 🔰USAGE - Below's how a potential user - may consume the component API - ==================================== **/ - -const MAXIMUM_USER_CLAP = 10 -const INITIAL_STATE = { - count: 0, - countTotal: 1206, - isClicked: false -} - -const Usage = () => { - const [clapValues, setClapValues] = useState(INITIAL_STATE) - - const onClap = () => { - setClapValues(({ count, countTotal }) => ({ - count: Math.min(count + 1, MAXIMUM_USER_CLAP), - countTotal: count < MAXIMUM_USER_CLAP ? countTotal + 1 : countTotal, - isClicked: true - })) - } - - return ( -
- - - - - - - - - - - -
- ) -} - -export default Usage diff --git a/showcase/src/patterns/06.js b/showcase/src/patterns/06.js deleted file mode 100644 index 6e745df..0000000 --- a/showcase/src/patterns/06.js +++ /dev/null @@ -1,409 +0,0 @@ -import React, { - useState, - useEffect, - useCallback, - useLayoutEffect, - forwardRef, - useRef - } from 'react' - - import mojs from 'mo-js' - import { generateRandomNumber } from '../utils/generateRandomNumber' - import styles from './index.css' - import userStyles from './usage.css' - - /** ==================================== - * 🔰Hook - Hook for Animation - ==================================== **/ - - 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: burstEl, - radius: { 50: 95 }, - count: 5, - angle: 30, - children: { - shape: 'polygon', - radius: { 6: 0 }, - scale: 1, - stroke: 'rgba(211,84,0 ,0.5)', - strokeWidth: 2, - angle: 210, - delay: 30, - speed: 0.2, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1), - duration: tlDuration - } - }) - - const circleBurst = new mojs.Burst({ - parent: burstEl, - radius: { 50: 75 }, - angle: 25, - duration: tlDuration, - children: { - shape: 'circle', - fill: 'rgba(149,165,166 ,0.5)', - delay: 30, - speed: 0.2, - radius: { 3: 0 }, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1) - } - }) - - const countAnimation = new mojs.Html({ - el: bounceEl, - isShowStart: false, - isShowEnd: true, - y: { 0: -30 }, - opacity: { 0: 1 }, - duration: tlDuration - }).then({ - opacity: { 1: 0 }, - y: -80, - delay: tlDuration / 2 - }) - - const countTotalAnimation = new mojs.Html({ - el: fadeEl, - isShowStart: false, - isShowEnd: true, - opacity: { 0: 1 }, - delay: (3 * tlDuration) / 2, - duration: tlDuration, - y: { 0: -3 } - }) - - const scaleButton = new mojs.Html({ - el: burstEl, - duration: tlDuration, - scale: { 1.3: 1 }, - easing: mojs.easing.out - }) - - if (typeof burstEl === 'string') { - const id = burstEl.slice(1, burstEl.length) - 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 - ]) - - setAnimationTimeline(updatedAnimationTimeline) - }, - [tlDuration, animationTimeline, bounceEl, fadeEl, burstEl] - ) - - return animationTimeline - } - - /** ==================================== - * 🔰Hook - Hook for Clap State - ==================================== **/ - const MAX_CLAP = 50 - const INIT_STATE = { - count: 0, - countTotal: generateRandomNumber(500, 10000), - isClicked: false - } - - const useClapState = ({ initialState = INIT_STATE } = {}) => { - const [clapState, setClapState] = useState(initialState) - const { count, countTotal } = clapState - - const handleClapClick = useCallback( - () => { - setClapState({ - count: Math.min(count + 1, MAX_CLAP), - countTotal: count < MAX_CLAP ? countTotal + 1 : countTotal, - isClicked: true - }) - }, - [count, countTotal] - ) - - return { - clapState, - handleClapClick - } - } - - /** ==================================== - * 🔰Hook - useEffectAfterMount - ==================================== **/ - - function useEffectAfterMount (cb, deps) { - const componentJustMounted = useRef(true) - useEffect(() => { - if (!componentJustMounted.current) { - return cb() - } - componentJustMounted.current = false - // eslint-disable-next-line react-hooks/exhaustive-deps - }, deps) - } - - /** ==================================== - * 🔰Hook - useDOMRef - ==================================== **/ - const useDOMRef = () => { - const [DOMRef, setDOMRef] = useState({}) - const setRef = useCallback(node => { - if (node !== null) { - setDOMRef(prevDOMRefs => ({ - ...prevDOMRefs, - [node.dataset.refkey]: node - })) - } - }, []) - - return [DOMRef, setRef] - } - - /** ==================================== - * 🔰 MediumClap - ==================================== **/ - - const MediumClap = () => { - const { clapState, handleClapClick } = useClapState() - const { count, countTotal, isClicked } = clapState - - const [ - { clapContainerRef, clapCountRef, countTotalRef }, - setRef - ] = useDOMRef() - - const animationTimeline = useClapAnimation({ - duration: 300, - bounceEl: clapCountRef, - fadeEl: countTotalRef, - burstEl: clapContainerRef - }) - - useEffectAfterMount( - () => { - animationTimeline.replay() - }, - [count] - ) - - return ( - - - - - - ) - } - - /** ==================================== - * 🔰SubComponents - Smaller Component used by - ==================================== **/ - - const ClapContainer = forwardRef( - ( - { children, handleClick, className, style: userStyles = {}, ...restProps }, - ref - ) => { - const classNames = [styles.clap, className].join(' ').trim() - - return ( - - ) - } - ) - - const ClapIcon = ({ className = '', style: userStyles = {}, isClicked }) => { - const classNames = [styles.icon, isClicked ? styles.checked : '', className] - .join(' ') - .trim() - - return ( - - - - - - - ) - } - - const ClapCount = forwardRef( - ({ count, className = '', style: userStyles = {}, ...restProps }, ref) => { - const classNames = [styles.count, className].join(' ').trim() - - return ( - - +{count} - - ) - } - ) - - const CountTotal = forwardRef( - ( - { countTotal, className = '', style: userStyles = {}, ...restProps }, - ref - ) => { - const classNames = [styles.total, className].join(' ').trim() - - return ( - - {countTotal} - - ) - } - ) - - /** ==================================== - * 🔰USAGE - Below's how a potential user - may consume the component API - ==================================== **/ - - const CupBowl = () => { - // Credit: Created by Kieu Thi Kim Cuong from the Noun Project - return ( - - - - - - ) - } - - const CupHandle = () => { - // Credit: Created by Kieu Thi Kim Cuong from the Noun Project - return ( - - - - - - ) - } - - const Stream = () => { - // Credit: Created by Kieu Thi Kim Cuong from the Noun Project - return ( - - - - - - ) - } - - const CupBase = () => { - // Credit: Created by Kieu Thi Kim Cuong from the Noun Project - return ( - - - - - - ) - } - - const Usage = () => { - const animationTimeline = useClapAnimation({ - duration: 300, - bounceEl: '#stream', - fadeEl: '#cupHandle', - burstEl: '#coffee' - }) - - const handleClick = () => { - animationTimeline.replay() - } - - return ( -
-
- -
-
- coffee -
-
- - -
-
- -
-
- -
-
- ) - } - - export default Usage \ No newline at end of file diff --git a/showcase/src/patterns/07.js b/showcase/src/patterns/07.js deleted file mode 100644 index e1ea07e..0000000 --- a/showcase/src/patterns/07.js +++ /dev/null @@ -1,323 +0,0 @@ -import React, { - useState, - useEffect, - useCallback, - useLayoutEffect, - forwardRef, - useRef -} from 'react' - -import mojs from 'mo-js' -import { generateRandomNumber } from '../utils/generateRandomNumber' -import styles from './index.css' -import userStyles from './usage.css' - -/** ==================================== - * 🔰Hook - Hook for Animation - ==================================== **/ - -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: burstEl, - radius: { 50: 95 }, - count: 5, - angle: 30, - children: { - shape: 'polygon', - radius: { 6: 0 }, - scale: 1, - stroke: 'rgba(211,84,0 ,0.5)', - strokeWidth: 2, - angle: 210, - delay: 30, - speed: 0.2, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1), - duration: tlDuration - } - }) - - const circleBurst = new mojs.Burst({ - parent: burstEl, - radius: { 50: 75 }, - angle: 25, - duration: tlDuration, - children: { - shape: 'circle', - fill: 'rgba(149,165,166 ,0.5)', - delay: 30, - speed: 0.2, - radius: { 3: 0 }, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1) - } - }) - - const countAnimation = new mojs.Html({ - el: bounceEl, - isShowStart: false, - isShowEnd: true, - y: { 0: -30 }, - opacity: { 0: 1 }, - duration: tlDuration - }).then({ - opacity: { 1: 0 }, - y: -80, - delay: tlDuration / 2 - }) - - const countTotalAnimation = new mojs.Html({ - el: fadeEl, - isShowStart: false, - isShowEnd: true, - opacity: { 0: 1 }, - delay: (3 * tlDuration) / 2, - duration: tlDuration, - y: { 0: -3 } - }) - - const scaleButton = new mojs.Html({ - el: burstEl, - duration: tlDuration, - scale: { 1.3: 1 }, - easing: mojs.easing.out - }) - - if (typeof burstEl === 'string') { - const id = burstEl.slice(1, burstEl.length) - 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 - ]) - - setAnimationTimeline(updatedAnimationTimeline) - }, - [tlDuration, animationTimeline, bounceEl, fadeEl, burstEl] - ) - - return animationTimeline -} - -/** ==================================== - * 🔰Hook - Hook for Clap State - ==================================== **/ -const MAX_CLAP = 50 -const INIT_STATE = { - count: 0, - countTotal: generateRandomNumber(500, 10000), - isClicked: false -} - -const useClapState = ({ initialState = INIT_STATE } = {}) => { - const [clapState, setClapState] = useState(initialState) - const { count, countTotal } = clapState - - const handleClapClick = useCallback( - () => { - setClapState({ - count: Math.min(count + 1, MAX_CLAP), - countTotal: count < MAX_CLAP ? countTotal + 1 : countTotal, - isClicked: true - }) - }, - [count, countTotal] - ) - - const togglerProps = { - onClick: handleClapClick, - 'aria-pressed': clapState.isClicked - } - - const counterProps = { - count, - 'aria-valuemax': MAX_CLAP, - 'aria-valuemin': 0, - 'aria-valuenow': count - } - - return { - clapState, - togglerProps, - counterProps - } -} - -/** ==================================== - * 🔰Hook - useEffectAfterMount - ==================================== **/ - -function useEffectAfterMount (cb, deps) { - const componentJustMounted = useRef(true) - useEffect(() => { - if (!componentJustMounted.current) { - return cb() - } - componentJustMounted.current = false - // eslint-disable-next-line react-hooks/exhaustive-deps - }, deps) -} - -/** ==================================== - * 🔰Hook - useDOMRef - ==================================== **/ -const useDOMRef = () => { - const [DOMRef, setDOMRef] = useState({}) - const setRef = useCallback(node => { - if (node !== null) { - setDOMRef(prevDOMRefs => ({ - ...prevDOMRefs, - [node.dataset.refkey]: node - })) - } - }, []) - - return [DOMRef, setRef] -} - -/** ==================================== - * 🔰SubComponents - Smaller Component used by - ==================================== **/ - -const ClapContainer = forwardRef( - ( - { children, handleClick, className, style: userStyles = {}, ...restProps }, - ref - ) => { - const classNames = [styles.clap, className].join(' ').trim() - - return ( - - ) - } -) - -const ClapIcon = ({ className = '', style: userStyles = {}, isClicked }) => { - const classNames = [styles.icon, isClicked ? styles.checked : '', className] - .join(' ') - .trim() - - return ( - - - - - - - ) -} - -const ClapCount = forwardRef( - ({ count, className = '', style: userStyles = {}, ...restProps }, ref) => { - const classNames = [styles.count, className].join(' ').trim() - - return ( - - +{count} - - ) - } -) - -const CountTotal = forwardRef( - ( - { countTotal, className = '', style: userStyles = {}, ...restProps }, - ref - ) => { - const classNames = [styles.total, className].join(' ').trim() - - return ( - - {countTotal} - - ) - } -) - -/** ==================================== - * 🔰USAGE - Below's how a potential user - may consume the component API - ==================================== **/ - -const Usage = () => { - const { clapState, togglerProps, counterProps } = useClapState() - const { count, countTotal, isClicked } = clapState - - const [ - { clapContainerRef, clapCountRef, countTotalRef }, - setRef - ] = useDOMRef() - - const animationTimeline = useClapAnimation({ - duration: 300, - bounceEl: clapCountRef, - fadeEl: countTotalRef, - burstEl: clapContainerRef - }) - - useEffectAfterMount( - () => { - animationTimeline.replay() - }, - [count] - ) - - return ( - - - - - - ) -} - -export default Usage diff --git a/showcase/src/patterns/08.js b/showcase/src/patterns/08.js deleted file mode 100644 index be75d84..0000000 --- a/showcase/src/patterns/08.js +++ /dev/null @@ -1,345 +0,0 @@ -import React, { - useState, - useEffect, - useCallback, - useLayoutEffect, - forwardRef, - useRef -} from 'react' - -import mojs from 'mo-js' -import { generateRandomNumber } from '../utils/generateRandomNumber' -import styles from './index.css' -import userStyles from './usage.css' - -/** ==================================== - * 🔰Hook - Hook for Animation - ==================================== **/ - -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: burstEl, - radius: { 50: 95 }, - count: 5, - angle: 30, - children: { - shape: 'polygon', - radius: { 6: 0 }, - scale: 1, - stroke: 'rgba(211,84,0 ,0.5)', - strokeWidth: 2, - angle: 210, - delay: 30, - speed: 0.2, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1), - duration: tlDuration - } - }) - - const circleBurst = new mojs.Burst({ - parent: burstEl, - radius: { 50: 75 }, - angle: 25, - duration: tlDuration, - children: { - shape: 'circle', - fill: 'rgba(149,165,166 ,0.5)', - delay: 30, - speed: 0.2, - radius: { 3: 0 }, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1) - } - }) - - const countAnimation = new mojs.Html({ - el: bounceEl, - isShowStart: false, - isShowEnd: true, - y: { 0: -30 }, - opacity: { 0: 1 }, - duration: tlDuration - }).then({ - opacity: { 1: 0 }, - y: -80, - delay: tlDuration / 2 - }) - - const countTotalAnimation = new mojs.Html({ - el: fadeEl, - isShowStart: false, - isShowEnd: true, - opacity: { 0: 1 }, - delay: (3 * tlDuration) / 2, - duration: tlDuration, - y: { 0: -3 } - }) - - const scaleButton = new mojs.Html({ - el: burstEl, - duration: tlDuration, - scale: { 1.3: 1 }, - easing: mojs.easing.out - }) - - if (typeof burstEl === 'string') { - const id = burstEl.slice(1, burstEl.length) - 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 - ]) - - setAnimationTimeline(updatedAnimationTimeline) - }, - [tlDuration, animationTimeline, bounceEl, fadeEl, burstEl] - ) - - return animationTimeline -} - -/** ==================================== - * 🔰Hook - Hook for Clap State - ==================================== **/ -const MAX_CLAP = 50 -const INIT_STATE = { - count: 0, - countTotal: generateRandomNumber(500, 10000), - isClicked: false -} - -const callFnsInSequence = (...fns) => (...args) => - fns.forEach(fn => fn && fn(...args)) - -const useClapState = ({ initialState = INIT_STATE } = {}) => { - const [clapState, setClapState] = useState(initialState) - const { count, countTotal } = clapState - - const handleClapClick = useCallback( - () => { - setClapState({ - count: Math.min(count + 1, MAX_CLAP), - countTotal: count < MAX_CLAP ? countTotal + 1 : countTotal, - isClicked: true - }) - }, - [count, countTotal] - ) - - const getTogglerProps = ({ onClick, ...otherProps } = {}) => ({ - onClick: callFnsInSequence(handleClapClick, onClick), - 'aria-pressed': clapState.isClicked, - ...otherProps - }) - - const getCounterProps = ({ ...otherProps }) => ({ - count, - 'aria-valuemax': MAX_CLAP, - 'aria-valuemin': 0, - 'aria-valuenow': count, - ...otherProps - }) - - return { - clapState, - getTogglerProps, - getCounterProps - } -} - -/** ==================================== - * 🔰Hook - useEffectAfterMount - ==================================== **/ - -function useEffectAfterMount (cb, deps) { - const componentJustMounted = useRef(true) - useEffect(() => { - if (!componentJustMounted.current) { - return cb() - } - componentJustMounted.current = false - // eslint-disable-next-line react-hooks/exhaustive-deps - }, deps) -} - -/** ==================================== - * 🔰Hook - useDOMRef - ==================================== **/ -const useDOMRef = () => { - const [DOMRef, setDOMRef] = useState({}) - const setRef = useCallback(node => { - if (node !== null) { - setDOMRef(prevDOMRefs => ({ - ...prevDOMRefs, - [node.dataset.refkey]: node - })) - } - }, []) - - return [DOMRef, setRef] -} - -/** ==================================== - * 🔰SubComponents - Smaller Component used by - ==================================== **/ - -const ClapContainer = forwardRef( - ( - { children, handleClick, className, style: userStyles = {}, ...restProps }, - ref - ) => { - const classNames = [styles.clap, className].join(' ').trim() - - return ( - - ) - } -) - -const ClapIcon = ({ className = '', style: userStyles = {}, isClicked }) => { - const classNames = [styles.icon, isClicked ? styles.checked : '', className] - .join(' ') - .trim() - - return ( - - - - - - - ) -} - -const ClapCount = forwardRef( - ({ count, className = '', style: userStyles = {}, ...restProps }, ref) => { - const classNames = [styles.count, className].join(' ').trim() - - return ( - - +{count} - - ) - } -) - -const CountTotal = forwardRef( - ( - { countTotal, className = '', style: userStyles = {}, ...restProps }, - ref - ) => { - const classNames = [styles.total, className].join(' ').trim() - - return ( - - {countTotal} - - ) - } -) - -/** ==================================== - * 🔰USAGE - Below's how a potential user - may consume the component API - ==================================== **/ - -const Usage = () => { - const { clapState, getTogglerProps, getCounterProps } = useClapState() - const { count, countTotal, isClicked } = clapState - - const [ - { clapContainerRef, clapCountRef, countTotalRef }, - setRef - ] = useDOMRef() - - const animationTimeline = useClapAnimation({ - duration: 300, - bounceEl: clapCountRef, - fadeEl: countTotalRef, - burstEl: clapContainerRef - }) - - const onClick = () => { - animationTimeline.replay() - } - - return ( -
- - - - - -
- {!isClicked ? 'No recommendations :(' : `Recommended ${count} times 🔥`} -
-
- ) -} - -export default Usage diff --git a/showcase/src/patterns/09.js b/showcase/src/patterns/09.js deleted file mode 100644 index b515497..0000000 --- a/showcase/src/patterns/09.js +++ /dev/null @@ -1,400 +0,0 @@ -import React, { - useState, - useEffect, - useCallback, - useLayoutEffect, - forwardRef, - useRef -} from 'react' - -import mojs from 'mo-js' -import { generateRandomNumber } from '../utils/generateRandomNumber' -import styles from './index.css' -import userStyles from './usage.css' - -/** ==================================== - * 🔰Hook - Hook for Holding Previous Vals - ==================================== **/ -function usePrevious (value) { - const ref = useRef() - useEffect(() => { - ref.current = value - }) - return ref.current === undefined ? null : ref.current -} - -/** ==================================== - * 🔰Hook - Hook for Animation - ==================================== **/ - -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: burstEl, - radius: { 50: 95 }, - count: 5, - angle: 30, - children: { - shape: 'polygon', - radius: { 6: 0 }, - scale: 1, - stroke: 'rgba(211,84,0 ,0.5)', - strokeWidth: 2, - angle: 210, - delay: 30, - speed: 0.2, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1), - duration: tlDuration - } - }) - - const circleBurst = new mojs.Burst({ - parent: burstEl, - radius: { 50: 75 }, - angle: 25, - duration: tlDuration, - children: { - shape: 'circle', - fill: 'rgba(149,165,166 ,0.5)', - delay: 30, - speed: 0.2, - radius: { 3: 0 }, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1) - } - }) - - const countAnimation = new mojs.Html({ - el: bounceEl, - isShowStart: false, - isShowEnd: true, - y: { 0: -30 }, - opacity: { 0: 1 }, - duration: tlDuration - }).then({ - opacity: { 1: 0 }, - y: -80, - delay: tlDuration / 2 - }) - - const countTotalAnimation = new mojs.Html({ - el: fadeEl, - isShowStart: false, - isShowEnd: true, - opacity: { 0: 1 }, - delay: (3 * tlDuration) / 2, - duration: tlDuration, - y: { 0: -3 } - }) - - const scaleButton = new mojs.Html({ - el: burstEl, - duration: tlDuration, - scale: { 1.3: 1 }, - easing: mojs.easing.out - }) - - if (typeof burstEl === 'string') { - const id = burstEl.slice(1, burstEl.length) - 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 - ]) - - setAnimationTimeline(updatedAnimationTimeline) - }, - [tlDuration, animationTimeline, bounceEl, fadeEl, burstEl] - ) - - return animationTimeline -} - -/** ==================================== - * 🔰Hook - Hook for Clap State - ==================================== **/ -const MAX_CLAP = 50 -const INIT_STATE = { - count: 0, - countTotal: generateRandomNumber(500, 10000), - isClicked: false -} - -const callFnsInSequence = (...fns) => (...args) => - fns.forEach(fn => fn && fn(...args)) - -const useClapState = ({ initialState = INIT_STATE } = {}) => { - const initialStateRef = useRef(initialState) - const [clapState, setClapState] = useState(initialStateRef.current) - const { count, countTotal } = clapState - - const handleClapClick = useCallback( - () => { - setClapState({ - count: Math.min(count + 1, MAX_CLAP), - countTotal: count < MAX_CLAP ? countTotal + 1 : countTotal, - isClicked: true - }) - }, - [count, countTotal] - ) - - const resetRef = useRef(0) - // reset only if there's a change. It's possible to check changes to other state values e.g. countTotal & isClicked - const prevCount = usePrevious(count) - const reset = useCallback( - () => { - if (prevCount !== count) { - setClapState(initialStateRef.current) - ++resetRef.current - } - }, - [prevCount, count] - ) - - const getTogglerProps = ({ onClick, ...otherProps } = {}) => ({ - onClick: callFnsInSequence(handleClapClick, onClick), - 'aria-pressed': clapState.isClicked, - ...otherProps - }) - - const getCounterProps = ({ ...otherProps }) => ({ - count, - 'aria-valuemax': MAX_CLAP, - 'aria-valuemin': 0, - 'aria-valuenow': count, - ...otherProps - }) - - return { - clapState, - getTogglerProps, - getCounterProps, - reset, - resetDep: resetRef.current - } -} - -/** ==================================== - * 🔰Hook - useEffectAfterMount - ==================================== **/ - -function useEffectAfterMount (cb, deps) { - const componentJustMounted = useRef(true) - useEffect(() => { - if (!componentJustMounted.current) { - return cb() - } - componentJustMounted.current = false - // eslint-disable-next-line react-hooks/exhaustive-deps - }, deps) -} - -/** ==================================== - * 🔰Hook - useDOMRef - ==================================== **/ -const useDOMRef = () => { - const [DOMRef, setDOMRef] = useState({}) - const setRef = useCallback(node => { - if (node !== null) { - setDOMRef(prevDOMRefs => ({ - ...prevDOMRefs, - [node.dataset.refkey]: node - })) - } - }, []) - - return [DOMRef, setRef] -} - -/** ==================================== - * 🔰SubComponents - Smaller Component used by - ==================================== **/ - -const ClapContainer = forwardRef( - ( - { children, handleClick, className, style: userStyles = {}, ...restProps }, - ref - ) => { - const classNames = [styles.clap, className].join(' ').trim() - - return ( - - ) - } -) - -const ClapIcon = ({ className = '', style: userStyles = {}, isClicked }) => { - const classNames = [styles.icon, isClicked ? styles.checked : '', className] - .join(' ') - .trim() - - return ( - - - - - - - ) -} - -const ClapCount = forwardRef( - ({ count, className = '', style: userStyles = {}, ...restProps }, ref) => { - const classNames = [styles.count, className].join(' ').trim() - - return ( - - +{count} - - ) - } -) - -const CountTotal = forwardRef( - ( - { countTotal, className = '', style: userStyles = {}, ...restProps }, - ref - ) => { - const classNames = [styles.total, className].join(' ').trim() - - return ( - - {countTotal} - - ) - } -) - -/** ==================================== - * 🔰USAGE - Below's how a potential user - may consume the component API - ==================================== **/ - -const initialState = { count: 10, countTotal: 22, isClicked: false } -const Usage = () => { - const { - clapState, - getTogglerProps, - getCounterProps, - reset, - resetDep - } = useClapState({ initialState }) - const { count, countTotal, isClicked } = clapState - - const [ - { clapContainerRef, clapCountRef, countTotalRef }, - setRef - ] = useDOMRef() - - const animationTimeline = useClapAnimation({ - duration: 300, - bounceEl: clapCountRef, - fadeEl: countTotalRef, - burstEl: clapContainerRef - }) - - const onClick = () => { - animationTimeline.replay() - } - - // Side effect after reset has occured. - const [uploadingReset, setUpload] = useState(false) - useEffectAfterMount( - () => { - setUpload(true) - - const id = setTimeout(() => { - setUpload(false) - console.log('RESET COMPLETE!!!') - }, 3000) - - return () => clearTimeout(id) - }, - [resetDep] - ) - - return ( -
- - - - - -
- -
- {JSON.stringify({ count, countTotal, isClicked })}
- 
-
- {uploadingReset ? `uploading reset ${resetDep}...` : ''}
- 
-
-
- ) -} - -export default Usage diff --git a/showcase/src/patterns/10.js b/showcase/src/patterns/10.js deleted file mode 100644 index a2ffb36..0000000 --- a/showcase/src/patterns/10.js +++ /dev/null @@ -1,427 +0,0 @@ -import React, { - useReducer, - useState, - useEffect, - useCallback, - useLayoutEffect, - forwardRef, - useRef - } from 'react' - - import mojs from 'mo-js' - import { generateRandomNumber } from '../utils/generateRandomNumber' - import styles from './index.css' - import userStyles from './usage.css' - - /** ==================================== - * 🔰Hook - Hook for Holding Previous Vals - ==================================== **/ - function usePrevious (value) { - const ref = useRef() - useEffect(() => { - ref.current = value - }) - return ref.current === undefined ? null : ref.current - } - - /** ==================================== - * 🔰Hook - Hook for Animation - ==================================== **/ - - 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: burstEl, - radius: { 50: 95 }, - count: 5, - angle: 30, - children: { - shape: 'polygon', - radius: { 6: 0 }, - scale: 1, - stroke: 'rgba(211,84,0 ,0.5)', - strokeWidth: 2, - angle: 210, - delay: 30, - speed: 0.2, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1), - duration: tlDuration - } - }) - - const circleBurst = new mojs.Burst({ - parent: burstEl, - radius: { 50: 75 }, - angle: 25, - duration: tlDuration, - children: { - shape: 'circle', - fill: 'rgba(149,165,166 ,0.5)', - delay: 30, - speed: 0.2, - radius: { 3: 0 }, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1) - } - }) - - const countAnimation = new mojs.Html({ - el: bounceEl, - isShowStart: false, - isShowEnd: true, - y: { 0: -30 }, - opacity: { 0: 1 }, - duration: tlDuration - }).then({ - opacity: { 1: 0 }, - y: -80, - delay: tlDuration / 2 - }) - - const countTotalAnimation = new mojs.Html({ - el: fadeEl, - isShowStart: false, - isShowEnd: true, - opacity: { 0: 1 }, - delay: (3 * tlDuration) / 2, - duration: tlDuration, - y: { 0: -3 } - }) - - const scaleButton = new mojs.Html({ - el: burstEl, - duration: tlDuration, - scale: { 1.3: 1 }, - easing: mojs.easing.out - }) - - if (typeof burstEl === 'string') { - const id = burstEl.slice(1, burstEl.length) - 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 - ]) - - setAnimationTimeline(updatedAnimationTimeline) - }, - [tlDuration, animationTimeline, bounceEl, fadeEl, burstEl] - ) - - return animationTimeline - } - - /** ==================================== - * 🔰Hook - Hook for Clap State - ==================================== **/ - const MAX_CLAP = 50 - const INIT_STATE = { - count: 0, - countTotal: generateRandomNumber(500, 10000), - isClicked: false - } - - const callFnsInSequence = (...fns) => (...args) => - fns.forEach(fn => fn && fn(...args)) - - const clapReducer = (state, { type, payload }) => { - const { count, countTotal } = state - - switch (type) { - case useClapState.types.clap: - return { - count: count + 1, - countTotal: countTotal + 1, - isClicked: true - } - case useClapState.types.reset: - return payload - default: - return state - } - } - - const useClapState = ({ - initialState = INIT_STATE, - reducer = clapReducer - } = {}) => { - const initialStateRef = useRef(initialState) - const [clapState, dispatch] = useReducer(reducer, initialStateRef.current) - const { count } = clapState - - const handleClapClick = () => dispatch({ type: 'clap' }) - - const resetRef = useRef(0) - // reset only if there's a change. It's possible to check changes to other state values e.g. countTotal & isClicked - const reset = useCallback(() => { - dispatch({ type: 'reset', payload: initialStateRef.current }) - ++resetRef.current - }, []) - - const getTogglerProps = ({ onClick, ...otherProps } = {}) => ({ - onClick: callFnsInSequence(handleClapClick, onClick), - 'aria-pressed': clapState.isClicked, - ...otherProps - }) - - const getCounterProps = ({ ...otherProps }) => ({ - count, - 'aria-valuemax': MAX_CLAP, - 'aria-valuemin': 0, - 'aria-valuenow': count, - ...otherProps - }) - - return { - clapState, - getTogglerProps, - getCounterProps, - reset, - resetDep: resetRef.current - } - } - - useClapState.reducer = clapReducer - useClapState.types = { - clap: 'clap', - reset: 'reset' - } - - /** ==================================== - * 🔰Hook - useEffectAfterMount - ==================================== **/ - - function useEffectAfterMount (cb, deps) { - const componentJustMounted = useRef(true) - useEffect(() => { - if (!componentJustMounted.current) { - return cb() - } - componentJustMounted.current = false - // eslint-disable-next-line react-hooks/exhaustive-deps - }, deps) - } - - /** ==================================== - * 🔰Hook - useDOMRef - ==================================== **/ - const useDOMRef = () => { - const [DOMRef, setDOMRef] = useState({}) - const setRef = useCallback(node => { - if (node !== null) { - setDOMRef(prevDOMRefs => ({ - ...prevDOMRefs, - [node.dataset.refkey]: node - })) - } - }, []) - - return [DOMRef, setRef] - } - - /** ==================================== - * 🔰SubComponents - Smaller Component used by - ==================================== **/ - - const ClapContainer = forwardRef( - ( - { children, handleClick, className, style: userStyles = {}, ...restProps }, - ref - ) => { - const classNames = [styles.clap, className].join(' ').trim() - - return ( - - ) - } - ) - - const ClapIcon = ({ className = '', style: userStyles = {}, isClicked }) => { - const classNames = [styles.icon, isClicked ? styles.checked : '', className] - .join(' ') - .trim() - - return ( - - - - - - - ) - } - - const ClapCount = forwardRef( - ({ count, className = '', style: userStyles = {}, ...restProps }, ref) => { - const classNames = [styles.count, className].join(' ').trim() - - return ( - - +{count} - - ) - } - ) - - const CountTotal = forwardRef( - ( - { countTotal, className = '', style: userStyles = {}, ...restProps }, - ref - ) => { - const classNames = [styles.total, className].join(' ').trim() - - return ( - - {countTotal} - - ) - } - ) - - /** ==================================== - * 🔰USAGE - Below's how a potential user - may consume the component API - ==================================== **/ - - const initialState = { count: 10, countTotal: 22, isClicked: false } - const Usage = () => { - const [timesClapped, setTimeClapped] = useState(0) - const clappedTooMuch = timesClapped>= 7 - - const reducer = (state, action) => { - if (action.type === useClapState.types.clap && clappedTooMuch) { - return state - } - return useClapState.reducer(state, action) - } - - const { - clapState, - getTogglerProps, - getCounterProps, - reset, - resetDep - } = useClapState({ initialState, reducer }) - const { count, countTotal, isClicked } = clapState - - const [ - { clapContainerRef, clapCountRef, countTotalRef }, - setRef - ] = useDOMRef() - - const animationTimeline = useClapAnimation({ - duration: 300, - bounceEl: clapCountRef, - fadeEl: countTotalRef, - burstEl: clapContainerRef - }) - - const onClick = () => { - setTimeClapped(t => t + 1) - !clappedTooMuch && animationTimeline.replay() - } - - // Side effect after reset has occured. - const [uploadingReset, setUpload] = useState(false) - useEffectAfterMount( - () => { - setTimeClapped(0) - setUpload(true) - - const id = setTimeout(() => { - setUpload(false) - console.log('RESET COMPLETE!!!') - }, 3000) - - return () => clearTimeout(id) - }, - [resetDep] - ) - - return ( -
- - - - - -
- -
- {JSON.stringify({ timesClapped, count, countTotal })}
- 
-
- {uploadingReset ? `uploading reset ${resetDep}...` : ''}
- 
-
-
- {clappedTooMuch ? `You clapped too much. Don't be so generous!` : ''}
- 
-
- ) - } - - export default Usage \ No newline at end of file From f16d7f029fbbb4341122a82c0b9b3b81b75dca9f Mon Sep 17 00:00:00 2001 From: Ohans Emmanuel Date: 2019年12月29日 11:12:54 +0000 Subject: [PATCH 03/41] initial commit --- showcase/src/patterns/01.js | 196 +----------------------------------- showcase/src/patterns/02.js | 7 ++ 2 files changed, 11 insertions(+), 192 deletions(-) create mode 100644 showcase/src/patterns/02.js diff --git a/showcase/src/patterns/01.js b/showcase/src/patterns/01.js index a4a5ae5..5787660 100644 --- a/showcase/src/patterns/01.js +++ b/showcase/src/patterns/01.js @@ -1,195 +1,7 @@ -import React, { Component, useState } from 'react' -import mojs from 'mo-js' -import { generateRandomNumber } from '../utils/generateRandomNumber' -import styles from './index.css' +import React from 'react' -/** ==================================== - * 🔰HOC -Higher Order Component for Animation -==================================== **/ -const withClapAnimation = WrappedComponent => { - class WithClapAnimation extends Component { - state = { - animationTimeline: new mojs.Timeline() - } - - componentDidMount () { - const tlDuration = 300 - - const triangleBurst = new mojs.Burst({ - parent: '#clap', - radius: { 50: 95 }, - count: 5, - angle: 30, - children: { - shape: 'polygon', - radius: { 6: 0 }, - scale: 1, - stroke: 'rgba(211,84,0 ,0.5)', - strokeWidth: 2, - angle: 210, - delay: 30, - speed: 0.2, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1), - duration: tlDuration - } - }) - - const circleBurst = new mojs.Burst({ - parent: '#clap', - radius: { 50: 75 }, - angle: 25, - duration: tlDuration, - children: { - shape: 'circle', - fill: 'rgba(149,165,166 ,0.5)', - delay: 30, - speed: 0.2, - radius: { 3: 0 }, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1) - } - }) - - const countAnimation = new mojs.Html({ - el: '#clapCount', - isShowStart: false, - isShowEnd: true, - y: { 0: -30 }, - opacity: { 0: 1 }, - duration: tlDuration - }).then({ - opacity: { 1: 0 }, - y: -80, - delay: tlDuration / 2 - }) - - const countTotalAnimation = new mojs.Html({ - el: '#clapCountTotal', - isShowStart: false, - isShowEnd: true, - opacity: { 0: 1 }, - delay: (3 * tlDuration) / 2, - duration: tlDuration, - y: { 0: -3 } - }) - - const scaleButton = new mojs.Html({ - el: '#clap', - 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([ - countAnimation, - countTotalAnimation, - scaleButton, - circleBurst, - triangleBurst - ]) - } - - render () { - return ( - - ) - } - } - - WithClapAnimation.displayName = `WithClapAnimation(${getDisplayName( - WrappedComponent - )})` - - return WithClapAnimation -} - -function getDisplayName (WrappedComponent) { - return WrappedComponent.displayName || WrappedComponent.name || 'Component' -} - -/** ==================================== - * 🔰 MediumClap -==================================== **/ -const initialState = { - count: 0, - countTotal: generateRandomNumber(500, 10000), - isClicked: false -} - -const MediumClap = ({ animationTimeline }) => { - const MAXIMUM_USER_CLAP = 50 - const [clapState, setClapState] = useState(initialState) - const { count, countTotal, isClicked } = clapState - - const handleClapClick = () => { - // 👉 prop from HOC - animationTimeline.replay() - - setClapState({ - count: Math.min(count + 1, MAXIMUM_USER_CLAP), - countTotal: count < MAXIMUM_USER_CLAP ? countTotal + 1 : countTotal, - isClicked: true - }) - } - - return ( - - ) -} - -/** ==================================== - * 🔰SubComponents -Smaller Component used by -==================================== **/ - -const ClapIcon = ({ isClicked }) => { - return ( - - - - - - - ) -} -const ClapCount = ({ count }) => { - return ( - - +{count} - - ) -} -const CountTotal = ({ countTotal }) => { - return ( - - {countTotal} - - ) -} - -/** ==================================== - * 🔰USAGE - Below's how a potential user - may consume the component API -==================================== **/ - -const Usage = () => { - const AnimatedMediumClap = withClapAnimation(MediumClap) - return +const Clap = () => { + return

Hello 01 Component

} -export default Usage +export default Clap diff --git a/showcase/src/patterns/02.js b/showcase/src/patterns/02.js new file mode 100644 index 0000000..225836d --- /dev/null +++ b/showcase/src/patterns/02.js @@ -0,0 +1,7 @@ +import React from 'react' + +const Clap = () => { + return

Hello 02 Component

+} + +export default Clap From d25e491c0421813151da84d085f6c38596f4c85b Mon Sep 17 00:00:00 2001 From: Ohans Emmanuel Date: 2019年12月29日 13:42:58 +0000 Subject: [PATCH 04/41] initial commit --- showcase/src/patterns/01.js | 39 ++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/showcase/src/patterns/01.js b/showcase/src/patterns/01.js index 5787660..9a5f946 100644 --- a/showcase/src/patterns/01.js +++ b/showcase/src/patterns/01.js @@ -1,7 +1,40 @@ import React from 'react' +import styles from './index.css' -const Clap = () => { - return

Hello 01 Component

+const MediumClap = () => { + return ( + + ) } -export default Clap +/** + * subcomponents + */ + +const ClapIcon = () => { + return ( + + + + + + + ) +} +const ClapCount = ({ count }) => { + return + {count} +} + +const CountTotal = ({ countTotal }) => { + return {countTotal} +} + +export default MediumClap From 6cfa109ff8babbdb1ad21cd686c5c3b6e862efad Mon Sep 17 00:00:00 2001 From: Ohans Emmanuel Date: 2019年12月30日 11:10:43 +0000 Subject: [PATCH 05/41] clean-slate-03: initial commit --- showcase/src/patterns/01.js | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/showcase/src/patterns/01.js b/showcase/src/patterns/01.js index 9a5f946..22f4e02 100644 --- a/showcase/src/patterns/01.js +++ b/showcase/src/patterns/01.js @@ -1,12 +1,33 @@ -import React from 'react' +import React, { useState } from 'react' import styles from './index.css' +const initialState = { + count: 0, + countTotal: 267, + isClicked: false +} + const MediumClap = () => { + const MAXIMUM_USER_CLAP = 50 + const [clapState, setClapState] = useState(initialState) + const { count, countTotal, isClicked } = clapState + + const handleClapClick = () => { + setClapState(prevState => ({ + isClicked: true, + count: Math.min(count + 1, MAXIMUM_USER_CLAP), + countTotal: + count < MAXIMUM_USER_CLAP + ? prevState.countTotal + 1 + : prevState.countTotal + })) + } + return ( - ) } @@ -15,13 +36,13 @@ const MediumClap = () => { * subcomponents */ -const ClapIcon = () => { +const ClapIcon = ({ isClicked }) => { return ( From 6908c2d07b72f5a6faa78a9e97163609c03d3511 Mon Sep 17 00:00:00 2001 From: Ohans Emmanuel Date: 2019年12月31日 01:31:59 +0000 Subject: [PATCH 06/41] clean-slate-04: initial commit --- showcase/src/patterns/01.js | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/showcase/src/patterns/01.js b/showcase/src/patterns/01.js index 22f4e02..1c02f34 100644 --- a/showcase/src/patterns/01.js +++ b/showcase/src/patterns/01.js @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import React, { Component, useState } from 'react' import styles from './index.css' const initialState = { @@ -7,12 +7,29 @@ const initialState = { isClicked: false } -const MediumClap = () => { +/** + * Higher Order Component + */ +const withClapAnimation = WrappedComponent => { + class WithClapAnimation extends Component { + // this handles animation logic + animate = () => { + console.log('%c Animate', 'background:yellow; color: black') + } + render () { + return + } + } + return WithClapAnimation +} + +const MediumClap = ({ animate }) => { const MAXIMUM_USER_CLAP = 50 const [clapState, setClapState] = useState(initialState) const { count, countTotal, isClicked } = clapState const handleClapClick = () => { + animate() setClapState(prevState => ({ isClicked: true, count: Math.min(count + 1, MAXIMUM_USER_CLAP), @@ -58,4 +75,13 @@ const CountTotal = ({ countTotal }) => { return {countTotal} } -export default MediumClap +/** + * Usage + */ + +const Usage = () => { + const AnimatedMediumClap = withClapAnimation(MediumClap) + return +} + +export default Usage From 806c28d354853b62e40d36118463c65f0e351e5a Mon Sep 17 00:00:00 2001 From: Ohans Emmanuel Date: 2019年12月31日 19:47:54 +0000 Subject: [PATCH 07/41] clean-slate-05: initial-commit --- showcase/src/patterns/01.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/showcase/src/patterns/01.js b/showcase/src/patterns/01.js index 1c02f34..d9e71ea 100644 --- a/showcase/src/patterns/01.js +++ b/showcase/src/patterns/01.js @@ -1,4 +1,5 @@ import React, { Component, useState } from 'react' +import mojs from 'mo-js' import styles from './index.css' const initialState = { @@ -12,24 +13,28 @@ const initialState = { */ const withClapAnimation = WrappedComponent => { class WithClapAnimation extends Component { - // this handles animation logic - animate = () => { - console.log('%c Animate', 'background:yellow; color: black') + state = { + animationTimeline: new mojs.Timeline() } render () { - return + return ( + + ) } } return WithClapAnimation } -const MediumClap = ({ animate }) => { +const MediumClap = ({ animationTimeline }) => { const MAXIMUM_USER_CLAP = 50 const [clapState, setClapState] = useState(initialState) const { count, countTotal, isClicked } = clapState const handleClapClick = () => { - animate() + animationTimeline.replay() setClapState(prevState => ({ isClicked: true, count: Math.min(count + 1, MAXIMUM_USER_CLAP), From a7ce83a8e61706f900532411214fb981504d116a Mon Sep 17 00:00:00 2001 From: Ohans Emmanuel Date: 2019年12月31日 20:05:58 +0000 Subject: [PATCH 08/41] handle initial scale animation --- showcase/src/patterns/01.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/showcase/src/patterns/01.js b/showcase/src/patterns/01.js index d9e71ea..666eb22 100644 --- a/showcase/src/patterns/01.js +++ b/showcase/src/patterns/01.js @@ -13,9 +13,23 @@ const initialState = { */ const withClapAnimation = WrappedComponent => { class WithClapAnimation extends Component { + animationTimeline = new mojs.Timeline() state = { - animationTimeline: new mojs.Timeline() + animationTimeline: this.animationTimeline } + + componentDidMount () { + const scaleButton = new mojs.Html({ + el: '#clap', + duration: 300, + scale: { 1.3: 1 }, + easing: mojs.easing.out + }) + + const newAnimationTimeline = this.animationTimeline.add([scaleButton]) + this.setState({ animationTimeline: newAnimationTimeline }) + } + render () { return ( { } return ( - + ) +} + +/** + * subcomponents + */ + +const ClapIcon = ({ isClicked }) => { + return ( + + + + + + + ) +} +const ClapCount = ({ count }) => { + return ( + + + {count} + + ) +} + +const CountTotal = ({ countTotal }) => { + return ( + + {countTotal} + + ) +} + +/** + * Usage + */ + +const Usage = () => { + return +} + +export default Usage From f9832973239c8c3ea9b87288f313c0d802b9fe22 Mon Sep 17 00:00:00 2001 From: Ohans Emmanuel Date: 2020年1月11日 10:29:19 +0100 Subject: [PATCH 15/41] animation hook in progress --- showcase/src/patterns/02.js | 56 ++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/showcase/src/patterns/02.js b/showcase/src/patterns/02.js index 4fa5de0..ee03e8d 100644 --- a/showcase/src/patterns/02.js +++ b/showcase/src/patterns/02.js @@ -1,4 +1,4 @@ -import React, { Component, useState, useEffect } from 'react' +import React, { useState, useEffect, useCallback } from 'react' import mojs from 'mo-js' import styles from './index.css' @@ -11,7 +11,7 @@ const initialState = { /** * Custom Hook for animation */ -const useClapAnimation = () => { +const useClapAnimation = ({ clapEl, countEl, clapTotalEl }) => { const [animationTimeline, setAnimationTimeline] = useState( () => new mojs.Timeline() ) @@ -19,14 +19,14 @@ const useClapAnimation = () => { useEffect(() => { const tlDuration = 300 const scaleButton = new mojs.Html({ - el: '#clap', + el: clapEl, duration: tlDuration, scale: { 1.3: 1 }, easing: mojs.easing.ease.out }) const triangleBurst = new mojs.Burst({ - parent: '#clap', + parent: clapEl, radius: { 50: 95 }, count: 5, angle: 30, @@ -44,7 +44,7 @@ const useClapAnimation = () => { }) const circleBurst = new mojs.Burst({ - parent: '#clap', + parent: clapEl, radius: { 50: 75 }, angle: 25, duration: tlDuration, @@ -59,7 +59,7 @@ const useClapAnimation = () => { }) const countAnimation = new mojs.Html({ - el: '#clapCount', + el: countEl, opacity: { 0: 1 }, y: { 0: -30 }, duration: tlDuration @@ -70,15 +70,19 @@ const useClapAnimation = () => { }) const countTotalAnimation = new mojs.Html({ - el: '#clapCountTotal', + el: clapTotalEl, opacity: { 0: 1 }, delay: (3 * tlDuration) / 2, duration: tlDuration, y: { 0: -3 } }) - const clap = document.getElementById('clap') - clap.style.transform = 'scale(1,1)' + if (typeof clapEl === 'string') { + const clap = document.getElementById('clap') + clap.style.transform = 'scale(1,1)' + } else { + clapEl.style.transform = 'scale(1,1)' + } const newAnimationTimeline = animationTimeline.add([ scaleButton, @@ -98,7 +102,20 @@ const MediumClap = () => { const [clapState, setClapState] = useState(initialState) const { count, countTotal, isClicked } = clapState - const animationTimeline = useClapAnimation() + const [{ clapRef, clapCountRef, clapTotalRef }, setRefState] = useState({}) + + const setRef = useCallback(node => { + setRefState(prevRefState => ({ + ...prevRefState, + [node.dataset.refkey]: node + })) + }, []) + + const animationTimeline = useClapAnimation({ + clapEl: clapRef, + countEl: clapCountRef, + clapTotalEl: clapTotalRef + }) const handleClapClick = () => { animationTimeline.replay() @@ -113,10 +130,15 @@ const MediumClap = () => { } return ( - ) } @@ -139,17 +161,17 @@ const ClapIcon = ({ isClicked }) => { ) } -const ClapCount = ({ count }) => { +const ClapCount = ({ count, setRef }) => { return ( - + + {count} ) } -const CountTotal = ({ countTotal }) => { +const CountTotal = ({ countTotal, setRef }) => { return ( - + {countTotal} ) From dc3fcc90476427b91329c0fe1e87004b26144cee Mon Sep 17 00:00:00 2001 From: Ohans Emmanuel Date: 2020年1月14日 07:30:23 +0100 Subject: [PATCH 16/41] fix final change to hook --- showcase/src/patterns/02.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/showcase/src/patterns/02.js b/showcase/src/patterns/02.js index ee03e8d..d3056cc 100644 --- a/showcase/src/patterns/02.js +++ b/showcase/src/patterns/02.js @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useCallback } from 'react' +import React, { useState, useLayoutEffect, useCallback } from 'react' import mojs from 'mo-js' import styles from './index.css' @@ -16,7 +16,11 @@ const useClapAnimation = ({ clapEl, countEl, clapTotalEl }) => { () => new mojs.Timeline() ) - useEffect(() => { + useLayoutEffect(() => { + if (!clapEl || !countEl || !clapTotalEl) { + return + } + const tlDuration = 300 const scaleButton = new mojs.Html({ el: clapEl, @@ -92,7 +96,7 @@ const useClapAnimation = ({ clapEl, countEl, clapTotalEl }) => { circleBurst ]) setAnimationTimeline(newAnimationTimeline) - }, []) + }, [clapEl, countEl, clapTotalEl]) return animationTimeline } From a0842648c37f1cdc972ccea312ac5dbcc671381b Mon Sep 17 00:00:00 2001 From: Ohans Emmanuel Date: 2020年1月14日 09:26:58 +0100 Subject: [PATCH 17/41] wip --- showcase/src/patterns/03.js | 223 ++++++++++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 showcase/src/patterns/03.js diff --git a/showcase/src/patterns/03.js b/showcase/src/patterns/03.js new file mode 100644 index 0000000..b3cffc5 --- /dev/null +++ b/showcase/src/patterns/03.js @@ -0,0 +1,223 @@ +import React, { + useState, + useLayoutEffect, + useCallback, + createContext, + useMemo, + useContext +} from 'react' +import mojs from 'mo-js' +import styles from './index.css' + +const initialState = { + count: 0, + countTotal: 267, + isClicked: false +} + +/** + * Custom Hook for animation + */ +const useClapAnimation = ({ clapEl, countEl, clapTotalEl }) => { + const [animationTimeline, setAnimationTimeline] = useState( + () => new mojs.Timeline() + ) + + useLayoutEffect(() => { + if (!clapEl || !countEl || !clapTotalEl) { + return + } + + const tlDuration = 300 + const scaleButton = new mojs.Html({ + el: clapEl, + duration: tlDuration, + scale: { 1.3: 1 }, + easing: mojs.easing.ease.out + }) + + const triangleBurst = new mojs.Burst({ + parent: clapEl, + radius: { 50: 95 }, + count: 5, + angle: 30, + children: { + shape: 'polygon', + radius: { 6: 0 }, + stroke: 'rgba(211,54,0,0.5)', + strokeWidth: 2, + angle: 210, + delay: 30, + speed: 0.2, + easing: mojs.easing.bezier(0.1, 1, 0.3, 1), + duration: tlDuration + } + }) + + const circleBurst = new mojs.Burst({ + parent: clapEl, + radius: { 50: 75 }, + angle: 25, + duration: tlDuration, + children: { + shape: 'circle', + fill: 'rgba(149,165,166,0.5)', + delay: 30, + speed: 0.2, + radius: { 3: 0 }, + easing: mojs.easing.bezier(0.1, 1, 0.3, 1) + } + }) + + const countAnimation = new mojs.Html({ + el: countEl, + opacity: { 0: 1 }, + y: { 0: -30 }, + duration: tlDuration + }).then({ + opacity: { 1: 0 }, + y: -80, + delay: tlDuration / 2 + }) + + const countTotalAnimation = new mojs.Html({ + el: clapTotalEl, + opacity: { 0: 1 }, + delay: (3 * tlDuration) / 2, + duration: tlDuration, + y: { 0: -3 } + }) + + if (typeof clapEl === 'string') { + const clap = document.getElementById('clap') + clap.style.transform = 'scale(1,1)' + } else { + clapEl.style.transform = 'scale(1,1)' + } + + const newAnimationTimeline = animationTimeline.add([ + scaleButton, + countTotalAnimation, + countAnimation, + triangleBurst, + circleBurst + ]) + setAnimationTimeline(newAnimationTimeline) + }, [clapEl, countEl, clapTotalEl]) + + return animationTimeline +} + +const MediumClapContext = createContext() +const { Provider } = MediumClapContext + +const MediumClap = ({ children }) => { + const MAXIMUM_USER_CLAP = 50 + const [clapState, setClapState] = useState(initialState) + const { count } = clapState + + const [{ clapRef, clapCountRef, clapTotalRef }, setRefState] = useState({}) + + const setRef = useCallback(node => { + setRefState(prevRefState => ({ + ...prevRefState, + [node.dataset.refkey]: node + })) + }, []) + + const animationTimeline = useClapAnimation({ + clapEl: clapRef, + countEl: clapCountRef, + clapTotalEl: clapTotalRef + }) + + const handleClapClick = () => { + animationTimeline.replay() + setClapState(prevState => ({ + isClicked: true, + count: Math.min(count + 1, MAXIMUM_USER_CLAP), + countTotal: + count < MAXIMUM_USER_CLAP + ? prevState.countTotal + 1 + : prevState.countTotal + })) + } + + const memoizedValue = useMemo( + () => ({ + ...clapState, + setRef + }), + [clapState, setRef] + ) + + return ( + + + + ) +} + +/** + * subcomponents + */ + +const ClapIcon = () => { + const { isClicked } = useContext(MediumClapContext) + return ( + + + + + + + ) +} +const ClapCount = () => { + const { count, setRef } = useContext(MediumClapContext) + return ( + + + {count} + + ) +} + +const CountTotal = () => { + const { countTotal, setRef } = useContext(MediumClapContext) + return ( + + {countTotal} + + ) +} + +MediumClap.Icon = ClapIcon +MediumClap.Count = ClapCount +MediumClap.Total = CountTotal + +/** + * Usage + */ +// import MediumClap from 'medium-clap' +const Usage = () => { + return ( + + + + + + ) +} + +export default Usage From 6b42efcf1ba4b0f52246edc8d6cde44df2c56d82 Mon Sep 17 00:00:00 2001 From: Ohans Emmanuel Date: 2020年1月15日 08:51:40 +0100 Subject: [PATCH 18/41] resolve compound components --- showcase/src/patterns/03.js | 34 ++++++++++++++++++++++++++------- showcase/src/patterns/index.css | 10 ++++------ 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/showcase/src/patterns/03.js b/showcase/src/patterns/03.js index b3cffc5..73e6d0e 100644 --- a/showcase/src/patterns/03.js +++ b/showcase/src/patterns/03.js @@ -4,7 +4,9 @@ import React, { useCallback, createContext, useMemo, - useContext + useContext, + useEffect, + useRef } from 'react' import mojs from 'mo-js' import styles from './index.css' @@ -111,7 +113,7 @@ const useClapAnimation = ({ clapEl, countEl, clapTotalEl }) => { const MediumClapContext = createContext() const { Provider } = MediumClapContext -const MediumClap = ({ children }) => { +const MediumClap = ({ children, onClap }) => { const MAXIMUM_USER_CLAP = 50 const [clapState, setClapState] = useState(initialState) const { count } = clapState @@ -131,6 +133,15 @@ const MediumClap = ({ children }) => { clapTotalEl: clapTotalRef }) + const componentJustMounted = useRef(true) + useEffect(() => { + if (!componentJustMounted.current) { + console.log('onClap was called!!!') + onClap && onClap(clapState) + } + componentJustMounted.current = false + }, [count]) + const handleClapClick = () => { animationTimeline.replay() setClapState(prevState => ({ @@ -211,12 +222,21 @@ MediumClap.Total = CountTotal */ // import MediumClap from 'medium-clap' const Usage = () => { + const [count, setCount] = useState(0) + const handleClap = clapState => { + setCount(clapState.count) + } return ( - - - - - +
+ + + + + + {!!count && ( +
{`You have clapped ${count} times`}
+ )} +
) } diff --git a/showcase/src/patterns/index.css b/showcase/src/patterns/index.css index baa6240..8427f45 100644 --- a/showcase/src/patterns/index.css +++ b/showcase/src/patterns/index.css @@ -10,7 +10,7 @@ user-select: none; } .clap:after { - content: ""; + content: ''; position: absolute; top: 0; left: 0; @@ -82,8 +82,6 @@ /* Clap Info */ .info { -position: absolute; -left: -20px; -right: -20px; -bottom: -50px; -} \ No newline at end of file + position: relative; + top: 47px; +} From 6e776c172185904786cdbe1a5279eea71d0ba08f Mon Sep 17 00:00:00 2001 From: Ohans Emmanuel Date: 2020年1月15日 09:20:41 +0100 Subject: [PATCH 19/41] resolve reusable styling --- showcase/src/patterns/04.js | 271 ++++++++++++++++++++++++++++++++++++ 1 file changed, 271 insertions(+) create mode 100644 showcase/src/patterns/04.js diff --git a/showcase/src/patterns/04.js b/showcase/src/patterns/04.js new file mode 100644 index 0000000..203c478 --- /dev/null +++ b/showcase/src/patterns/04.js @@ -0,0 +1,271 @@ +import React, { + useState, + useLayoutEffect, + useCallback, + createContext, + useMemo, + useContext, + useEffect, + useRef +} from 'react' +import mojs from 'mo-js' +import styles from './index.css' +import userStyles from './usage.css' + +const initialState = { + count: 0, + countTotal: 267, + isClicked: false +} + +/** + * Custom Hook for animation + */ +const useClapAnimation = ({ clapEl, countEl, clapTotalEl }) => { + const [animationTimeline, setAnimationTimeline] = useState( + () => new mojs.Timeline() + ) + + useLayoutEffect(() => { + if (!clapEl || !countEl || !clapTotalEl) { + return + } + + const tlDuration = 300 + const scaleButton = new mojs.Html({ + el: clapEl, + duration: tlDuration, + scale: { 1.3: 1 }, + easing: mojs.easing.ease.out + }) + + const triangleBurst = new mojs.Burst({ + parent: clapEl, + radius: { 50: 95 }, + count: 5, + angle: 30, + children: { + shape: 'polygon', + radius: { 6: 0 }, + stroke: 'rgba(211,54,0,0.5)', + strokeWidth: 2, + angle: 210, + delay: 30, + speed: 0.2, + easing: mojs.easing.bezier(0.1, 1, 0.3, 1), + duration: tlDuration + } + }) + + const circleBurst = new mojs.Burst({ + parent: clapEl, + radius: { 50: 75 }, + angle: 25, + duration: tlDuration, + children: { + shape: 'circle', + fill: 'rgba(149,165,166,0.5)', + delay: 30, + speed: 0.2, + radius: { 3: 0 }, + easing: mojs.easing.bezier(0.1, 1, 0.3, 1) + } + }) + + const countAnimation = new mojs.Html({ + el: countEl, + opacity: { 0: 1 }, + y: { 0: -30 }, + duration: tlDuration + }).then({ + opacity: { 1: 0 }, + y: -80, + delay: tlDuration / 2 + }) + + const countTotalAnimation = new mojs.Html({ + el: clapTotalEl, + opacity: { 0: 1 }, + delay: (3 * tlDuration) / 2, + duration: tlDuration, + y: { 0: -3 } + }) + + if (typeof clapEl === 'string') { + const clap = document.getElementById('clap') + clap.style.transform = 'scale(1,1)' + } else { + clapEl.style.transform = 'scale(1,1)' + } + + const newAnimationTimeline = animationTimeline.add([ + scaleButton, + countTotalAnimation, + countAnimation, + triangleBurst, + circleBurst + ]) + setAnimationTimeline(newAnimationTimeline) + }, [clapEl, countEl, clapTotalEl]) + + return animationTimeline +} + +const MediumClapContext = createContext() +const { Provider } = MediumClapContext + +const MediumClap = ({ + children, + onClap, + className = '', + style: userStyles = {} +}) => { + const MAXIMUM_USER_CLAP = 50 + const [clapState, setClapState] = useState(initialState) + const { count } = clapState + + const [{ clapRef, clapCountRef, clapTotalRef }, setRefState] = useState({}) + + const setRef = useCallback(node => { + setRefState(prevRefState => ({ + ...prevRefState, + [node.dataset.refkey]: node + })) + }, []) + + const animationTimeline = useClapAnimation({ + clapEl: clapRef, + countEl: clapCountRef, + clapTotalEl: clapTotalRef + }) + + const componentJustMounted = useRef(true) + useEffect(() => { + if (!componentJustMounted.current) { + console.log('onClap was called!!!') + onClap && onClap(clapState) + } + componentJustMounted.current = false + }, [count]) + + const handleClapClick = () => { + animationTimeline.replay() + setClapState(prevState => ({ + isClicked: true, + count: Math.min(count + 1, MAXIMUM_USER_CLAP), + countTotal: + count < MAXIMUM_USER_CLAP + ? prevState.countTotal + 1 + : prevState.countTotal + })) + } + + const memoizedValue = useMemo( + () => ({ + ...clapState, + setRef + }), + [clapState, setRef] + ) + + const classNames = [styles.clap, className].join(' ').trim() + + return ( + + + + ) +} + +/** + * subcomponents + */ + +const ClapIcon = ({ className = '', style: userStyles = {} }) => { + const { isClicked } = useContext(MediumClapContext) + const classNames = [styles.icon, isClicked ? styles.checked : '', className] + .join(' ') + .trim() + + return ( + + + + + + + ) +} +const ClapCount = ({ className = '', style: userStyles = {} }) => { + const { count, setRef } = useContext(MediumClapContext) + const classNames = [styles.count, className].join(' ').trim() + + return ( + + + {count} + + ) +} + +const CountTotal = ({ className = '', style: userStyles = {} }) => { + const { countTotal, setRef } = useContext(MediumClapContext) + const classNames = [styles.total, className].join(' ').trim() + + return ( + + {countTotal} + + ) +} + +MediumClap.Icon = ClapIcon +MediumClap.Count = ClapCount +MediumClap.Total = CountTotal + +/** + * Usage + */ +// import MediumClap from 'medium-clap' +const Usage = () => { + const [count, setCount] = useState(0) + const handleClap = clapState => { + setCount(clapState.count) + } + return ( +
+ + + + + + {!!count && ( +
{`You have clapped ${count} times`}
+ )} +
+ ) +} + +export default Usage From dc049884a6351a5421946668dc13cb21af36d84c Mon Sep 17 00:00:00 2001 From: Ohans Emmanuel Date: 2020年1月16日 07:32:16 +0100 Subject: [PATCH 20/41] redo reusable styles --- showcase/src/patterns/04.js | 23 ++++++------- showcase/src/patterns/usage.css | 58 ++++++++++++++++++--------------- 2 files changed, 43 insertions(+), 38 deletions(-) diff --git a/showcase/src/patterns/04.js b/showcase/src/patterns/04.js index 203c478..b84af10 100644 --- a/showcase/src/patterns/04.js +++ b/showcase/src/patterns/04.js @@ -10,7 +10,7 @@ import React, { } from 'react' import mojs from 'mo-js' import styles from './index.css' -import userStyles from './usage.css' +import userCustomStyles from './usage.css' const initialState = { count: 0, @@ -117,8 +117,8 @@ const { Provider } = MediumClapContext const MediumClap = ({ children, onClap, - className = '', - style: userStyles = {} + style: userStyles = {}, + className }) => { const MAXIMUM_USER_CLAP = 50 const [clapState, setClapState] = useState(initialState) @@ -177,7 +177,7 @@ const MediumClap = ({ data-refkey='clapRef' className={classNames} onClick={handleClapClick} - styles={userStyles} + style={userStyles} > {children} @@ -189,8 +189,9 @@ const MediumClap = ({ * subcomponents */ -const ClapIcon = ({ className = '', style: userStyles = {} }) => { +const ClapIcon = ({ style: userStyles = {}, className }) => { const { isClicked } = useContext(MediumClapContext) + const classNames = [styles.icon, isClicked ? styles.checked : '', className] .join(' ') .trim() @@ -209,7 +210,7 @@ const ClapIcon = ({ className = '', style: userStyles = {} }) => {
) } -const ClapCount = ({ className = '', style: userStyles = {} }) => { +const ClapCount = ({ style: userStyles = {}, className }) => { const { count, setRef } = useContext(MediumClapContext) const classNames = [styles.count, className].join(' ').trim() @@ -225,7 +226,7 @@ const ClapCount = ({ className = '', style: userStyles = {} }) => { ) } -const CountTotal = ({ className = '', style: userStyles = {} }) => { +const CountTotal = ({ style: userStyles = {}, className }) => { const { countTotal, setRef } = useContext(MediumClapContext) const classNames = [styles.total, className].join(' ').trim() @@ -256,10 +257,10 @@ const Usage = () => { } return (
- - - - + + + + {!!count && (
{`You have clapped ${count} times`}
diff --git a/showcase/src/patterns/usage.css b/showcase/src/patterns/usage.css index a345649..1005afc 100644 --- a/showcase/src/patterns/usage.css +++ b/showcase/src/patterns/usage.css @@ -1,56 +1,60 @@ /** total **/ .total { - color: #896EAF; + color: white; + background: #896eaf; } - /* count */ +/* count */ .count { - background: #896EAF; + background: #896eaf; } - /* clap */ +/* clap */ .clap { - border: 1px solid #896EAF; + border: 1px solid #896eaf; } .clap:hover { - border: 1px solid #896EAF; + border: 1px solid #896eaf; +} +/* icon */ +.icon { } .cupContainer { - width: 100px; - min-height: 100px; - text-align: center; + width: 100px; + min-height: 100px; + text-align: center; } .cupBody { - display: flex; + display: flex; } -.cupStream> svg{ - height: 40px; +.cupStream> svg { + height: 40px; } .cupBody> svg { - min-height: 50px; + min-height: 50px; } /* 1. cup handle 2. cup bowl*/ .cupBody> svg:nth-child(1) { - width: 20%; + width: 20%; } .cupBody> svg:nth-child(2) { - width: 80%; - position: relative; - right: 10px; + width: 80%; + position: relative; + right: 10px; } .resetBtn { - border-bottom: 1px solid #bdc3c7; - color: '#27ae60'; - margin-top: 30px; - padding: 10px 20px; - border-radius: 20px; - outline: 0; - display: block; - width: 100%; + border-bottom: 1px solid #bdc3c7; + color: '#27ae60'; + margin-top: 30px; + padding: 10px 20px; + border-radius: 20px; + outline: 0; + display: block; + width: 100%; } .resetMsg { - color: black; -} \ No newline at end of file + color: black; +} From f449da3bda2a2969e4c5ed1fb93b808763b500b1 Mon Sep 17 00:00:00 2001 From: Ohans Emmanuel Date: 2020年1月16日 14:37:31 +0100 Subject: [PATCH 21/41] compound components starting point --- showcase/src/patterns/05.js | 281 ++++++++++++++++++++++++++++++++++++ 1 file changed, 281 insertions(+) create mode 100644 showcase/src/patterns/05.js diff --git a/showcase/src/patterns/05.js b/showcase/src/patterns/05.js new file mode 100644 index 0000000..652c77b --- /dev/null +++ b/showcase/src/patterns/05.js @@ -0,0 +1,281 @@ +import React, { + useState, + useLayoutEffect, + useCallback, + createContext, + useMemo, + useContext, + useEffect, + useRef +} from 'react' +import mojs from 'mo-js' +import styles from './index.css' +import userCustomStyles from './usage.css' + +const initialState = { + count: 0, + countTotal: 267, + isClicked: false +} + +/** + * Custom Hook for animation + */ +const useClapAnimation = ({ clapEl, countEl, clapTotalEl }) => { + const [animationTimeline, setAnimationTimeline] = useState( + () => new mojs.Timeline() + ) + + useLayoutEffect(() => { + if (!clapEl || !countEl || !clapTotalEl) { + return + } + + const tlDuration = 300 + const scaleButton = new mojs.Html({ + el: clapEl, + duration: tlDuration, + scale: { 1.3: 1 }, + easing: mojs.easing.ease.out + }) + + const triangleBurst = new mojs.Burst({ + parent: clapEl, + radius: { 50: 95 }, + count: 5, + angle: 30, + children: { + shape: 'polygon', + radius: { 6: 0 }, + stroke: 'rgba(211,54,0,0.5)', + strokeWidth: 2, + angle: 210, + delay: 30, + speed: 0.2, + easing: mojs.easing.bezier(0.1, 1, 0.3, 1), + duration: tlDuration + } + }) + + const circleBurst = new mojs.Burst({ + parent: clapEl, + radius: { 50: 75 }, + angle: 25, + duration: tlDuration, + children: { + shape: 'circle', + fill: 'rgba(149,165,166,0.5)', + delay: 30, + speed: 0.2, + radius: { 3: 0 }, + easing: mojs.easing.bezier(0.1, 1, 0.3, 1) + } + }) + + const countAnimation = new mojs.Html({ + el: countEl, + opacity: { 0: 1 }, + y: { 0: -30 }, + duration: tlDuration + }).then({ + opacity: { 1: 0 }, + y: -80, + delay: tlDuration / 2 + }) + + const countTotalAnimation = new mojs.Html({ + el: clapTotalEl, + opacity: { 0: 1 }, + delay: (3 * tlDuration) / 2, + duration: tlDuration, + y: { 0: -3 } + }) + + if (typeof clapEl === 'string') { + const clap = document.getElementById('clap') + clap.style.transform = 'scale(1,1)' + } else { + clapEl.style.transform = 'scale(1,1)' + } + + const newAnimationTimeline = animationTimeline.add([ + scaleButton, + countTotalAnimation, + countAnimation, + triangleBurst, + circleBurst + ]) + setAnimationTimeline(newAnimationTimeline) + }, [clapEl, countEl, clapTotalEl]) + + return animationTimeline +} + +const MediumClapContext = createContext() +const { Provider } = MediumClapContext + +const MediumClap = ({ + children, + onClap, + values = null, + style: userStyles = {}, + className +}) => { + const MAXIMUM_USER_CLAP = 50 + const [clapState, setClapState] = useState(initialState) + const { count } = clapState + + const [{ clapRef, clapCountRef, clapTotalRef }, setRefState] = useState({}) + + const setRef = useCallback(node => { + setRefState(prevRefState => ({ + ...prevRefState, + [node.dataset.refkey]: node + })) + }, []) + + const animationTimeline = useClapAnimation({ + clapEl: clapRef, + countEl: clapCountRef, + clapTotalEl: clapTotalRef + }) + + const componentJustMounted = useRef(true) + useEffect(() => { + if (!componentJustMounted.current && !isControlled) { + onClap && onClap(clapState) + } + componentJustMounted.current = false + }, [count, onClap, isControlled]) + + // controlled component? + const isControlled = !!values && onClap + const handleClapClick = () => { + animationTimeline.replay() + isControlled + ? onClap() + : setClapState(prevState => ({ + isClicked: true, + count: Math.min(count + 1, MAXIMUM_USER_CLAP), + countTotal: + count < MAXIMUM_USER_CLAP + ? prevState.countTotal + 1 + : prevState.countTotal + })) + } + const getState = useCallback(() => (isControlled ? values : clapState), [ + isControlled, + values, + clapState + ]) + + const memoizedValue = useMemo( + () => ({ + ...getState(), + setRef + }), + [getState, setRef] + ) + + const classNames = [styles.clap, className].join(' ').trim() + + return ( + + + + ) +} + +/** + * subcomponents + */ + +const ClapIcon = ({ style: userStyles = {}, className }) => { + const { isClicked } = useContext(MediumClapContext) + + const classNames = [styles.icon, isClicked ? styles.checked : '', className] + .join(' ') + .trim() + + return ( + + + + + + + ) +} +const ClapCount = ({ style: userStyles = {}, className }) => { + const { count, setRef } = useContext(MediumClapContext) + const classNames = [styles.count, className].join(' ').trim() + + return ( + + + {count} + + ) +} + +const CountTotal = ({ style: userStyles = {}, className }) => { + const { countTotal, setRef } = useContext(MediumClapContext) + const classNames = [styles.total, className].join(' ').trim() + + return ( + + {countTotal} + + ) +} + +MediumClap.Icon = ClapIcon +MediumClap.Count = ClapCount +MediumClap.Total = CountTotal + +/** + * Usage + */ +// import MediumClap from 'medium-clap' +const Usage = () => { + const [count, setCount] = useState(0) + const handleClap = clapState => { + setCount(clapState.count) + } + return ( +
+ + + + + + {!!count && ( +
{`You have clapped ${count} times`}
+ )} +
+ ) +} + +export default Usage From dc6b0aec6808c902ca045fc79f752bb9e7de9f07 Mon Sep 17 00:00:00 2001 From: Ohans Emmanuel Date: 2020年1月16日 22:32:12 +0100 Subject: [PATCH 22/41] complete control props example --- showcase/src/patterns/05.js | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/showcase/src/patterns/05.js b/showcase/src/patterns/05.js index 652c77b..f90110a 100644 --- a/showcase/src/patterns/05.js +++ b/showcase/src/patterns/05.js @@ -259,21 +259,43 @@ MediumClap.Total = CountTotal * Usage */ // import MediumClap from 'medium-clap' +const INITIAL_STATE = { + count: 0, + countTotal: 2100, + isClicked: false +} +const MAXIMUM_CLAP_VAL = 10 + const Usage = () => { - const [count, setCount] = useState(0) - const handleClap = clapState => { - setCount(clapState.count) + const [state, setState] = useState(INITIAL_STATE) + const handleClap = () => { + setState(({ count, countTotal }) => ({ + count: Math.min(count + 1, MAXIMUM_CLAP_VAL), + countTotal: count < MAXIMUM_CLAP_VAL ? countTotal + 1 : countTotal, + isClicked: true + })) } + return (
- + + + + + + - {!!count && ( -
{`You have clapped ${count} times`}
- )}
) } From 17f8a7d3b87b46fe7adb1948b552002ae753787a Mon Sep 17 00:00:00 2001 From: Ohans Emmanuel Date: 2020年1月24日 05:37:11 +0100 Subject: [PATCH 23/41] fix hook: useDomRef --- showcase/src/patterns/06.js | 201 ++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 showcase/src/patterns/06.js diff --git a/showcase/src/patterns/06.js b/showcase/src/patterns/06.js new file mode 100644 index 0000000..6c6ea71 --- /dev/null +++ b/showcase/src/patterns/06.js @@ -0,0 +1,201 @@ +import React, { useState, useLayoutEffect, useCallback } from 'react' +import mojs from 'mo-js' +import styles from './index.css' + +const initialState = { + count: 0, + countTotal: 267, + isClicked: false +} + +/** + * Custom Hook for animation + */ +const useClapAnimation = ({ clapEl, countEl, clapTotalEl }) => { + const [animationTimeline, setAnimationTimeline] = useState( + () => new mojs.Timeline() + ) + + useLayoutEffect(() => { + if (!clapEl || !countEl || !clapTotalEl) { + return + } + + const tlDuration = 300 + const scaleButton = new mojs.Html({ + el: clapEl, + duration: tlDuration, + scale: { 1.3: 1 }, + easing: mojs.easing.ease.out + }) + + const triangleBurst = new mojs.Burst({ + parent: clapEl, + radius: { 50: 95 }, + count: 5, + angle: 30, + children: { + shape: 'polygon', + radius: { 6: 0 }, + stroke: 'rgba(211,54,0,0.5)', + strokeWidth: 2, + angle: 210, + delay: 30, + speed: 0.2, + easing: mojs.easing.bezier(0.1, 1, 0.3, 1), + duration: tlDuration + } + }) + + const circleBurst = new mojs.Burst({ + parent: clapEl, + radius: { 50: 75 }, + angle: 25, + duration: tlDuration, + children: { + shape: 'circle', + fill: 'rgba(149,165,166,0.5)', + delay: 30, + speed: 0.2, + radius: { 3: 0 }, + easing: mojs.easing.bezier(0.1, 1, 0.3, 1) + } + }) + + const countAnimation = new mojs.Html({ + el: countEl, + opacity: { 0: 1 }, + y: { 0: -30 }, + duration: tlDuration + }).then({ + opacity: { 1: 0 }, + y: -80, + delay: tlDuration / 2 + }) + + const countTotalAnimation = new mojs.Html({ + el: clapTotalEl, + opacity: { 0: 1 }, + delay: (3 * tlDuration) / 2, + duration: tlDuration, + y: { 0: -3 } + }) + + if (typeof clapEl === 'string') { + const clap = document.getElementById('clap') + clap.style.transform = 'scale(1,1)' + } else { + clapEl.style.transform = 'scale(1,1)' + } + + const newAnimationTimeline = animationTimeline.add([ + scaleButton, + countTotalAnimation, + countAnimation, + triangleBurst, + circleBurst + ]) + setAnimationTimeline(newAnimationTimeline) + }, [clapEl, countEl, clapTotalEl]) + + return animationTimeline +} + +/** + * useDOMRef Hook + */ +const useDOMRef = () => { + const [DOMRef, setRefState] = useState({}) + + const setRef = useCallback(node => { + setRefState(prevRefState => ({ + ...prevRefState, + [node.dataset.refkey]: node + })) + }, []) + + return [DOMRef, setRef] +} + +const MediumClap = () => { + const MAXIMUM_USER_CLAP = 50 + const [clapState, setClapState] = useState(initialState) + const { count, countTotal, isClicked } = clapState + + const [{ clapRef, clapCountRef, clapTotalRef }, setRef] = useDOMRef() + + const animationTimeline = useClapAnimation({ + clapEl: clapRef, + countEl: clapCountRef, + clapTotalEl: clapTotalRef + }) + + const handleClapClick = () => { + animationTimeline.replay() + setClapState(prevState => ({ + isClicked: true, + count: Math.min(count + 1, MAXIMUM_USER_CLAP), + countTotal: + count < MAXIMUM_USER_CLAP + ? prevState.countTotal + 1 + : prevState.countTotal + })) + } + + return ( + + ) +} + +/** + * subcomponents + */ + +const ClapIcon = ({ isClicked }) => { + return ( + + + + + + + ) +} +const ClapCount = ({ count, setRef }) => { + return ( + + + {count} + + ) +} + +const CountTotal = ({ countTotal, setRef }) => { + return ( + + {countTotal} + + ) +} + +/** + * Usage + */ + +const Usage = () => { + return +} + +export default Usage From 708aec5085e539bf1a5af4c1ef0f2bcada75c2ad Mon Sep 17 00:00:00 2001 From: Ohans Emmanuel Date: 2020年1月24日 06:06:11 +0100 Subject: [PATCH 24/41] fix hook: useClapState --- showcase/src/patterns/06.js | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/showcase/src/patterns/06.js b/showcase/src/patterns/06.js index 6c6ea71..2ba29f4 100644 --- a/showcase/src/patterns/06.js +++ b/showcase/src/patterns/06.js @@ -2,7 +2,7 @@ import React, { useState, useLayoutEffect, useCallback } from 'react' import mojs from 'mo-js' import styles from './index.css' -const initialState = { +const INITIAL_STATE = { count: 0, countTotal: 267, isClicked: false @@ -117,9 +117,27 @@ const useDOMRef = () => { return [DOMRef, setRef] } -const MediumClap = () => { +/** + * custom hook for useClapState + */ +const useClapState = (initialState = INITIAL_STATE) => { const MAXIMUM_USER_CLAP = 50 const [clapState, setClapState] = useState(initialState) + const { count, countTotal } = clapState + + const updateClapState = useCallback(() => { + setClapState(({ count, countTotal }) => ({ + isClicked: true, + count: Math.min(count + 1, MAXIMUM_USER_CLAP), + countTotal: count < MAXIMUM_USER_CLAP ? countTotal + 1 : countTotal + })) + }, [count, countTotal]) + + return [clapState, updateClapState] +} + +const MediumClap = () => { + const [clapState, updateClapState] = useClapState() const { count, countTotal, isClicked } = clapState const [{ clapRef, clapCountRef, clapTotalRef }, setRef] = useDOMRef() @@ -132,14 +150,7 @@ const MediumClap = () => { const handleClapClick = () => { animationTimeline.replay() - setClapState(prevState => ({ - isClicked: true, - count: Math.min(count + 1, MAXIMUM_USER_CLAP), - countTotal: - count < MAXIMUM_USER_CLAP - ? prevState.countTotal + 1 - : prevState.countTotal - })) + updateClapState() } return ( From 83ae6e54ac9da9bde4a17b39755e7f526779a05e Mon Sep 17 00:00:00 2001 From: Ohans Emmanuel Date: 2020年1月24日 06:17:09 +0100 Subject: [PATCH 25/41] fix hook: useEffectAfterMount --- showcase/src/patterns/06.js | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/showcase/src/patterns/06.js b/showcase/src/patterns/06.js index 2ba29f4..b67f364 100644 --- a/showcase/src/patterns/06.js +++ b/showcase/src/patterns/06.js @@ -1,4 +1,10 @@ -import React, { useState, useLayoutEffect, useCallback } from 'react' +import React, { + useState, + useLayoutEffect, + useCallback, + useRef, + useEffect +} from 'react' import mojs from 'mo-js' import styles from './index.css' @@ -136,6 +142,19 @@ const useClapState = (initialState = INITIAL_STATE) => { return [clapState, updateClapState] } +/** + * custom useEffectAfterMount hook + */ +const useEffectAfterMount = (cb, deps) => { + const componentJustMounted = useRef(true) + useEffect(() => { + if (!componentJustMounted.current) { + return cb() + } + componentJustMounted.current = false + }, deps) +} + const MediumClap = () => { const [clapState, updateClapState] = useClapState() const { count, countTotal, isClicked } = clapState @@ -148,17 +167,16 @@ const MediumClap = () => { clapTotalEl: clapTotalRef }) - const handleClapClick = () => { + useEffectAfterMount(() => { animationTimeline.replay() - updateClapState() - } + }, [count]) return ( + ) +} + +const ClapIcon = ({ isClicked, ...restProps }) => { + return ( + + + + + + + ) +} +const ClapCount = ({ count, setRef, ...restProps }) => { + return ( + + + {count} + + ) +} + +const CountTotal = ({ countTotal, setRef, ...restProps }) => { + return ( + + {countTotal} + + ) +} + +/** + * Usage + */ + +const Usage = () => { + const [clapState, updateClapState] = useClapState() + const { count, countTotal, isClicked } = clapState + + const [{ clapRef, clapCountRef, clapTotalRef }, setRef] = useDOMRef() + + const animationTimeline = useClapAnimation({ + clapEl: clapRef, + countEl: clapCountRef, + clapTotalEl: clapTotalRef + }) + + useEffectAfterMount(() => { + animationTimeline.replay() + }, [count]) + + return ( + + + + + + ) +} + +export default Usage From 4fe7f2dce36b97b65cd82462f741dade4bff6867 Mon Sep 17 00:00:00 2001 From: Ohans Emmanuel Date: 2020年1月28日 05:19:00 +0100 Subject: [PATCH 27/41] delelte #07 --- showcase/src/patterns/07.js | 244 ------------------------------------ 1 file changed, 244 deletions(-) delete mode 100644 showcase/src/patterns/07.js diff --git a/showcase/src/patterns/07.js b/showcase/src/patterns/07.js deleted file mode 100644 index e45bd24..0000000 --- a/showcase/src/patterns/07.js +++ /dev/null @@ -1,244 +0,0 @@ -import React, { - useState, - useLayoutEffect, - useCallback, - useRef, - useEffect -} from 'react' -import mojs from 'mo-js' -import styles from './index.css' - -const INITIAL_STATE = { - count: 0, - countTotal: 267, - isClicked: false -} - -/** - * Custom Hook for animation - */ -const useClapAnimation = ({ clapEl, countEl, clapTotalEl }) => { - const [animationTimeline, setAnimationTimeline] = useState( - () => new mojs.Timeline() - ) - - useLayoutEffect(() => { - if (!clapEl || !countEl || !clapTotalEl) { - return - } - - const tlDuration = 300 - const scaleButton = new mojs.Html({ - el: clapEl, - duration: tlDuration, - scale: { 1.3: 1 }, - easing: mojs.easing.ease.out - }) - - const triangleBurst = new mojs.Burst({ - parent: clapEl, - radius: { 50: 95 }, - count: 5, - angle: 30, - children: { - shape: 'polygon', - radius: { 6: 0 }, - stroke: 'rgba(211,54,0,0.5)', - strokeWidth: 2, - angle: 210, - delay: 30, - speed: 0.2, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1), - duration: tlDuration - } - }) - - const circleBurst = new mojs.Burst({ - parent: clapEl, - radius: { 50: 75 }, - angle: 25, - duration: tlDuration, - children: { - shape: 'circle', - fill: 'rgba(149,165,166,0.5)', - delay: 30, - speed: 0.2, - radius: { 3: 0 }, - easing: mojs.easing.bezier(0.1, 1, 0.3, 1) - } - }) - - const countAnimation = new mojs.Html({ - el: countEl, - opacity: { 0: 1 }, - y: { 0: -30 }, - duration: tlDuration - }).then({ - opacity: { 1: 0 }, - y: -80, - delay: tlDuration / 2 - }) - - const countTotalAnimation = new mojs.Html({ - el: clapTotalEl, - opacity: { 0: 1 }, - delay: (3 * tlDuration) / 2, - duration: tlDuration, - y: { 0: -3 } - }) - - if (typeof clapEl === 'string') { - const clap = document.getElementById('clap') - clap.style.transform = 'scale(1,1)' - } else { - clapEl.style.transform = 'scale(1,1)' - } - - const newAnimationTimeline = animationTimeline.add([ - scaleButton, - countTotalAnimation, - countAnimation, - triangleBurst, - circleBurst - ]) - setAnimationTimeline(newAnimationTimeline) - }, [clapEl, countEl, clapTotalEl]) - - return animationTimeline -} - -/** - * useDOMRef Hook - */ -const useDOMRef = () => { - const [DOMRef, setRefState] = useState({}) - - const setRef = useCallback(node => { - setRefState(prevRefState => ({ - ...prevRefState, - [node.dataset.refkey]: node - })) - }, []) - - return [DOMRef, setRef] -} - -/** - * custom hook for useClapState - */ -const useClapState = (initialState = INITIAL_STATE) => { - const MAXIMUM_USER_CLAP = 50 - const [clapState, setClapState] = useState(initialState) - const { count, countTotal } = clapState - - const updateClapState = useCallback(() => { - setClapState(({ count, countTotal }) => ({ - isClicked: true, - count: Math.min(count + 1, MAXIMUM_USER_CLAP), - countTotal: count < MAXIMUM_USER_CLAP ? countTotal + 1 : countTotal - })) - }, [count, countTotal]) - - return [clapState, updateClapState] -} - -/** - * custom useEffectAfterMount hook - */ -const useEffectAfterMount = (cb, deps) => { - const componentJustMounted = useRef(true) - useEffect(() => { - if (!componentJustMounted.current) { - return cb() - } - componentJustMounted.current = false - }, deps) -} - -/** - * subcomponents - */ - -const ClapContainer = ({ children, handleClick, setRef, ...restProps }) => { - return ( - - ) -} - -const ClapIcon = ({ isClicked, ...restProps }) => { - return ( - - - - - - - ) -} -const ClapCount = ({ count, setRef, ...restProps }) => { - return ( - - + {count} - - ) -} - -const CountTotal = ({ countTotal, setRef, ...restProps }) => { - return ( - - {countTotal} - - ) -} - -/** - * Usage - */ - -const Usage = () => { - const [clapState, updateClapState] = useClapState() - const { count, countTotal, isClicked } = clapState - - const [{ clapRef, clapCountRef, clapTotalRef }, setRef] = useDOMRef() - - const animationTimeline = useClapAnimation({ - clapEl: clapRef, - countEl: clapCountRef, - clapTotalEl: clapTotalRef - }) - - useEffectAfterMount(() => { - animationTimeline.replay() - }, [count]) - - return ( - - - - - - ) -} - -export default Usage From dfbe09a664e1c15532265775bc1efc9bfd58a4de Mon Sep 17 00:00:00 2001 From: Ohans Emmanuel Date: 2020年1月28日 05:20:37 +0100 Subject: [PATCH 28/41] initial refactor #07 --- showcase/src/patterns/07.js | 244 ++++++++++++++++++++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 showcase/src/patterns/07.js diff --git a/showcase/src/patterns/07.js b/showcase/src/patterns/07.js new file mode 100644 index 0000000..e45bd24 --- /dev/null +++ b/showcase/src/patterns/07.js @@ -0,0 +1,244 @@ +import React, { + useState, + useLayoutEffect, + useCallback, + useRef, + useEffect +} from 'react' +import mojs from 'mo-js' +import styles from './index.css' + +const INITIAL_STATE = { + count: 0, + countTotal: 267, + isClicked: false +} + +/** + * Custom Hook for animation + */ +const useClapAnimation = ({ clapEl, countEl, clapTotalEl }) => { + const [animationTimeline, setAnimationTimeline] = useState( + () => new mojs.Timeline() + ) + + useLayoutEffect(() => { + if (!clapEl || !countEl || !clapTotalEl) { + return + } + + const tlDuration = 300 + const scaleButton = new mojs.Html({ + el: clapEl, + duration: tlDuration, + scale: { 1.3: 1 }, + easing: mojs.easing.ease.out + }) + + const triangleBurst = new mojs.Burst({ + parent: clapEl, + radius: { 50: 95 }, + count: 5, + angle: 30, + children: { + shape: 'polygon', + radius: { 6: 0 }, + stroke: 'rgba(211,54,0,0.5)', + strokeWidth: 2, + angle: 210, + delay: 30, + speed: 0.2, + easing: mojs.easing.bezier(0.1, 1, 0.3, 1), + duration: tlDuration + } + }) + + const circleBurst = new mojs.Burst({ + parent: clapEl, + radius: { 50: 75 }, + angle: 25, + duration: tlDuration, + children: { + shape: 'circle', + fill: 'rgba(149,165,166,0.5)', + delay: 30, + speed: 0.2, + radius: { 3: 0 }, + easing: mojs.easing.bezier(0.1, 1, 0.3, 1) + } + }) + + const countAnimation = new mojs.Html({ + el: countEl, + opacity: { 0: 1 }, + y: { 0: -30 }, + duration: tlDuration + }).then({ + opacity: { 1: 0 }, + y: -80, + delay: tlDuration / 2 + }) + + const countTotalAnimation = new mojs.Html({ + el: clapTotalEl, + opacity: { 0: 1 }, + delay: (3 * tlDuration) / 2, + duration: tlDuration, + y: { 0: -3 } + }) + + if (typeof clapEl === 'string') { + const clap = document.getElementById('clap') + clap.style.transform = 'scale(1,1)' + } else { + clapEl.style.transform = 'scale(1,1)' + } + + const newAnimationTimeline = animationTimeline.add([ + scaleButton, + countTotalAnimation, + countAnimation, + triangleBurst, + circleBurst + ]) + setAnimationTimeline(newAnimationTimeline) + }, [clapEl, countEl, clapTotalEl]) + + return animationTimeline +} + +/** + * useDOMRef Hook + */ +const useDOMRef = () => { + const [DOMRef, setRefState] = useState({}) + + const setRef = useCallback(node => { + setRefState(prevRefState => ({ + ...prevRefState, + [node.dataset.refkey]: node + })) + }, []) + + return [DOMRef, setRef] +} + +/** + * custom hook for useClapState + */ +const useClapState = (initialState = INITIAL_STATE) => { + const MAXIMUM_USER_CLAP = 50 + const [clapState, setClapState] = useState(initialState) + const { count, countTotal } = clapState + + const updateClapState = useCallback(() => { + setClapState(({ count, countTotal }) => ({ + isClicked: true, + count: Math.min(count + 1, MAXIMUM_USER_CLAP), + countTotal: count < MAXIMUM_USER_CLAP ? countTotal + 1 : countTotal + })) + }, [count, countTotal]) + + return [clapState, updateClapState] +} + +/** + * custom useEffectAfterMount hook + */ +const useEffectAfterMount = (cb, deps) => { + const componentJustMounted = useRef(true) + useEffect(() => { + if (!componentJustMounted.current) { + return cb() + } + componentJustMounted.current = false + }, deps) +} + +/** + * subcomponents + */ + +const ClapContainer = ({ children, handleClick, setRef, ...restProps }) => { + return ( + + ) +} + +const ClapIcon = ({ isClicked, ...restProps }) => { + return ( + + + + + + + ) +} +const ClapCount = ({ count, setRef, ...restProps }) => { + return ( + + + {count} + + ) +} + +const CountTotal = ({ countTotal, setRef, ...restProps }) => { + return ( + + {countTotal} + + ) +} + +/** + * Usage + */ + +const Usage = () => { + const [clapState, updateClapState] = useClapState() + const { count, countTotal, isClicked } = clapState + + const [{ clapRef, clapCountRef, clapTotalRef }, setRef] = useDOMRef() + + const animationTimeline = useClapAnimation({ + clapEl: clapRef, + countEl: clapCountRef, + clapTotalEl: clapTotalRef + }) + + useEffectAfterMount(() => { + animationTimeline.replay() + }, [count]) + + return ( + + + + + + ) +} + +export default Usage From 6614dca45355cfadad37d281d09015d18d19fd82 Mon Sep 17 00:00:00 2001 From: Ohans Emmanuel Date: 2020年1月28日 06:33:53 +0100 Subject: [PATCH 29/41] initial refactor for props collection --- showcase/src/patterns/07.js | 43 ++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/showcase/src/patterns/07.js b/showcase/src/patterns/07.js index e45bd24..e6db6f3 100644 --- a/showcase/src/patterns/07.js +++ b/showcase/src/patterns/07.js @@ -155,11 +155,41 @@ const useEffectAfterMount = (cb, deps) => { }, deps) } +const MediumClap = () => { + const [clapState, updateClapState] = useClapState() + const { count, countTotal, isClicked } = clapState + + const [{ clapRef, clapCountRef, clapTotalRef }, setRef] = useDOMRef() + + const animationTimeline = useClapAnimation({ + clapEl: clapRef, + countEl: clapCountRef, + clapTotalEl: clapTotalRef + }) + + useEffectAfterMount(() => { + animationTimeline.replay() + }, [count]) + + return ( + + ) +} + /** * subcomponents */ -const ClapContainer = ({ children, handleClick, setRef, ...restProps }) => { +const ClapContainer = ({ children, setRef, handleClick, ...restProps }) => { return ( ) } - -const ClapIcon = ({ isClicked, ...restProps }) => { +const ClapIcon = ({ isClicked }) => { return ( @@ -206,7 +234,6 @@ const CountTotal = ({ countTotal, setRef, ...restProps }) => { /** * Usage */ - const Usage = () => { const [clapState, updateClapState] = useClapState() const { count, countTotal, isClicked } = clapState @@ -226,11 +253,11 @@ const Usage = () => { return ( - + {/* */} + 🇳🇬 Date: 2020年1月28日 07:25:53 +0100 Subject: [PATCH 30/41] complete props collection --- showcase/src/patterns/07.js | 62 ++++++++++++++----------------------- 1 file changed, 24 insertions(+), 38 deletions(-) diff --git a/showcase/src/patterns/07.js b/showcase/src/patterns/07.js index e6db6f3..839b1c0 100644 --- a/showcase/src/patterns/07.js +++ b/showcase/src/patterns/07.js @@ -139,7 +139,21 @@ const useClapState = (initialState = INITIAL_STATE) => { })) }, [count, countTotal]) - return [clapState, updateClapState] + // props collection for 'click' + const togglerProps = { + onClick: updateClapState, + 'aria-pressed': clapState.isClicked + } + + // props collection for 'count' + const counterProps = { + count, + 'aria-valuemax': MAXIMUM_USER_CLAP, + 'aria-valuemin': 0, + 'aria-valuenow': count + } + + return { clapState, updateClapState, togglerProps, counterProps } } /** @@ -155,36 +169,6 @@ const useEffectAfterMount = (cb, deps) => { }, deps) } -const MediumClap = () => { - const [clapState, updateClapState] = useClapState() - const { count, countTotal, isClicked } = clapState - - const [{ clapRef, clapCountRef, clapTotalRef }, setRef] = useDOMRef() - - const animationTimeline = useClapAnimation({ - clapEl: clapRef, - countEl: clapCountRef, - clapTotalEl: clapTotalRef - }) - - useEffectAfterMount(() => { - animationTimeline.replay() - }, [count]) - - return ( - - ) -} - /** * subcomponents */ @@ -235,7 +219,13 @@ const CountTotal = ({ countTotal, setRef, ...restProps }) => { * Usage */ const Usage = () => { - const [clapState, updateClapState] = useClapState() + const { + clapState, + updateClapState, + togglerProps, + counterProps + } = useClapState() + const { count, countTotal, isClicked } = clapState const [{ clapRef, clapCountRef, clapTotalRef }, setRef] = useDOMRef() @@ -251,14 +241,10 @@ const Usage = () => { }, [count]) return ( - + {/* */} 🇳🇬 - + Date: 2020年1月31日 08:08:38 +0100 Subject: [PATCH 31/41] end prop getters --- showcase/src/patterns/07.js | 48 ++++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/showcase/src/patterns/07.js b/showcase/src/patterns/07.js index 839b1c0..cd07e3e 100644 --- a/showcase/src/patterns/07.js +++ b/showcase/src/patterns/07.js @@ -122,6 +122,11 @@ const useDOMRef = () => { return [DOMRef, setRef] } +// const handleClick = (evt) => { ... } +// +const callFnsInSequence = (...fns) => (...args) => { + fns.forEach(fn => fn && fn(...args)) +} /** * custom hook for useClapState @@ -139,21 +144,21 @@ const useClapState = (initialState = INITIAL_STATE) => { })) }, [count, countTotal]) - // props collection for 'click' - const togglerProps = { - onClick: updateClapState, - 'aria-pressed': clapState.isClicked - } + const getTogglerProps = ({ onClick, ...otherProps } = {}) => ({ + onClick: callFnsInSequence(updateClapState, onClick), + 'aria-pressed': clapState.isClicked, + ...otherProps + }) - // props collection for 'count' - const counterProps = { + const getCounterProps = ({ ...otherProps }) => ({ count, 'aria-valuemax': MAXIMUM_USER_CLAP, 'aria-valuemin': 0, - 'aria-valuenow': count - } + 'aria-valuenow': count, + ...otherProps + }) - return { clapState, updateClapState, togglerProps, counterProps } + return { clapState, updateClapState, getTogglerProps, getCounterProps } } /** @@ -222,8 +227,8 @@ const Usage = () => { const { clapState, updateClapState, - togglerProps, - counterProps + getTogglerProps, + getCounterProps } = useClapState() const { count, countTotal, isClicked } = clapState @@ -240,11 +245,26 @@ const Usage = () => { animationTimeline.replay() }, [count]) + const handleClick = () => { + console.log('CLICKED!!!!') + } + return ( - + {/* */} 🇳🇬 - + Date: Mon, 3 Feb 2020 10:46:55 +0100 Subject: [PATCH 32/41] split prop collection into separate file --- showcase/src/patterns/07.js | 47 ++---- showcase/src/patterns/08.js | 277 ++++++++++++++++++++++++++++++++++++ 2 files changed, 289 insertions(+), 35 deletions(-) create mode 100644 showcase/src/patterns/08.js diff --git a/showcase/src/patterns/07.js b/showcase/src/patterns/07.js index cd07e3e..c335bc2 100644 --- a/showcase/src/patterns/07.js +++ b/showcase/src/patterns/07.js @@ -144,21 +144,19 @@ const useClapState = (initialState = INITIAL_STATE) => { })) }, [count, countTotal]) - const getTogglerProps = ({ onClick, ...otherProps } = {}) => ({ - onClick: callFnsInSequence(updateClapState, onClick), - 'aria-pressed': clapState.isClicked, - ...otherProps - }) + const togglerProps = { + onClick: updateClapState, + 'aria-pressed': clapState.isClicked + } - const getCounterProps = ({ ...otherProps }) => ({ + const counterProps = { count, 'aria-valuemax': MAXIMUM_USER_CLAP, 'aria-valuemin': 0, - 'aria-valuenow': count, - ...otherProps - }) + 'aria-valuenow': count + } - return { clapState, updateClapState, getTogglerProps, getCounterProps } + return { clapState, updateClapState, togglerProps, counterProps } } /** @@ -224,12 +222,7 @@ const CountTotal = ({ countTotal, setRef, ...restProps }) => { * Usage */ const Usage = () => { - const { - clapState, - updateClapState, - getTogglerProps, - getCounterProps - } = useClapState() + const { clapState, togglerProps, counterProps } = useClapState() const { count, countTotal, isClicked } = clapState @@ -245,26 +238,10 @@ const Usage = () => { animationTimeline.replay() }, [count]) - const handleClick = () => { - console.log('CLICKED!!!!') - } - return ( - - {/* */} - 🇳🇬 - + + + { + const [animationTimeline, setAnimationTimeline] = useState( + () => new mojs.Timeline() + ) + + useLayoutEffect(() => { + if (!clapEl || !countEl || !clapTotalEl) { + return + } + + const tlDuration = 300 + const scaleButton = new mojs.Html({ + el: clapEl, + duration: tlDuration, + scale: { 1.3: 1 }, + easing: mojs.easing.ease.out + }) + + const triangleBurst = new mojs.Burst({ + parent: clapEl, + radius: { 50: 95 }, + count: 5, + angle: 30, + children: { + shape: 'polygon', + radius: { 6: 0 }, + stroke: 'rgba(211,54,0,0.5)', + strokeWidth: 2, + angle: 210, + delay: 30, + speed: 0.2, + easing: mojs.easing.bezier(0.1, 1, 0.3, 1), + duration: tlDuration + } + }) + + const circleBurst = new mojs.Burst({ + parent: clapEl, + radius: { 50: 75 }, + angle: 25, + duration: tlDuration, + children: { + shape: 'circle', + fill: 'rgba(149,165,166,0.5)', + delay: 30, + speed: 0.2, + radius: { 3: 0 }, + easing: mojs.easing.bezier(0.1, 1, 0.3, 1) + } + }) + + const countAnimation = new mojs.Html({ + el: countEl, + opacity: { 0: 1 }, + y: { 0: -30 }, + duration: tlDuration + }).then({ + opacity: { 1: 0 }, + y: -80, + delay: tlDuration / 2 + }) + + const countTotalAnimation = new mojs.Html({ + el: clapTotalEl, + opacity: { 0: 1 }, + delay: (3 * tlDuration) / 2, + duration: tlDuration, + y: { 0: -3 } + }) + + if (typeof clapEl === 'string') { + const clap = document.getElementById('clap') + clap.style.transform = 'scale(1,1)' + } else { + clapEl.style.transform = 'scale(1,1)' + } + + const newAnimationTimeline = animationTimeline.add([ + scaleButton, + countTotalAnimation, + countAnimation, + triangleBurst, + circleBurst + ]) + setAnimationTimeline(newAnimationTimeline) + }, [clapEl, countEl, clapTotalEl]) + + return animationTimeline +} + +/** + * useDOMRef Hook + */ +const useDOMRef = () => { + const [DOMRef, setRefState] = useState({}) + + const setRef = useCallback(node => { + setRefState(prevRefState => ({ + ...prevRefState, + [node.dataset.refkey]: node + })) + }, []) + + return [DOMRef, setRef] +} +// const handleClick = (evt) => { ... } +// +const callFnsInSequence = (...fns) => (...args) => { + fns.forEach(fn => fn && fn(...args)) +} + +/** + * custom hook for useClapState + */ +const useClapState = (initialState = INITIAL_STATE) => { + const MAXIMUM_USER_CLAP = 50 + const [clapState, setClapState] = useState(initialState) + const { count, countTotal } = clapState + + const updateClapState = useCallback(() => { + setClapState(({ count, countTotal }) => ({ + isClicked: true, + count: Math.min(count + 1, MAXIMUM_USER_CLAP), + countTotal: count < MAXIMUM_USER_CLAP ? countTotal + 1 : countTotal + })) + }, [count, countTotal]) + + const getTogglerProps = ({ onClick, ...otherProps } = {}) => ({ + onClick: callFnsInSequence(updateClapState, onClick), + 'aria-pressed': clapState.isClicked, + ...otherProps + }) + + const getCounterProps = ({ ...otherProps }) => ({ + count, + 'aria-valuemax': MAXIMUM_USER_CLAP, + 'aria-valuemin': 0, + 'aria-valuenow': count, + ...otherProps + }) + + return { clapState, updateClapState, getTogglerProps, getCounterProps } +} + +/** + * custom useEffectAfterMount hook + */ +const useEffectAfterMount = (cb, deps) => { + const componentJustMounted = useRef(true) + useEffect(() => { + if (!componentJustMounted.current) { + return cb() + } + componentJustMounted.current = false + }, deps) +} + +/** + * subcomponents + */ + +const ClapContainer = ({ children, setRef, handleClick, ...restProps }) => { + return ( + + ) +} +const ClapIcon = ({ isClicked }) => { + return ( + + + + + + + ) +} +const ClapCount = ({ count, setRef, ...restProps }) => { + return ( + + + {count} + + ) +} + +const CountTotal = ({ countTotal, setRef, ...restProps }) => { + return ( + + {countTotal} + + ) +} + +/** + * Usage + */ +const Usage = () => { + const { + clapState, + updateClapState, + getTogglerProps, + getCounterProps + } = useClapState() + + const { count, countTotal, isClicked } = clapState + + const [{ clapRef, clapCountRef, clapTotalRef }, setRef] = useDOMRef() + + const animationTimeline = useClapAnimation({ + clapEl: clapRef, + countEl: clapCountRef, + clapTotalEl: clapTotalRef + }) + + useEffectAfterMount(() => { + animationTimeline.replay() + }, [count]) + + const handleClick = () => { + console.log('CLICKED!!!!') + } + + return ( + + {/* */} + 🇳🇬 + + + + ) +} + +export default Usage From 5a20524498187034961b5ff6d5c1c968a725942f Mon Sep 17 00:00:00 2001 From: Ohans Emmanuel Date: Mon, 3 Feb 2020 14:31:02 +0100 Subject: [PATCH 33/41] fulfil initial requirements for state init --- showcase/src/patterns/07.js | 5 - showcase/src/patterns/08.js | 10 +- showcase/src/patterns/09.js | 295 ++++++++++++++++++++++++++++++++++++ 3 files changed, 297 insertions(+), 13 deletions(-) create mode 100644 showcase/src/patterns/09.js diff --git a/showcase/src/patterns/07.js b/showcase/src/patterns/07.js index c335bc2..2b7bec0 100644 --- a/showcase/src/patterns/07.js +++ b/showcase/src/patterns/07.js @@ -122,11 +122,6 @@ const useDOMRef = () => { return [DOMRef, setRef] } -// const handleClick = (evt) => { ... } -// + ) +} +const ClapIcon = ({ isClicked }) => { + return ( + + + + + + + ) +} +const ClapCount = ({ count, setRef, ...restProps }) => { + return ( + + + {count} + + ) +} + +const CountTotal = ({ countTotal, setRef, ...restProps }) => { + return ( + + {countTotal} + + ) +} + +/** + * Usage + */ +const userInitialState = { + count: 0, + countTotal: 1000, + isClicked: false +} + +const Usage = () => { + const { clapState, getTogglerProps, getCounterProps, reset } = useClapState( + userInitialState + ) + + const { count, countTotal, isClicked } = clapState + + const [{ clapRef, clapCountRef, clapTotalRef }, setRef] = useDOMRef() + + const animationTimeline = useClapAnimation({ + clapEl: clapRef, + countEl: clapCountRef, + clapTotalEl: clapTotalRef + }) + + useEffectAfterMount(() => { + animationTimeline.replay() + }, [count]) + + const handleClick = () => { + console.log('CLICKED!!!!') + } + + return ( +
+ + + + + +
+ +
+ {JSON.stringify({ count, countTotal, isClicked })}
+ 
+
+
+ ) +} +export default Usage From 77c41d0950bd39a7a3c946f2395022e56bded6b9 Mon Sep 17 00:00:00 2001 From: Ohans Emmanuel Date: Thu, 6 Feb 2020 06:47:14 +0100 Subject: [PATCH 34/41] resolve state init --- showcase/src/patterns/09.js | 55 +++++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 6 deletions(-) diff --git a/showcase/src/patterns/09.js b/showcase/src/patterns/09.js index cfde5b8..8ed86fb 100644 --- a/showcase/src/patterns/09.js +++ b/showcase/src/patterns/09.js @@ -123,6 +123,18 @@ const useDOMRef = () => { return [DOMRef, setRef] } +/** + * + * custom hook for getting preivous prop/state + */ +const usePrevious = value => { + const ref = useRef() + useEffect(() => { + ref.current = value + }) + return ref.current +} + // const handleClick = (evt) => { ... } //
) From 5d27fe60d6260c186de5e701e2ebb3b0f2235c30 Mon Sep 17 00:00:00 2001 From: Ohans Emmanuel Date: Thu, 6 Feb 2020 17:51:48 +0100 Subject: [PATCH 35/41] from useState to useReducer --- showcase/src/patterns/10.js | 347 ++++++++++++++++++++++++++++++++++++ 1 file changed, 347 insertions(+) create mode 100644 showcase/src/patterns/10.js diff --git a/showcase/src/patterns/10.js b/showcase/src/patterns/10.js new file mode 100644 index 0000000..344421d --- /dev/null +++ b/showcase/src/patterns/10.js @@ -0,0 +1,347 @@ +import React, { + useState, + useLayoutEffect, + useCallback, + useRef, + useEffect, + useReducer +} from 'react' +import mojs from 'mo-js' +import styles from './index.css' +import userStyles from './usage.css' + +const INITIAL_STATE = { + count: 0, + countTotal: 267, + isClicked: false +} + +/** + * Custom Hook for animation + */ +const useClapAnimation = ({ clapEl, countEl, clapTotalEl }) => { + const [animationTimeline, setAnimationTimeline] = useState( + () => new mojs.Timeline() + ) + + useLayoutEffect(() => { + if (!clapEl || !countEl || !clapTotalEl) { + return + } + + const tlDuration = 300 + const scaleButton = new mojs.Html({ + el: clapEl, + duration: tlDuration, + scale: { 1.3: 1 }, + easing: mojs.easing.ease.out + }) + + const triangleBurst = new mojs.Burst({ + parent: clapEl, + radius: { 50: 95 }, + count: 5, + angle: 30, + children: { + shape: 'polygon', + radius: { 6: 0 }, + stroke: 'rgba(211,54,0,0.5)', + strokeWidth: 2, + angle: 210, + delay: 30, + speed: 0.2, + easing: mojs.easing.bezier(0.1, 1, 0.3, 1), + duration: tlDuration + } + }) + + const circleBurst = new mojs.Burst({ + parent: clapEl, + radius: { 50: 75 }, + angle: 25, + duration: tlDuration, + children: { + shape: 'circle', + fill: 'rgba(149,165,166,0.5)', + delay: 30, + speed: 0.2, + radius: { 3: 0 }, + easing: mojs.easing.bezier(0.1, 1, 0.3, 1) + } + }) + + const countAnimation = new mojs.Html({ + el: countEl, + opacity: { 0: 1 }, + y: { 0: -30 }, + duration: tlDuration + }).then({ + opacity: { 1: 0 }, + y: -80, + delay: tlDuration / 2 + }) + + const countTotalAnimation = new mojs.Html({ + el: clapTotalEl, + opacity: { 0: 1 }, + delay: (3 * tlDuration) / 2, + duration: tlDuration, + y: { 0: -3 } + }) + + if (typeof clapEl === 'string') { + const clap = document.getElementById('clap') + clap.style.transform = 'scale(1,1)' + } else { + clapEl.style.transform = 'scale(1,1)' + } + + const newAnimationTimeline = animationTimeline.add([ + scaleButton, + countTotalAnimation, + countAnimation, + triangleBurst, + circleBurst + ]) + setAnimationTimeline(newAnimationTimeline) + }, [clapEl, countEl, clapTotalEl]) + + return animationTimeline +} + +/** + * useDOMRef Hook + */ +const useDOMRef = () => { + const [DOMRef, setRefState] = useState({}) + + const setRef = useCallback(node => { + setRefState(prevRefState => ({ + ...prevRefState, + [node.dataset.refkey]: node + })) + }, []) + + return [DOMRef, setRef] +} +/** + * + * custom hook for getting preivous prop/state + */ +const usePrevious = value => { + const ref = useRef() + useEffect(() => { + ref.current = value + }) + return ref.current +} + +// const handleClick = (evt) => { ... } +// + ) +} +const ClapIcon = ({ isClicked }) => { + return ( + + + + + + + ) +} +const ClapCount = ({ count, setRef, ...restProps }) => { + return ( + + + {count} + + ) +} + +const CountTotal = ({ countTotal, setRef, ...restProps }) => { + return ( + + {countTotal} + + ) +} + +/** + * Usage + */ +const userInitialState = { + count: 0, + countTotal: 1000, + isClicked: false +} + +const Usage = () => { + const { + clapState, + getTogglerProps, + getCounterProps, + reset, + resetDep + } = useClapState(userInitialState) + + const { count, countTotal, isClicked } = clapState + + const [{ clapRef, clapCountRef, clapTotalRef }, setRef] = useDOMRef() + + const animationTimeline = useClapAnimation({ + clapEl: clapRef, + countEl: clapCountRef, + clapTotalEl: clapTotalRef + }) + + useEffectAfterMount(() => { + animationTimeline.replay() + }, [count]) + + const [uploadingReset, setUpload] = useState(false) + useEffectAfterMount(() => { + setUpload(true) + + const id = setTimeout(() => { + setUpload(false) + }, 3000) + + return () => clearTimeout(id) + }, [resetDep]) + + const handleClick = () => { + console.log('CLICKED!!!!') + } + + return ( +
+ + + + + +
+ +
+ {JSON.stringify({ count, countTotal, isClicked })}
+ 
+
+ {uploadingReset ? `uploading reset ${resetDep} ...` : ''}
+ 
+
+
+ ) +} +export default Usage From d4a68b71249da6e8a8ce3fed38e214008ddbced4 Mon Sep 17 00:00:00 2001 From: Ohans Emmanuel Date: 2020年2月12日 20:36:11 +0100 Subject: [PATCH 36/41] complete state reducer --- showcase/src/patterns/10.js | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/showcase/src/patterns/10.js b/showcase/src/patterns/10.js index 344421d..b3d6a0d 100644 --- a/showcase/src/patterns/10.js +++ b/showcase/src/patterns/10.js @@ -146,7 +146,7 @@ const callFnsInSequence = (...fns) => (...args) => { * custom hook for useClapState */ const MAXIMUM_USER_CLAP = 50 -const reducer = ({ count, countTotal }, { type, payload }) => { +const internalReducer = ({ count, countTotal }, { type, payload }) => { switch (type) { case 'clap': return { @@ -160,7 +160,10 @@ const reducer = ({ count, countTotal }, { type, payload }) => { break } } -const useClapState = (initialState = INITIAL_STATE) => { +const useClapState = ( + initialState = INITIAL_STATE, + reducer = internalReducer +) => { const userInitialState = useRef(initialState) const [clapState, dispatch] = useReducer(reducer, initialState) @@ -202,6 +205,12 @@ const useClapState = (initialState = INITIAL_STATE) => { } } +useClapState.reducer = internalReducer +useClapState.types = { + clap: 'clap', + reset: 'reset' +} + /** * custom useEffectAfterMount hook */ @@ -271,13 +280,22 @@ const userInitialState = { } const Usage = () => { + const [timesClapped, setTimeClapped] = useState(0) + const isClappedTooMuch = timesClapped>= 7 // true/false + const reducer = (state, action) => { + if (action.type === useClapState.types.clap && isClappedTooMuch) { + return state + } + return useClapState.reducer(state, action) + } + const { clapState, getTogglerProps, getCounterProps, reset, resetDep - } = useClapState(userInitialState) + } = useClapState(userInitialState, reducer) const { count, countTotal, isClicked } = clapState @@ -296,6 +314,7 @@ const Usage = () => { const [uploadingReset, setUpload] = useState(false) useEffectAfterMount(() => { setUpload(true) + setTimeClapped(0) const id = setTimeout(() => { setUpload(false) @@ -305,7 +324,7 @@ const Usage = () => { }, [resetDep]) const handleClick = () => { - console.log('CLICKED!!!!') + setTimeClapped(t => t + 1) } return ( @@ -335,11 +354,16 @@ const Usage = () => { reset
- {JSON.stringify({ count, countTotal, isClicked })}
+ {JSON.stringify({ timesClapped, count, countTotal })}
 
 {uploadingReset ? `uploading reset ${resetDep} ...` : ''}
 
+
+ {isClappedTooMuch
+ ? `You have clapped too much. Don't be so generous!`
+ : ''}
+ 
) From 4b394f56be60fc38d19764092bd8b8238efcd392 Mon Sep 17 00:00:00 2001 From: Ohans Emmanuel Date: 2020年2月20日 08:40:04 +0100 Subject: [PATCH 37/41] open default browser OS agnostic --- showcase/webpack.config.dev.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/showcase/webpack.config.dev.js b/showcase/webpack.config.dev.js index cc41701..dae14c8 100644 --- a/showcase/webpack.config.dev.js +++ b/showcase/webpack.config.dev.js @@ -5,7 +5,7 @@ module.exports = merge(baseConfig, { mode: 'development', devServer: { port: 4646, - open: 'Google Chrome', + open: true, overlay: { warnings: true, errors: true From 8b2dbf2d7d7805a94a3fa4fbcf75fc3ad8ab1b80 Mon Sep 17 00:00:00 2001 From: Ohans Emmanuel Date: 2020年2月26日 05:35:05 +0100 Subject: [PATCH 38/41] fix reload issue --- showcase/webpack.config.base.js | 3 ++- showcase/webpack.config.dev.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/showcase/webpack.config.base.js b/showcase/webpack.config.base.js index 085c1a0..f375cdb 100644 --- a/showcase/webpack.config.base.js +++ b/showcase/webpack.config.base.js @@ -5,7 +5,8 @@ module.exports = { entry: './src/index.js', output: { filename: 'app.bundle.js', - path: path.join(__dirname, 'dist') + path: path.join(__dirname, 'dist'), + publicPath: '/' }, module: { rules: [ diff --git a/showcase/webpack.config.dev.js b/showcase/webpack.config.dev.js index dae14c8..e5a0f7a 100644 --- a/showcase/webpack.config.dev.js +++ b/showcase/webpack.config.dev.js @@ -10,7 +10,8 @@ module.exports = merge(baseConfig, { warnings: true, errors: true }, - hot: true + hot: true, + historyApiFallback: true }, devtool: 'source-map' }) From dfd7017b1f2edfdf2345c40cc551ab48111831e0 Mon Sep 17 00:00:00 2001 From: Ohans Emmanuel Date: 2020年2月26日 06:07:40 +0100 Subject: [PATCH 39/41] fix refresh --- showcase/webpack.config.dev.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/showcase/webpack.config.dev.js b/showcase/webpack.config.dev.js index e5a0f7a..c8f056e 100644 --- a/showcase/webpack.config.dev.js +++ b/showcase/webpack.config.dev.js @@ -10,8 +10,8 @@ module.exports = merge(baseConfig, { warnings: true, errors: true }, - hot: true, - historyApiFallback: true + historyApiFallback: true, + hot: true }, devtool: 'source-map' }) From 2069b8ac7f083f088024e45ca6ebc35612c4bead Mon Sep 17 00:00:00 2001 From: ohansemmanuel Date: 2020年3月17日 11:39:11 +0100 Subject: [PATCH 40/41] fix: remove if clause from reset fn --- showcase/src/patterns/10.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/showcase/src/patterns/10.js b/showcase/src/patterns/10.js index b3d6a0d..34951a4 100644 --- a/showcase/src/patterns/10.js +++ b/showcase/src/patterns/10.js @@ -175,10 +175,8 @@ const useClapState = ( const resetRef = useRef(0) const prevCount = usePrevious(count) const reset = useCallback(() => { - if (prevCount !== count) { - dispatch({ type: 'reset', payload: userInitialState.current }) - resetRef.current++ - } + dispatch({ type: 'reset', payload: userInitialState.current }) + resetRef.current++ }, [prevCount, count, dispatch]) const getTogglerProps = ({ onClick, ...otherProps } = {}) => ({ From e05fbd3a2f4b656f5ee8f788abf7c837a0acfbed Mon Sep 17 00:00:00 2001 From: ohansemmanuel Date: 2020年3月17日 13:27:37 +0100 Subject: [PATCH 41/41] add useful comment --- showcase/src/patterns/10.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/showcase/src/patterns/10.js b/showcase/src/patterns/10.js index 34951a4..40bba52 100644 --- a/showcase/src/patterns/10.js +++ b/showcase/src/patterns/10.js @@ -175,6 +175,9 @@ const useClapState = ( const resetRef = useRef(0) const prevCount = usePrevious(count) const reset = useCallback(() => { + // ⚠️ The video lesson had this wrapped in an if statement which I've removed ... + // owing to the bug opened by Matija here https://www.udemy.com/instructor/communication/qa/9651560/detail/ + dispatch({ type: 'reset', payload: userInitialState.current }) resetRef.current++ }, [prevCount, count, dispatch])

AltStyle によって変換されたページ (->オリジナル) /