From 0a8c95109c68f4f38f64603dbb287137b8a9b8da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateo=20Guzm=C3=A1n?= Date: Sun, 4 Sep 2022 22:22:37 +0200 Subject: [PATCH] feat(context): adding fiesta context --- example/src/App.tsx | 150 ++-------------------------- example/src/components/Examples.tsx | 147 +++++++++++++++++++++++++++ src/components/Balloons.tsx | 47 +++++---- src/components/Popper.tsx | 39 +++++++- src/contexts/FiestaContext.tsx | 62 ++++++++++++ src/contexts/index.ts | 1 + src/index.tsx | 1 + 7 files changed, 280 insertions(+), 167 deletions(-) create mode 100644 example/src/components/Examples.tsx create mode 100644 src/contexts/FiestaContext.tsx create mode 100644 src/contexts/index.ts diff --git a/example/src/App.tsx b/example/src/App.tsx index 9f00767..4343953 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -1,117 +1,15 @@ -import React, { ReactChild, useRef, useState } from 'react'; -import { - SafeAreaView, - StyleSheet, - Text, - TouchableOpacity, - View, -} from 'react-native'; -import { - Balloons, - Fireworks, - FiestaThemes, - Stars, - Hearts, - Balloon, - Star, - Heart, - Firework, - EmojiPopper, - Emoji, - PopperHandler, -} from 'react-native-fiesta'; -import Content from './components/Content'; -import { Canvas, useFont } from '@shopify/react-native-skia'; +import React from 'react'; +import { SafeAreaView, StyleSheet } from 'react-native'; +import { FiestaProvider } from 'react-native-fiesta'; -function App() { - const font = useFont(require('./fonts/OpenMoji-Color.ttf'), 30); - const [lightMode, setLightMode] = useState(false); - const [componentToRender, setComponentToRender] = useState( - null - ); - const textColor = lightMode ? styles.textLightColor : styles.textDarkColor; - const theme = lightMode ? FiestaThemes.Dark : FiestaThemes.Halloween; - - const heartsRef = useRef(null); - - if (!font) return null; +import { Examples } from './components/Examples'; +function App() { return ( - - setLightMode((mode) => !mode)} - /> - - - { - setComponentToRender(); - }} - style={styles.pressable} - > - - - - Balloons - - - setComponentToRender()} - style={styles.pressable} - > - - - - Stars - - - heartsRef?.current?.start()} - style={styles.pressable} - > - - - - Hearts - - - setComponentToRender()} - style={styles.pressable} - > - - - - Fireworks - - - - setComponentToRender( - - ) - } - style={styles.pressable} - > - - - - - Emoji Popper - - - - - - {componentToRender} + + + + ); } @@ -120,36 +18,6 @@ const styles = StyleSheet.create({ container: { flex: 1, }, - darkMode: { - backgroundColor: 'black', - }, - lightMode: { - backgroundColor: 'white', - }, - canvas: { - height: 80, - width: 100, - }, - column: { - flex: 1, - flexDirection: 'column', - paddingTop: 20, - }, - pressable: { - marginHorizontal: 8, - borderBottomWidth: 1, - borderColor: 'rgba(255, 0, 255, 0.4)', - padding: 4, - alignItems: 'center', - flexDirection: 'row', - width: '100%', - }, - pressableText: { - color: 'white', - fontSize: 20, - }, - textLightColor: { color: 'black' }, - textDarkColor: { color: 'white' }, }); export default App; diff --git a/example/src/components/Examples.tsx b/example/src/components/Examples.tsx new file mode 100644 index 0000000..b34354f --- /dev/null +++ b/example/src/components/Examples.tsx @@ -0,0 +1,147 @@ +import React, { ReactChild, useState } from 'react'; +import { StyleSheet, Text, TouchableOpacity, View } from 'react-native'; +import { + Fireworks, + FiestaThemes, + Stars, + Balloon, + Star, + Heart, + Firework, + EmojiPopper, + Emoji, + useFiesta, + FiestaAnimations, +} from 'react-native-fiesta'; +import Content from './Content'; +import { Canvas, useFont } from '@shopify/react-native-skia'; + +export function Examples() { + const { runFiestaAnimation } = useFiesta(); + const font = useFont(require('../fonts/OpenMoji-Color.ttf'), 30); + const [lightMode, setLightMode] = useState(false); + const [componentToRender, setComponentToRender] = useState( + null + ); + const textColor = lightMode ? styles.textLightColor : styles.textDarkColor; + const theme = lightMode ? FiestaThemes.Dark : FiestaThemes.Halloween; + + if (!font) return null; + + return ( + + setLightMode((mode) => !mode)} + /> + + + {/* Example using Fiesta context */} + + runFiestaAnimation({ animation: FiestaAnimations.Balloons }) + } + style={styles.pressable} + > + + + + Balloons + + + {/* Example using Fiesta context */} + + runFiestaAnimation({ animation: FiestaAnimations.Hearts }) + } + style={styles.pressable} + > + + + + Hearts + + + setComponentToRender()} + style={styles.pressable} + > + + + + Stars + + + setComponentToRender()} + style={styles.pressable} + > + + + + Fireworks + + + + setComponentToRender( + + ) + } + style={styles.pressable} + > + + + + + Emoji Popper + + + + {componentToRender} + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + darkMode: { + backgroundColor: 'black', + }, + lightMode: { + backgroundColor: 'white', + }, + canvas: { + height: 80, + width: 100, + }, + column: { + flex: 1, + flexDirection: 'column', + paddingTop: 20, + }, + pressable: { + marginHorizontal: 8, + borderBottomWidth: 1, + borderColor: 'rgba(255, 0, 255, 0.4)', + padding: 4, + alignItems: 'center', + flexDirection: 'row', + width: '100%', + }, + pressableText: { + color: 'white', + fontSize: 20, + }, + textLightColor: { color: 'black' }, + textDarkColor: { color: 'white' }, +}); diff --git a/src/components/Balloons.tsx b/src/components/Balloons.tsx index 3fc6546..f04660a 100644 --- a/src/components/Balloons.tsx +++ b/src/components/Balloons.tsx @@ -1,6 +1,6 @@ -import React from 'react'; +import React, { forwardRef } from 'react'; import { Balloon } from './Balloon'; -import { Popper, PopperProps } from './Popper'; +import { Popper, PopperHandler, PopperProps } from './Popper'; import { screenWidth } from '../constants/dimensions'; import { getBalloonsYPositions } from '../utils/balloons'; @@ -15,23 +15,26 @@ const balloonsYPositions = getBalloonsYPositions( export interface BalloonsProps extends Omit {} -export function Balloons({ spacing = SPACING, ...props }: BalloonsProps) { - return ( - ( - - )} - {...props} - /> - ); -} +export const Balloons = forwardRef( + ({ spacing = SPACING, ...props }: BalloonsProps, ref) => { + return ( + ( + + )} + {...props} + ref={ref} + /> + ); + } +); diff --git a/src/components/Popper.tsx b/src/components/Popper.tsx index 883069b..4966bbc 100644 --- a/src/components/Popper.tsx +++ b/src/components/Popper.tsx @@ -6,6 +6,7 @@ import React, { useImperativeHandle, useMemo, ForwardedRef, + useState, } from 'react'; import { StyleSheet } from 'react-native'; import { @@ -55,6 +56,8 @@ export const Popper = memo( }: PopperProps, ref: PopperRef ) => { + const [displayCanvas, setDisplayCanvas] = useState(autoPlay); + const optimalNumberOfItems = Math.floor(screenWidth / spacing); const itemsToRenderArray = [...Array(optimalNumberOfItems)]; @@ -83,18 +86,41 @@ export const Popper = memo( [containerYPosition] ); + // Once the animation finishes, we proceed to hiding the canvas to avoid blocking the UI + useEffect(() => { + const unsubscribe = containerYPosition.addListener((value) => { + if (value < -250 && displayCanvas) { + setDisplayCanvas(false); + containerYPosition.current = screenHeight; + } + }); + + return () => { + unsubscribe(); + }; + }, [containerYPosition, displayCanvas]); + useImperativeHandle(ref, () => ({ start() { - changeItemPosition(); + setDisplayCanvas(true); }, })); useEffect(() => { - autoPlay && changeItemPosition(); - }, [changeItemPosition, autoPlay]); + displayCanvas && changeItemPosition(); + }, [displayCanvas, changeItemPosition]); + + if (!displayCanvas) return null; return ( - + {itemsToRenderArray.map((_, index) => renderItem( @@ -116,6 +142,11 @@ const styles = StyleSheet.create({ left: 0, right: 0, bottom: 0, + }, + canvasBehind: { zIndex: -1, }, + canvasInFront: { + zIndex: 1, + }, }); diff --git a/src/contexts/FiestaContext.tsx b/src/contexts/FiestaContext.tsx new file mode 100644 index 0000000..322a5ac --- /dev/null +++ b/src/contexts/FiestaContext.tsx @@ -0,0 +1,62 @@ +import React, { createContext, useContext, useRef } from 'react'; +import { + Balloons as _Balloons, + Hearts as _Hearts, + PopperHandler, +} from '../components'; + +export enum FiestaAnimations { + Hearts = 'Hearts', + Balloons = 'Balloons', +} + +interface RunFiestaAnimationParams { + animation: FiestaAnimations; +} + +interface FiestContextType { + runFiestaAnimation(params: RunFiestaAnimationParams): void; +} + +export const FiestaContext = createContext({ + runFiestaAnimation: () => {}, +}); + +export const FiestaProvider: React.FC = ({ children }) => { + const balloonsRef = useRef(null); + const heartsRef = useRef(null); + + const runFiestaAnimation = ({ animation }: RunFiestaAnimationParams) => { + switch (animation) { + case FiestaAnimations.Balloons: + balloonsRef.current?.start(); + break; + case FiestaAnimations.Hearts: + heartsRef.current?.start(); + break; + default: + balloonsRef.current?.start(); + } + }; + + return ( + + <_Balloons autoPlay={false} ref={balloonsRef} /> + <_Hearts autoPlay={false} ref={heartsRef} /> + + {children} + + ); +}; + +export const useFiesta = () => { + const { runFiestaAnimation } = useContext(FiestaContext); + + return { + runFiestaAnimation, + }; +}; diff --git a/src/contexts/index.ts b/src/contexts/index.ts new file mode 100644 index 0000000..1cbcf72 --- /dev/null +++ b/src/contexts/index.ts @@ -0,0 +1 @@ +export * from './FiestaContext'; diff --git a/src/index.tsx b/src/index.tsx index b6ad3a8..7167bdb 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,2 +1,3 @@ export * from './components'; export * from './constants'; +export * from './contexts';