From 2d034c07864ce02ab9b60e121609d258cc29cf72 Mon Sep 17 00:00:00 2001 From: Reagan Middlebrook Date: Wed, 15 Feb 2023 08:57:35 -0800 Subject: [PATCH 01/15] Generate typescript declaration files --- .gitignore | 4 +++- index.d.ts | 32 ++++++++++++++++++++++++++++++++ index.d.ts.map | 1 + package.json | 7 ++++--- tsconfig.json | 14 ++++++++++++++ 5 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 index.d.ts create mode 100644 index.d.ts.map create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore index 40b878d..9684106 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -node_modules/ \ No newline at end of file +node_modules/ +.idea/ +package-lock.json \ No newline at end of file diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..657c949 --- /dev/null +++ b/index.d.ts @@ -0,0 +1,32 @@ +export default class AnimatedEllipsis extends Component { + static propTypes: { + numberOfDots: any; + animationDelay: any; + minOpacity: any; + style: any; + useNativeDriver: any; + }; + static defaultProps: { + numberOfDots: number; + animationDelay: number; + minOpacity: number; + style: { + color: string; + fontSize: number; + }; + useNativeDriver: boolean; + }; + constructor(props: any); + _animation_state: { + dot_opacities: any[]; + target_opacity: number; + should_animate: boolean; + }; + initializeDots(): any[]; + componentDidMount(): void; + componentWillUnmount(): void; + animate_dots(which_dot: any): void; + render(): JSX.Element; +} +import { Component } from "react"; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/index.d.ts.map b/index.d.ts.map new file mode 100644 index 0000000..6715876 --- /dev/null +++ b/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.js"],"names":[],"mappings":"AAMA;IACE;;;;;;MAME;IAEF;;;;;;;;;MASE;IAEF,wBAQC;IALC;;;;MAIC;IAGH,wBASC;IAED,0BAEC;IAED,6BAEC;IAED,mCAkBC;IAED,sBASC;CACF"} \ No newline at end of file diff --git a/package.json b/package.json index 7ae95d8..bfe0e52 100644 --- a/package.json +++ b/package.json @@ -16,12 +16,13 @@ "licenseFilename": "LICENSE", "readmeFilename": "README.md", "dependencies": { - "prop-types": "^15.5.10", "babel-preset-stage-2": "^6.24.1", - "deprecated-react-native-prop-types": "^2.3.0" + "deprecated-react-native-prop-types": "^2.3.0", + "prop-types": "^15.5.10" }, "devDependencies": { "babel-cli": "^6.24.1", - "babel-plugin-transform-react-jsx": "^6.24.1" + "babel-plugin-transform-react-jsx": "^6.24.1", + "typescript": "^4.9.5" } } diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..06c8a3f --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,14 @@ +{ + "include": ["*"], + + "compilerOptions": { + // Read JS files as source + "allowJs": true, + // Generate .d.ts files + "declaration": true, + // Compiler run should only output .d.ts files + "emitDeclarationOnly": true, + // Let 'Go to definition'-type IDE functions find our js files + "declarationMap": true, + } +} From f3dbd3092af069ad1319da501fd4435ebbb5cd29 Mon Sep 17 00:00:00 2001 From: Reagan Middlebrook Date: Thu, 5 Dec 2024 21:53:12 -0800 Subject: [PATCH 02/15] Set up as @reagankm/rn-animated-ellipsis --- .gitignore | 3 ++- README.md | 16 +++++++++++++++- index.d.ts | 50 ++++++++++++++++++++++---------------------------- package.json | 28 +++++++++++++++++++--------- 4 files changed, 58 insertions(+), 39 deletions(-) diff --git a/.gitignore b/.gitignore index 9684106..5b3930a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules/ .idea/ -package-lock.json \ No newline at end of file +package-lock.json +.DS_Store \ No newline at end of file diff --git a/README.md b/README.md index 77df975..49542cd 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,22 @@ +# rn-animated-ellipsis-extended + +This is a fork of [rn-animated-ellipsis](https://github.com/Thanhal-P-A/rn-animated-ellipsis) by +[Thanhal-P-A](https://github.com/Thanhal-P-A), which is a fork of +[react-native-animated-ellipsis](https://github.com/adorableio/react-native-animated-ellipsis) +by [adorableio](https://github.com/adorableio). + +This fork includes TypeScript types and additional updates. + # React Native Animated Ellipsis -A simple, customizable animated dots component ideal for loading screens in React Native apps (forked from adorableio/react-native-animated-ellipsis - not maintained). + +A simple, customizable animated dots component ideal for loading screens in +React Native apps (forked from Thanhal-P-A/rn-animated-ellipsis - not maintained). ![Kinda like iOS](https://raw.githubusercontent.com/wiki/adorableio/react-native-animated-ellipsis/images/example_ios_ish.gif) +## Supported Versions +- **React:** >=18.0.0 +- **React Native:** >=0.70.0 ## Installation using npm diff --git a/index.d.ts b/index.d.ts index 657c949..815c9fc 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,32 +1,26 @@ -export default class AnimatedEllipsis extends Component { - static propTypes: { - numberOfDots: any; - animationDelay: any; - minOpacity: any; - style: any; - useNativeDriver: any; - }; - static defaultProps: { - numberOfDots: number; - animationDelay: number; - minOpacity: number; - style: { - color: string; - fontSize: number; - }; - useNativeDriver: boolean; - }; - constructor(props: any); - _animation_state: { - dot_opacities: any[]; - target_opacity: number; - should_animate: boolean; - }; - initializeDots(): any[]; +import { Component } from "react"; +import { Animated, StyleProp, TextStyle, ViewStyle } from 'react-native'; + +export interface AnimatedEllipsisProps { + numberOfDots?: number; // Number of dots to display + animationDelay?: number; // Delay between animations + minOpacity?: number; // Minimum opacity of dots + style?: StyleProp; // Styles for the dots + useNativeDriver?: boolean; // Whether to use native driver for animations +} + +export default class AnimatedEllipsis extends Component { + static defaultProps: AnimatedEllipsisProps; + + initializeDots(): Animated.Value[]; // Initializes Animated.Value objects for dots + componentDidMount(): void; + componentWillUnmount(): void; - animate_dots(which_dot: any): void; + + animate_dots(which_dot: number): void; + render(): JSX.Element; } -import { Component } from "react"; -//# sourceMappingURL=index.d.ts.map \ No newline at end of file + +//# sourceMappingURL=index.d.ts.map diff --git a/package.json b/package.json index bfe0e52..38fbff1 100644 --- a/package.json +++ b/package.json @@ -1,28 +1,38 @@ { - "name": "rn-animated-ellipsis", - "version": "2.1.3", - "description": "A simple, customizable animated dots component ideal for loading screens in React Native apps.", + "name": "@reagankm/rn-animated-ellipsis", + "version": "2.2.0", + "description": "An updated version of rn-animated-ellipsis, a simple, customizable animated dots component ideal for loading screens in React Native apps.", "main": "index.js", + "types": "index.d.ts", "author": { - "name": "Thanhal P A", - "email": "thanhalpa@gmail.com" + "name": "Reagan Middlebrook", + "email": "reagankm@gmail.com" }, "repository": { "type": "git", - "url": "git+https://github.com/thanhal-p-a/rn-animated-ellipsis.git" + "url": "git+https://github.com/reagankm/rn-animated-ellipsis.git" }, - "homepage": "https://github.com/thanhal-p-a/rn-animated-ellipsis", + "bugs": { + "url": "https://github.com/reagankm/rn-animated-ellipsis/issues" + }, + "homepage": "https://github.com/reagankm/rn-animated-ellipsis", "license": "MIT", "licenseFilename": "LICENSE", "readmeFilename": "README.md", "dependencies": { - "babel-preset-stage-2": "^6.24.1", "deprecated-react-native-prop-types": "^2.3.0", "prop-types": "^15.5.10" }, + "peerDependencies": { + "react": ">=18.0.0", + "react-native": ">=0.70.0" + }, "devDependencies": { "babel-cli": "^6.24.1", + "babel-preset-stage-2": "^6.24.1", "babel-plugin-transform-react-jsx": "^6.24.1", - "typescript": "^4.9.5" + "typescript": "^4.9.5", + "@types/react": "^18.0.0", + "@types/react-native": "^0.70.0" } } From 53dbd1cdbeb3cedc06b3fd799122bd3ffcdbfd8c Mon Sep 17 00:00:00 2001 From: Reagan Middlebrook Date: Thu, 5 Dec 2024 23:02:27 -0800 Subject: [PATCH 03/15] Use .tsx functional component instead of .js class --- AnimatedEllipsis.tsx | 109 +++++++++++++++++++++++++++++++++++++++++++ index.d.ts | 26 ----------- index.d.ts.map | 1 - index.js | 92 ------------------------------------ package.json | 5 +- tsconfig.json | 23 +++++---- 6 files changed, 124 insertions(+), 132 deletions(-) create mode 100644 AnimatedEllipsis.tsx delete mode 100644 index.d.ts delete mode 100644 index.d.ts.map delete mode 100644 index.js diff --git a/AnimatedEllipsis.tsx b/AnimatedEllipsis.tsx new file mode 100644 index 0000000..1b1756b --- /dev/null +++ b/AnimatedEllipsis.tsx @@ -0,0 +1,109 @@ +import React, { PropsWithChildren, useEffect, useState, } from 'react'; +import { + Animated, + StyleProp, + StyleSheet, + TextStyle, + View, + ViewStyle +} from 'react-native'; + +type Props = PropsWithChildren<{ + numberOfDots?: number, + animationDelay?: number, + minOpacity?: number, + style?: StyleProp, + useNativeDriver?: boolean +}>; + +interface AnimationState { + dotOpacities: Animated.Value[]; + targetOpacity: number; + shouldAnimate: boolean; +} + +const initializeDots = (numberOfDots: number, minOpacity: number): Animated.Value[] => { + let opacities: Animated.Value[] = []; + + for (let i = 0; i < numberOfDots; i++) { + let dot: Animated.Value = new Animated.Value(minOpacity); + opacities.push(dot); + } + + return opacities; +} + +const styles = StyleSheet.create({ + container: { + flexDirection: 'row' + } +}); + +const AnimatedEllipsis: React.FC = ({ + numberOfDots = 3, + animationDelay = 300, + minOpacity = 0, + style = { + color: '#aaa', + fontSize: 32, + } as StyleProp, + useNativeDriver = true + }: Props) => { + + const [animationState, setAnimationState] = + useState(() => { + return { + dotOpacities: initializeDots(numberOfDots, minOpacity), + targetOpacity: 1, + shouldAnimate: true, + } + }); + + useEffect(() => { + animateDots(0); + return () => { + setAnimationState((previousState: AnimationState) => ({ + ...previousState, + shouldAnimate: false, + })); + } + }, []); + + const animateDots = (currentDot: number): void => { + if (!animationState.shouldAnimate) return; + + // Swap fade direction when we hit the end of the list + if (currentDot >= animationState.dotOpacities.length) { + const oppositeOpacity = animationState.targetOpacity === minOpacity ? 1 : minOpacity; + + setAnimationState((previousState: AnimationState) => ({ + ...previousState, + targetOpacity: oppositeOpacity, + })); + } + + const nextDot = currentDot + 1; + + Animated.timing(animationState.dotOpacities[currentDot], { + toValue: animationState.targetOpacity, + duration: animationDelay, + useNativeDriver: useNativeDriver, + }).start(() => animateDots(nextDot)); + } + + return ( + + {animationState.dotOpacities.map((opacity, index) => ( + + {' '} + . + + ))} + + ); + +} + +export default AnimatedEllipsis; + + diff --git a/index.d.ts b/index.d.ts deleted file mode 100644 index 815c9fc..0000000 --- a/index.d.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Component } from "react"; -import { Animated, StyleProp, TextStyle, ViewStyle } from 'react-native'; - -export interface AnimatedEllipsisProps { - numberOfDots?: number; // Number of dots to display - animationDelay?: number; // Delay between animations - minOpacity?: number; // Minimum opacity of dots - style?: StyleProp; // Styles for the dots - useNativeDriver?: boolean; // Whether to use native driver for animations -} - -export default class AnimatedEllipsis extends Component { - static defaultProps: AnimatedEllipsisProps; - - initializeDots(): Animated.Value[]; // Initializes Animated.Value objects for dots - - componentDidMount(): void; - - componentWillUnmount(): void; - - animate_dots(which_dot: number): void; - - render(): JSX.Element; -} - -//# sourceMappingURL=index.d.ts.map diff --git a/index.d.ts.map b/index.d.ts.map deleted file mode 100644 index 6715876..0000000 --- a/index.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.js"],"names":[],"mappings":"AAMA;IACE;;;;;;MAME;IAEF;;;;;;;;;MASE;IAEF,wBAQC;IALC;;;;MAIC;IAGH,wBASC;IAED,0BAEC;IAED,6BAEC;IAED,mCAkBC;IAED,sBASC;CACF"} \ No newline at end of file diff --git a/index.js b/index.js deleted file mode 100644 index 9d3907d..0000000 --- a/index.js +++ /dev/null @@ -1,92 +0,0 @@ -import React, { Component } from 'react'; -import { Animated, View, StyleSheet } from 'react-native'; -import { TextPropTypes } from 'deprecated-react-native-prop-types'; -import PropTypes from 'prop-types'; - - -export default class AnimatedEllipsis extends Component { - static propTypes = { - numberOfDots: PropTypes.number, - animationDelay: PropTypes.number, - minOpacity: PropTypes.number, - style: TextPropTypes.style, - useNativeDriver: PropTypes.bool - }; - - static defaultProps = { - numberOfDots: 3, - animationDelay: 300, - minOpacity: 0, - style: { - color: '#aaa', - fontSize: 32, - }, - useNativeDriver: true - }; - - constructor(props) { - super(props); - - this._animation_state = { - dot_opacities: this.initializeDots(), - target_opacity: 1, - should_animate: true, - }; - } - - initializeDots() { - let opacities = []; - - for (let i = 0; i < this.props.numberOfDots; i++) { - let dot = new Animated.Value(this.props.minOpacity); - opacities.push(dot); - } - - return opacities; - } - - componentDidMount() { - this.animate_dots.bind(this)(0); - } - - componentWillUnmount() { - this._animation_state.should_animate = false; - } - - animate_dots(which_dot) { - if (!this._animation_state.should_animate) return; - - // swap fade direction when we hit end of list - if (which_dot >= this._animation_state.dot_opacities.length) { - which_dot = 0; - let min = this.props.minOpacity; - this._animation_state.target_opacity = - this._animation_state.target_opacity == min ? 1 : min; - } - - let next_dot = which_dot + 1; - - Animated.timing(this._animation_state.dot_opacities[which_dot], { - toValue: this._animation_state.target_opacity, - duration: this.props.animationDelay, - useNativeDriver: this.props.useNativeDriver, - }).start(this.animate_dots.bind(this, next_dot)); - } - - render() { - let dots = this._animation_state.dot_opacities.map((o, i) => - - {' '} - . - - ); - - return {dots} - } -} - -const styles = StyleSheet.create({ - container: { - flexDirection: 'row' - } -}); \ No newline at end of file diff --git a/package.json b/package.json index 38fbff1..22360f3 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,8 @@ { "name": "@reagankm/rn-animated-ellipsis", - "version": "2.2.0", + "version": "2.2.1", "description": "An updated version of rn-animated-ellipsis, a simple, customizable animated dots component ideal for loading screens in React Native apps.", - "main": "index.js", - "types": "index.d.ts", + "main": "AnimatedEllipsis.tsx", "author": { "name": "Reagan Middlebrook", "email": "reagankm@gmail.com" diff --git a/tsconfig.json b/tsconfig.json index 06c8a3f..5d5d860 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,14 +1,17 @@ { - "include": ["*"], - + //"extends": "@react-native/typescript-config/tsconfig.json", "compilerOptions": { - // Read JS files as source - "allowJs": true, - // Generate .d.ts files - "declaration": true, - // Compiler run should only output .d.ts files - "emitDeclarationOnly": true, - // Let 'Go to definition'-type IDE functions find our js files - "declarationMap": true, + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Completeness */ + //"skipLibCheck": true, /* Skip type checking all .d.ts files. */ + "jsx": "react-native", + "moduleResolution": "node", + "strictBindCallApply": true, + "allowSyntheticDefaultImports": true + //"strict": false, /* Started getting opinionated about dumb shit */ + //"suppressImplicitAnyIndexErrors": true, + //"noImplicitAny": false, + //"noImplicitThis": false, } } From 291269e2dfe8f0e1d48883100335504d59a5e2c9 Mon Sep 17 00:00:00 2001 From: Reagan Middlebrook Date: Fri, 6 Dec 2024 14:14:04 -0800 Subject: [PATCH 04/15] Update formatting for prettier/eslint --- AnimatedEllipsis.tsx | 127 ++++++++++++++---------- package.json | 78 +++++++++++++-- src/__tests__/AnimatedEllipsis.test.tsx | 0 tsconfig.json | 39 +++++--- 4 files changed, 166 insertions(+), 78 deletions(-) create mode 100644 src/__tests__/AnimatedEllipsis.test.tsx diff --git a/AnimatedEllipsis.tsx b/AnimatedEllipsis.tsx index 1b1756b..e24f6a9 100644 --- a/AnimatedEllipsis.tsx +++ b/AnimatedEllipsis.tsx @@ -1,19 +1,18 @@ -import React, { PropsWithChildren, useEffect, useState, } from 'react'; +import React, { type PropsWithChildren, useEffect, useState } from 'react'; import { Animated, - StyleProp, + type StyleProp, StyleSheet, - TextStyle, + type TextStyle, View, - ViewStyle } from 'react-native'; type Props = PropsWithChildren<{ - numberOfDots?: number, - animationDelay?: number, - minOpacity?: number, - style?: StyleProp, - useNativeDriver?: boolean + numberOfDots?: number; + animationDelay?: number; + minOpacity?: number; + style?: StyleProp; + useNativeDriver?: boolean; }>; interface AnimationState { @@ -22,7 +21,10 @@ interface AnimationState { shouldAnimate: boolean; } -const initializeDots = (numberOfDots: number, minOpacity: number): Animated.Value[] => { +const initializeDots = ( + numberOfDots: number, + minOpacity: number +): Animated.Value[] => { let opacities: Animated.Value[] = []; for (let i = 0; i < numberOfDots; i++) { @@ -31,12 +33,12 @@ const initializeDots = (numberOfDots: number, minOpacity: number): Animated.Valu } return opacities; -} +}; const styles = StyleSheet.create({ container: { - flexDirection: 'row' - } + flexDirection: 'row', + }, }); const AnimatedEllipsis: React.FC = ({ @@ -46,18 +48,57 @@ const AnimatedEllipsis: React.FC = ({ style = { color: '#aaa', fontSize: 32, - } as StyleProp, - useNativeDriver = true - }: Props) => { - - const [animationState, setAnimationState] = - useState(() => { - return { - dotOpacities: initializeDots(numberOfDots, minOpacity), - targetOpacity: 1, - shouldAnimate: true, + } as TextStyle, + useNativeDriver = true, + }: Props): React.JSX.Element => { + const [animationState, setAnimationState] = useState(() => { + return { + dotOpacities: initializeDots(numberOfDots, minOpacity), + targetOpacity: 1, + shouldAnimate: true, + }; + }); + + const animateDots = React.useCallback( + (currentDot: number): void => { + if (!animationState.shouldAnimate) return; + + // Swap fade direction when we hit the end of the list + if (currentDot >= animationState.dotOpacities.length) { + const oppositeOpacity = + animationState.targetOpacity === minOpacity + ? 1 + : minOpacity; + + setAnimationState((previousState: AnimationState) => ({ + ...previousState, + targetOpacity: oppositeOpacity, + })); + } + + const nextDot = currentDot + 1; + + if (currentDot >= animationState.dotOpacities.length) { + throw new Error( + `currentDot ${currentDot} is bigger than dotOpacities length: ${animationState.dotOpacities.length}` + ); } - }); + + Animated.timing(animationState.dotOpacities[currentDot]!, { + toValue: animationState.targetOpacity, + duration: animationDelay, + useNativeDriver: useNativeDriver, + }).start(() => animateDots(nextDot)); + }, + [ + animationState.shouldAnimate, + animationState.dotOpacities, + animationState.targetOpacity, + animationDelay, + useNativeDriver, + minOpacity, + ] + ); useEffect(() => { animateDots(0); @@ -66,44 +107,22 @@ const AnimatedEllipsis: React.FC = ({ ...previousState, shouldAnimate: false, })); - } - }, []); - - const animateDots = (currentDot: number): void => { - if (!animationState.shouldAnimate) return; - - // Swap fade direction when we hit the end of the list - if (currentDot >= animationState.dotOpacities.length) { - const oppositeOpacity = animationState.targetOpacity === minOpacity ? 1 : minOpacity; - - setAnimationState((previousState: AnimationState) => ({ - ...previousState, - targetOpacity: oppositeOpacity, - })); - } - - const nextDot = currentDot + 1; - - Animated.timing(animationState.dotOpacities[currentDot], { - toValue: animationState.targetOpacity, - duration: animationDelay, - useNativeDriver: useNativeDriver, - }).start(() => animateDots(nextDot)); - } + }; + }, [animateDots]); return ( {animationState.dotOpacities.map((opacity, index) => ( - + {' '} . ))} - ); - -} + ) as React.JSX.Element; +}; export default AnimatedEllipsis; - - diff --git a/package.json b/package.json index 22360f3..225ced7 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,15 @@ { "name": "@reagankm/rn-animated-ellipsis", - "version": "2.2.1", + "version": "2.2.5", "description": "An updated version of rn-animated-ellipsis, a simple, customizable animated dots component ideal for loading screens in React Native apps.", - "main": "AnimatedEllipsis.tsx", + "main": "src/AnimatedEllipsis.tsx", "author": { "name": "Reagan Middlebrook", "email": "reagankm@gmail.com" }, + "publishConfig": { + "registry": "https://registry.npmjs.org/" + }, "repository": { "type": "git", "url": "git+https://github.com/reagankm/rn-animated-ellipsis.git" @@ -19,19 +22,72 @@ "licenseFilename": "LICENSE", "readmeFilename": "README.md", "dependencies": { - "deprecated-react-native-prop-types": "^2.3.0", - "prop-types": "^15.5.10" + "rn-animated-ellipsis": "^2.1.3" }, "peerDependencies": { - "react": ">=18.0.0", - "react-native": ">=0.70.0" + "react": "*", + "react-native": "*" }, "devDependencies": { + "@react-native/eslint-config": "^0.73.1", + "@types/react": "^18.0.0", + "@types/react-native": "^0.70.0", "babel-cli": "^6.24.1", - "babel-preset-stage-2": "^6.24.1", "babel-plugin-transform-react-jsx": "^6.24.1", - "typescript": "^4.9.5", - "@types/react": "^18.0.0", - "@types/react-native": "^0.70.0" - } + "babel-preset-stage-2": "^6.24.1", + "eslint": "^8.51.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-prettier": "^5.0.1", + "jest": "^29.2.1", + "prettier": "^3.0.3", + "react": "^18.2.0", + "react-native": "0.75.4", + "react-test-renderer": "^18.2.0", + "typescript": "^4.9.5" + }, + "scripts": { + "test": "jest", + "typecheck": "tsc", + "lint": "eslint \"**/*.{js,ts,tsx}\"" + }, + "jest": { + "preset": "react-native" + }, + "eslintConfig": { + "root": true, + "extends": [ + "@react-native", + "prettier" + ], + "rules": { + "react/react-in-jsx-scope": "off", + "prettier/prettier": [ + "error", + { + "quoteProps": "consistent", + "singleQuote": true, + "tabWidth": 4, + "trailingComma": "es5", + "useTabs": false + } + ] + } + }, + "eslintIgnore": [ + "node_modules/" + ], + "prettier": { + "quoteProps": "consistent", + "singleQuote": true, + "tabWidth": 4, + "trailingComma": "es5", + "useTabs": false + }, + "keywords": [ + "react-native", + "animated ellipsis", + "rn-animated-ellipsis", + "react-native-animated-ellipsis", + "loading animation" + ] } diff --git a/src/__tests__/AnimatedEllipsis.test.tsx b/src/__tests__/AnimatedEllipsis.test.tsx new file mode 100644 index 0000000..e69de29 diff --git a/tsconfig.json b/tsconfig.json index 5d5d860..b9a2771 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,17 +1,30 @@ { - //"extends": "@react-native/typescript-config/tsconfig.json", "compilerOptions": { - /* Visit https://aka.ms/tsconfig.json to read more about this file */ - - /* Completeness */ - //"skipLibCheck": true, /* Skip type checking all .d.ts files. */ - "jsx": "react-native", - "moduleResolution": "node", - "strictBindCallApply": true, - "allowSyntheticDefaultImports": true - //"strict": false, /* Started getting opinionated about dumb shit */ - //"suppressImplicitAnyIndexErrors": true, - //"noImplicitAny": false, - //"noImplicitThis": false, + "rootDir": ".", + "paths": { + "rn-animated-ellipsis": ["./src/index"] + }, + "allowUnreachableCode": false, + "allowUnusedLabels": false, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "jsx": "react-jsx", + "lib": ["ESNext"], + "module": "ESNext", + "moduleResolution": "Bundler", + "noEmit": true, + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "noImplicitUseStrict": false, + "noStrictGenericChecks": false, + "noUncheckedIndexedAccess": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "resolveJsonModule": true, + "resolvePackageJsonImports": false, + "skipLibCheck": true, + "strict": true, + "target": "ESNext", + "verbatimModuleSyntax": true } } From c2be7d493b6594acb11b19e9d397c92c17548b2e Mon Sep 17 00:00:00 2001 From: Reagan Middlebrook Date: Fri, 6 Dec 2024 14:15:15 -0800 Subject: [PATCH 05/15] Move into src for better organization --- AnimatedEllipsis.tsx => src/AnimatedEllipsis.tsx | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename AnimatedEllipsis.tsx => src/AnimatedEllipsis.tsx (100%) diff --git a/AnimatedEllipsis.tsx b/src/AnimatedEllipsis.tsx similarity index 100% rename from AnimatedEllipsis.tsx rename to src/AnimatedEllipsis.tsx From eef498d26f379bb31170abf540a99d85b1c7fdcd Mon Sep 17 00:00:00 2001 From: Reagan Middlebrook Date: Fri, 6 Dec 2024 19:25:07 -0800 Subject: [PATCH 06/15] Use refs instead of state to avoid sync issues --- package.json | 11 +++--- src/AnimatedEllipsis.tsx | 76 +++++++++++++++------------------------- 2 files changed, 35 insertions(+), 52 deletions(-) diff --git a/package.json b/package.json index 225ced7..d99cedc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@reagankm/rn-animated-ellipsis", - "version": "2.2.5", + "version": "2.2.8", "description": "An updated version of rn-animated-ellipsis, a simple, customizable animated dots component ideal for loading screens in React Native apps.", "main": "src/AnimatedEllipsis.tsx", "author": { @@ -38,11 +38,11 @@ "eslint": "^8.51.0", "eslint-config-prettier": "^9.0.0", "eslint-plugin-prettier": "^5.0.1", - "jest": "^29.2.1", + "jest": "^29.7.0", "prettier": "^3.0.3", "react": "^18.2.0", "react-native": "0.75.4", - "react-test-renderer": "^18.2.0", + "react-test-renderer": "^18.3.1", "typescript": "^4.9.5" }, "scripts": { @@ -51,7 +51,10 @@ "lint": "eslint \"**/*.{js,ts,tsx}\"" }, "jest": { - "preset": "react-native" + "preset": "react-native", + "setupFilesAfterEnv": [ + "/jest-setup.js" + ] }, "eslintConfig": { "root": true, diff --git a/src/AnimatedEllipsis.tsx b/src/AnimatedEllipsis.tsx index e24f6a9..9877579 100644 --- a/src/AnimatedEllipsis.tsx +++ b/src/AnimatedEllipsis.tsx @@ -1,4 +1,4 @@ -import React, { type PropsWithChildren, useEffect, useState } from 'react'; +import React, { type PropsWithChildren, useEffect } from 'react'; import { Animated, type StyleProp, @@ -15,12 +15,6 @@ type Props = PropsWithChildren<{ useNativeDriver?: boolean; }>; -interface AnimationState { - dotOpacities: Animated.Value[]; - targetOpacity: number; - shouldAnimate: boolean; -} - const initializeDots = ( numberOfDots: number, minOpacity: number @@ -41,78 +35,64 @@ const styles = StyleSheet.create({ }, }); +const initialTargetOpacity: number = 1; + +const defaultStyle: TextStyle = { + color: '#aaa', + fontSize: 32, +}; + const AnimatedEllipsis: React.FC = ({ numberOfDots = 3, animationDelay = 300, minOpacity = 0, - style = { - color: '#aaa', - fontSize: 32, - } as TextStyle, + style = defaultStyle, useNativeDriver = true, }: Props): React.JSX.Element => { - const [animationState, setAnimationState] = useState(() => { - return { - dotOpacities: initializeDots(numberOfDots, minOpacity), - targetOpacity: 1, - shouldAnimate: true, - }; - }); + const isMountedRef = React.useRef(true); + const targetOpacityRef = React.useRef(initialTargetOpacity); + const dotOpacitiesRef = React.useRef(initializeDots(numberOfDots, minOpacity)); const animateDots = React.useCallback( (currentDot: number): void => { - if (!animationState.shouldAnimate) return; - - // Swap fade direction when we hit the end of the list - if (currentDot >= animationState.dotOpacities.length) { - const oppositeOpacity = - animationState.targetOpacity === minOpacity - ? 1 - : minOpacity; - - setAnimationState((previousState: AnimationState) => ({ - ...previousState, - targetOpacity: oppositeOpacity, - })); + if (!isMountedRef.current) return; + + if (currentDot >= dotOpacitiesRef.current.length) { + currentDot = 0; + targetOpacityRef.current = targetOpacityRef.current === minOpacity ? 1 : minOpacity; } const nextDot = currentDot + 1; - if (currentDot >= animationState.dotOpacities.length) { - throw new Error( - `currentDot ${currentDot} is bigger than dotOpacities length: ${animationState.dotOpacities.length}` - ); - } - - Animated.timing(animationState.dotOpacities[currentDot]!, { - toValue: animationState.targetOpacity, + Animated.timing(dotOpacitiesRef.current[currentDot]!, { + toValue: targetOpacityRef.current, duration: animationDelay, useNativeDriver: useNativeDriver, }).start(() => animateDots(nextDot)); }, [ - animationState.shouldAnimate, - animationState.dotOpacities, - animationState.targetOpacity, animationDelay, useNativeDriver, - minOpacity, + minOpacity ] ); useEffect(() => { + isMountedRef.current = true; animateDots(0); + return () => { - setAnimationState((previousState: AnimationState) => ({ - ...previousState, - shouldAnimate: false, - })); + isMountedRef.current = false; }; }, [animateDots]); + useEffect(() => { + dotOpacitiesRef.current = initializeDots(numberOfDots, minOpacity); + }, [numberOfDots, minOpacity]); + return ( - {animationState.dotOpacities.map((opacity, index) => ( + {dotOpacitiesRef.current.map((opacity, index) => ( Date: Fri, 6 Dec 2024 19:59:22 -0800 Subject: [PATCH 07/15] Add a test --- .babelrc | 9 +++++++-- jest.config.js | 12 ++++++++++++ jest.setup.js | 23 +++++++++++++++++++++++ package.json | 15 ++++++--------- src/__tests__/AnimatedEllipsis.test.tsx | 8 ++++++++ tsconfig.json | 4 +--- tsconfig.test.json | 8 ++++++++ 7 files changed, 65 insertions(+), 14 deletions(-) create mode 100644 jest.config.js create mode 100644 jest.setup.js create mode 100644 tsconfig.test.json diff --git a/.babelrc b/.babelrc index 01ddf8f..b1cd0f2 100644 --- a/.babelrc +++ b/.babelrc @@ -1,4 +1,9 @@ { - "plugins": ["transform-react-jsx"], - "presets": ["stage-2"] + "presets": ["module:metro-react-native-babel-preset"], + "plugins": [ + ["@babel/plugin-transform-class-properties", { "loose": true }], + ["@babel/plugin-transform-private-methods", { "loose": true }], + ["@babel/plugin-transform-private-property-in-object", { "loose": true }] + ] + } diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..49c8664 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,12 @@ +module.exports = { + preset: 'react-native', + setupFilesAfterEnv: ['/jest.setup.js'], + transformIgnorePatterns: [ + // Transform everything except react-native-related packages + 'node_modules/(?!(react-native|@react-native|@react-native-community|@testing-library)/)', + ], + transform: { + '^.+\\.(js|jsx|ts|tsx)$':['babel-jest', { configFile: './.babelrc' }], + }, + moduleFileExtensions: ['js', 'jsx', 'ts', 'tsx'], +}; diff --git a/jest.setup.js b/jest.setup.js new file mode 100644 index 0000000..d5c5345 --- /dev/null +++ b/jest.setup.js @@ -0,0 +1,23 @@ +import '@testing-library/jest-native/extend-expect'; + +// Mock Native Animated Helper +jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper'); + +// Mock Animated.timing +jest.mock('react-native/Libraries/Animated/Animated', () => { + const ActualAnimated = jest.requireActual('react-native/Libraries/Animated/Animated'); + return { + ...ActualAnimated, + timing: jest.fn().mockReturnValue({ + start: jest.fn(), + }), + }; +}); + +// Enable Jest Fake Timers +jest.useFakeTimers(); + +// Clear timers after each test +afterEach(() => { + jest.clearAllTimers(); +}); diff --git a/package.json b/package.json index d99cedc..a4784c2 100644 --- a/package.json +++ b/package.json @@ -29,16 +29,19 @@ "react-native": "*" }, "devDependencies": { + "@babel/core": "^7.26.0", + "@babel/plugin-transform-private-methods": "^7.25.9", "@react-native/eslint-config": "^0.73.1", + "@testing-library/jest-native": "^5.4.3", + "@testing-library/react-native": "^12.9.0", + "@types/jest": "^29.5.14", "@types/react": "^18.0.0", "@types/react-native": "^0.70.0", - "babel-cli": "^6.24.1", - "babel-plugin-transform-react-jsx": "^6.24.1", - "babel-preset-stage-2": "^6.24.1", "eslint": "^8.51.0", "eslint-config-prettier": "^9.0.0", "eslint-plugin-prettier": "^5.0.1", "jest": "^29.7.0", + "metro-react-native-babel-preset": "^0.77.0", "prettier": "^3.0.3", "react": "^18.2.0", "react-native": "0.75.4", @@ -50,12 +53,6 @@ "typecheck": "tsc", "lint": "eslint \"**/*.{js,ts,tsx}\"" }, - "jest": { - "preset": "react-native", - "setupFilesAfterEnv": [ - "/jest-setup.js" - ] - }, "eslintConfig": { "root": true, "extends": [ diff --git a/src/__tests__/AnimatedEllipsis.test.tsx b/src/__tests__/AnimatedEllipsis.test.tsx index e69de29..7a8e7e2 100644 --- a/src/__tests__/AnimatedEllipsis.test.tsx +++ b/src/__tests__/AnimatedEllipsis.test.tsx @@ -0,0 +1,8 @@ +import { render } from '@testing-library/react-native'; +import AnimatedEllipsis from '../AnimatedEllipsis'; + +test('renders the correct number of dots with default props', () => { + const { getAllByText } = render(); + const dots = getAllByText('.'); + expect(dots.length).toBe(3); // Default `numberOfDots` is 3 +}); diff --git a/tsconfig.json b/tsconfig.json index b9a2771..99ce823 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,7 +11,7 @@ "jsx": "react-jsx", "lib": ["ESNext"], "module": "ESNext", - "moduleResolution": "Bundler", + "moduleResolution": "node", "noEmit": true, "noFallthroughCasesInSwitch": true, "noImplicitReturns": true, @@ -21,10 +21,8 @@ "noUnusedLocals": true, "noUnusedParameters": true, "resolveJsonModule": true, - "resolvePackageJsonImports": false, "skipLibCheck": true, "strict": true, "target": "ESNext", - "verbatimModuleSyntax": true } } diff --git a/tsconfig.test.json b/tsconfig.test.json new file mode 100644 index 0000000..4a49656 --- /dev/null +++ b/tsconfig.test.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "moduleResolution": "node", // Overrides "Bundler" for Jest compatibility + "types": ["jest", "@testing-library/jest-native"] + }, + "include": ["__tests__/**/*", "src/**/*"] +} From eb9ff6612405bf980b8c4450480a8a40301ccff2 Mon Sep 17 00:00:00 2001 From: Reagan Middlebrook Date: Fri, 6 Dec 2024 21:12:14 -0800 Subject: [PATCH 08/15] Write unit tests --- jest.setup.js | 16 +--- src/AnimatedEllipsis.tsx | 15 +++- src/__tests__/AnimatedEllipsis.test.tsx | 98 ++++++++++++++++++++++++- 3 files changed, 109 insertions(+), 20 deletions(-) diff --git a/jest.setup.js b/jest.setup.js index d5c5345..827c65d 100644 --- a/jest.setup.js +++ b/jest.setup.js @@ -1,23 +1,9 @@ import '@testing-library/jest-native/extend-expect'; -// Mock Native Animated Helper jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper'); -// Mock Animated.timing -jest.mock('react-native/Libraries/Animated/Animated', () => { - const ActualAnimated = jest.requireActual('react-native/Libraries/Animated/Animated'); - return { - ...ActualAnimated, - timing: jest.fn().mockReturnValue({ - start: jest.fn(), - }), - }; -}); - -// Enable Jest Fake Timers jest.useFakeTimers(); - -// Clear timers after each test afterEach(() => { + jest.clearAllMocks(); jest.clearAllTimers(); }); diff --git a/src/AnimatedEllipsis.tsx b/src/AnimatedEllipsis.tsx index 9877579..e27ed2c 100644 --- a/src/AnimatedEllipsis.tsx +++ b/src/AnimatedEllipsis.tsx @@ -49,9 +49,12 @@ const AnimatedEllipsis: React.FC = ({ style = defaultStyle, useNativeDriver = true, }: Props): React.JSX.Element => { + // Ensure non-negative dots + const validatedNumberOfDots = Math.max(0, numberOfDots); + const isMountedRef = React.useRef(true); const targetOpacityRef = React.useRef(initialTargetOpacity); - const dotOpacitiesRef = React.useRef(initializeDots(numberOfDots, minOpacity)); + const dotOpacitiesRef = React.useRef(initializeDots(validatedNumberOfDots, minOpacity)); const animateDots = React.useCallback( (currentDot: number): void => { @@ -79,15 +82,19 @@ const AnimatedEllipsis: React.FC = ({ useEffect(() => { isMountedRef.current = true; - animateDots(0); + + if (validatedNumberOfDots > 0) { + animateDots(0); + } return () => { isMountedRef.current = false; + dotOpacitiesRef.current.forEach(opacity => opacity.stopAnimation()); }; - }, [animateDots]); + }, [animateDots, numberOfDots]); useEffect(() => { - dotOpacitiesRef.current = initializeDots(numberOfDots, minOpacity); + dotOpacitiesRef.current = initializeDots(validatedNumberOfDots, minOpacity); }, [numberOfDots, minOpacity]); return ( diff --git a/src/__tests__/AnimatedEllipsis.test.tsx b/src/__tests__/AnimatedEllipsis.test.tsx index 7a8e7e2..63b43e5 100644 --- a/src/__tests__/AnimatedEllipsis.test.tsx +++ b/src/__tests__/AnimatedEllipsis.test.tsx @@ -1,4 +1,5 @@ -import { render } from '@testing-library/react-native'; +import { act, render } from '@testing-library/react-native'; +import { Animated } from 'react-native'; import AnimatedEllipsis from '../AnimatedEllipsis'; test('renders the correct number of dots with default props', () => { @@ -6,3 +7,98 @@ test('renders the correct number of dots with default props', () => { const dots = getAllByText('.'); expect(dots.length).toBe(3); // Default `numberOfDots` is 3 }); + +test('renders the correct number of dots with custom numberOfDots', () => { + const { getAllByText } = render(); + const dots = getAllByText('.'); + expect(dots.length).toBe(5); // Custom `numberOfDots` is 5 +}); + +test('applies custom styles to the dots', () => { + const customStyle = { color: 'red', fontSize: 20 }; + const { getAllByText } = render(); + const dots = getAllByText('.'); + + dots.forEach(dot => { + expect(dot.props.style).toEqual( + expect.objectContaining(customStyle) + ); + }); +}); + +test('triggers animations for each dot', () => { + jest.useFakeTimers(); + + const { getAllByText } = render(); + + act(() => { + jest.advanceTimersByTime(300 * 3); // Assuming animationDelay is 300ms + }); + + const dots = getAllByText('.'); + expect(dots.length).toBe(3); + + jest.useRealTimers(); +}); + +test('uses custom animationDelay', () => { + jest.useFakeTimers(); + + const timingSpy = jest.spyOn(Animated, 'timing'); + + render(); + + act(() => { + // Advance timers to simulate animation + jest.advanceTimersByTime(500); + }); + + expect(timingSpy).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ duration: 500 }) + ); + + jest.useRealTimers(); +}); + +test('renders no dots when numberOfDots is 0', () => { + const { queryAllByText } = render(); + const dots = queryAllByText('.'); + expect(dots.length).toBe(0); // No dots should be rendered +}); + +test('renders no dots when numberOfDots is negative', () => { + const { queryAllByText } = render(); + const dots = queryAllByText('.'); + expect(dots.length).toBe(0); // No dots should be rendered +}); + +test('applies minOpacity to dots', () => { + const { getAllByText } = render(); + const dots = getAllByText('.'); + + dots.forEach(dot => { + expect(dot.props.style).toEqual( + expect.objectContaining({ opacity: 0.5 }) + ); + }); +}); + +test('uses useNativeDriver when provided', () => { + jest.useFakeTimers(); + const timingSpy = jest.spyOn(Animated, 'timing'); + + render(); + + act(() => { + jest.advanceTimersByTime(300); + }); + + expect(timingSpy).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ useNativeDriver: true }) + ); + + jest.useRealTimers(); + timingSpy.mockRestore(); // Cleanup the spy +}); From 216211188649e5a321a51f1eab8e44c4515771d0 Mon Sep 17 00:00:00 2001 From: Reagan Middlebrook Date: Sun, 8 Dec 2024 14:34:48 -0800 Subject: [PATCH 09/15] Update configs and publish --- .babelrc | 9 --------- .gitignore | 3 ++- babel.config.js | 6 ++++++ jest.config.js | 3 +-- package.json | 23 +++++++++++++++-------- src/index.ts | 1 + tsconfig.json | 24 ++++++++++++++---------- 7 files changed, 39 insertions(+), 30 deletions(-) delete mode 100644 .babelrc create mode 100644 babel.config.js create mode 100644 src/index.ts diff --git a/.babelrc b/.babelrc deleted file mode 100644 index b1cd0f2..0000000 --- a/.babelrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "presets": ["module:metro-react-native-babel-preset"], - "plugins": [ - ["@babel/plugin-transform-class-properties", { "loose": true }], - ["@babel/plugin-transform-private-methods", { "loose": true }], - ["@babel/plugin-transform-private-property-in-object", { "loose": true }] - ] - -} diff --git a/.gitignore b/.gitignore index 5b3930a..f830dd8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ node_modules/ .idea/ package-lock.json -.DS_Store \ No newline at end of file +.DS_Store +dist/ diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 0000000..0c81577 --- /dev/null +++ b/babel.config.js @@ -0,0 +1,6 @@ +module.exports = { + presets: ["module:metro-react-native-babel-preset"], + plugins: [ + ["@babel/plugin-transform-private-methods", { loose: true }], + ] +}; diff --git a/jest.config.js b/jest.config.js index 49c8664..2801320 100644 --- a/jest.config.js +++ b/jest.config.js @@ -2,11 +2,10 @@ module.exports = { preset: 'react-native', setupFilesAfterEnv: ['/jest.setup.js'], transformIgnorePatterns: [ - // Transform everything except react-native-related packages 'node_modules/(?!(react-native|@react-native|@react-native-community|@testing-library)/)', ], transform: { - '^.+\\.(js|jsx|ts|tsx)$':['babel-jest', { configFile: './.babelrc' }], + '^.+\\.(js|jsx|ts|tsx)$': 'babel-jest', }, moduleFileExtensions: ['js', 'jsx', 'ts', 'tsx'], }; diff --git a/package.json b/package.json index a4784c2..bfea964 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,9 @@ { "name": "@reagankm/rn-animated-ellipsis", - "version": "2.2.8", + "version": "2.2.9", "description": "An updated version of rn-animated-ellipsis, a simple, customizable animated dots component ideal for loading screens in React Native apps.", - "main": "src/AnimatedEllipsis.tsx", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", "author": { "name": "Reagan Middlebrook", "email": "reagankm@gmail.com" @@ -21,9 +22,6 @@ "license": "MIT", "licenseFilename": "LICENSE", "readmeFilename": "README.md", - "dependencies": { - "rn-animated-ellipsis": "^2.1.3" - }, "peerDependencies": { "react": "*", "react-native": "*" @@ -46,12 +44,15 @@ "react": "^18.2.0", "react-native": "0.75.4", "react-test-renderer": "^18.3.1", + "tsup": "^8.3.5", "typescript": "^4.9.5" }, "scripts": { + "build": "tsup src/index.ts --format cjs,esm --dts", "test": "jest", - "typecheck": "tsc", - "lint": "eslint \"**/*.{js,ts,tsx}\"" + "typecheck": "tsc --noEmit", + "lint": "eslint \"**/*.{js,ts,tsx}\"", + "postbuild": "rm -f dist/*.mts" }, "eslintConfig": { "root": true, @@ -89,5 +90,11 @@ "rn-animated-ellipsis", "react-native-animated-ellipsis", "loading animation" - ] + ], + "files": [ + "dist", + "README.md", + "LICENSE" + ], + "readme": "README.md" } diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..756c848 --- /dev/null +++ b/src/index.ts @@ -0,0 +1 @@ +export { default as AnimatedEllipsis } from './AnimatedEllipsis'; diff --git a/tsconfig.json b/tsconfig.json index 99ce823..68561de 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,18 +1,23 @@ { + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "**/__tests__/**", "**/*.test.ts", "**/*.test.tsx"], "compilerOptions": { - "rootDir": ".", - "paths": { - "rn-animated-ellipsis": ["./src/index"] - }, + "outDir": "dist", + "rootDir": "src", + "target": "ESNext", + "jsx": "react-jsx", + "lib": ["ES2017", "DOM"], + "module": "ESNext", + "moduleResolution": "node", + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "sourceMap": true, + "allowUnreachableCode": false, "allowUnusedLabels": false, "esModuleInterop": true, "forceConsistentCasingInFileNames": true, - "jsx": "react-jsx", - "lib": ["ESNext"], - "module": "ESNext", - "moduleResolution": "node", - "noEmit": true, "noFallthroughCasesInSwitch": true, "noImplicitReturns": true, "noImplicitUseStrict": false, @@ -23,6 +28,5 @@ "resolveJsonModule": true, "skipLibCheck": true, "strict": true, - "target": "ESNext", } } From 10f674e9fc805ddab805dcbe8029c144fcfab3d0 Mon Sep 17 00:00:00 2001 From: Reagan Middlebrook Date: Sun, 8 Dec 2024 14:45:50 -0800 Subject: [PATCH 10/15] Update README.md Move forkage info to bottom so usage info is at the top --- README.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 49542cd..d42e558 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,3 @@ -# rn-animated-ellipsis-extended - -This is a fork of [rn-animated-ellipsis](https://github.com/Thanhal-P-A/rn-animated-ellipsis) by -[Thanhal-P-A](https://github.com/Thanhal-P-A), which is a fork of -[react-native-animated-ellipsis](https://github.com/adorableio/react-native-animated-ellipsis) -by [adorableio](https://github.com/adorableio). - -This fork includes TypeScript types and additional updates. - # React Native Animated Ellipsis A simple, customizable animated dots component ideal for loading screens in @@ -104,3 +95,11 @@ Customize the number of dots, animation speed, and style using these props: }} /> ``` + +This is a fork of [rn-animated-ellipsis](https://github.com/Thanhal-P-A/rn-animated-ellipsis) by +[Thanhal-P-A](https://github.com/Thanhal-P-A), which is a fork of +[react-native-animated-ellipsis](https://github.com/adorableio/react-native-animated-ellipsis) +by [adorableio](https://github.com/adorableio). + +This fork includes TypeScript types and additional updates. + From ae2c4d93143af454bf8ee2d4c59d25f22ae3dafd Mon Sep 17 00:00:00 2001 From: Reagan Middlebrook Date: Sun, 8 Dec 2024 14:47:16 -0800 Subject: [PATCH 11/15] Update imports --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d42e558..dbd4c80 100644 --- a/README.md +++ b/README.md @@ -12,15 +12,15 @@ React Native apps (forked from Thanhal-P-A/rn-animated-ellipsis - not maintained ## Installation using npm ```shell -npm install rn-animated-ellipsis +npm install @reagankm/rn-animated-ellipsis ``` or using yarn ```shell -yarn add rn-animated-ellipsis +yarn add @reagankm/rn-animated-ellipsis ``` ## Importing ```js -import AnimatedEllipsis from 'rn-animated-ellipsis'; +import AnimatedEllipsis from '@reagankm/rn-animated-ellipsis'; ``` ## Usage From eb53310bd3825727302da8b5163c886b3ce3fc33 Mon Sep 17 00:00:00 2001 From: Reagan Middlebrook Date: Mon, 9 Dec 2024 16:02:25 -0800 Subject: [PATCH 12/15] Fix export so it can be imported by default, like the original version --- jest.config.js | 17 ++++++++++++++--- package.json | 15 +++++++-------- src/index.ts | 2 +- tsup.config.js | 9 +++++++++ 4 files changed, 31 insertions(+), 12 deletions(-) create mode 100644 tsup.config.js diff --git a/jest.config.js b/jest.config.js index 2801320..fec72b2 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,3 +1,15 @@ +// module.exports = { +// preset: 'react-native', +// setupFilesAfterEnv: ['/jest.setup.js'], +// transformIgnorePatterns: [ +// 'node_modules/(?!(react-native|@react-native|@react-native-community|@testing-library)/)', +// ], +// transform: { +// '^.+\\.(js|jsx|ts|tsx)$': 'babel-jest', +// }, +// moduleFileExtensions: ['js', 'jsx', 'ts', 'tsx'], +// }; + module.exports = { preset: 'react-native', setupFilesAfterEnv: ['/jest.setup.js'], @@ -5,7 +17,6 @@ module.exports = { 'node_modules/(?!(react-native|@react-native|@react-native-community|@testing-library)/)', ], transform: { - '^.+\\.(js|jsx|ts|tsx)$': 'babel-jest', - }, - moduleFileExtensions: ['js', 'jsx', 'ts', 'tsx'], + '^.+\\.(js|jsx|ts|tsx)$': ['babel-jest', { configFile: './babel.config.js' }] + } }; diff --git a/package.json b/package.json index bfea964..eb7ee5c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@reagankm/rn-animated-ellipsis", - "version": "2.2.9", + "version": "2.2.10", "description": "An updated version of rn-animated-ellipsis, a simple, customizable animated dots component ideal for loading screens in React Native apps.", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -22,13 +22,10 @@ "license": "MIT", "licenseFilename": "LICENSE", "readmeFilename": "README.md", - "peerDependencies": { - "react": "*", - "react-native": "*" - }, "devDependencies": { "@babel/core": "^7.26.0", "@babel/plugin-transform-private-methods": "^7.25.9", + "@babel/runtime": "^7.26.0", "@react-native/eslint-config": "^0.73.1", "@testing-library/jest-native": "^5.4.3", "@testing-library/react-native": "^12.9.0", @@ -41,14 +38,16 @@ "jest": "^29.7.0", "metro-react-native-babel-preset": "^0.77.0", "prettier": "^3.0.3", - "react": "^18.2.0", - "react-native": "0.75.4", "react-test-renderer": "^18.3.1", "tsup": "^8.3.5", "typescript": "^4.9.5" }, + "peerDependencies": { + "react": "^18.3.1", + "react-native": "^0.75.2" + }, "scripts": { - "build": "tsup src/index.ts --format cjs,esm --dts", + "build": "tsup src/index.ts", "test": "jest", "typecheck": "tsc --noEmit", "lint": "eslint \"**/*.{js,ts,tsx}\"", diff --git a/src/index.ts b/src/index.ts index 756c848..ddfdfa5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1 +1 @@ -export { default as AnimatedEllipsis } from './AnimatedEllipsis'; +export { default } from './AnimatedEllipsis'; diff --git a/tsup.config.js b/tsup.config.js new file mode 100644 index 0000000..442b4c4 --- /dev/null +++ b/tsup.config.js @@ -0,0 +1,9 @@ +import { defineConfig } from 'tsup'; + +export default defineConfig({ + entry: ['src/index.ts'], + format: ['cjs', 'esm'], + dts: true, + external: ['react', 'react-native'], + clean: true, +}); From c4bb1f8212dfb43fcaaf37b95182cd541bdb2c48 Mon Sep 17 00:00:00 2001 From: Reagan Middlebrook Date: Tue, 10 Dec 2024 14:50:12 -0800 Subject: [PATCH 13/15] Revert unnecessary peerDependency and build changes --- package.json | 6 +++--- tsup.config.js | 9 --------- 2 files changed, 3 insertions(+), 12 deletions(-) delete mode 100644 tsup.config.js diff --git a/package.json b/package.json index eb7ee5c..3b45436 100644 --- a/package.json +++ b/package.json @@ -43,11 +43,11 @@ "typescript": "^4.9.5" }, "peerDependencies": { - "react": "^18.3.1", - "react-native": "^0.75.2" + "react": "*", + "react-native": "*" }, "scripts": { - "build": "tsup src/index.ts", + "build": "tsup src/index.ts --format cjs,esm --dts", "test": "jest", "typecheck": "tsc --noEmit", "lint": "eslint \"**/*.{js,ts,tsx}\"", diff --git a/tsup.config.js b/tsup.config.js deleted file mode 100644 index 442b4c4..0000000 --- a/tsup.config.js +++ /dev/null @@ -1,9 +0,0 @@ -import { defineConfig } from 'tsup'; - -export default defineConfig({ - entry: ['src/index.ts'], - format: ['cjs', 'esm'], - dts: true, - external: ['react', 'react-native'], - clean: true, -}); From 8ba23dc83d92a959c3c3f8638bf5b3b5c4b32efd Mon Sep 17 00:00:00 2001 From: Reagan Middlebrook Date: Tue, 10 Dec 2024 14:50:53 -0800 Subject: [PATCH 14/15] Update version number --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3b45436..ac98b4e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@reagankm/rn-animated-ellipsis", - "version": "2.2.10", + "version": "2.2.11", "description": "An updated version of rn-animated-ellipsis, a simple, customizable animated dots component ideal for loading screens in React Native apps.", "main": "./dist/index.js", "types": "./dist/index.d.ts", From b933df590b532fb4b033a4f50df1e72dcfa157c0 Mon Sep 17 00:00:00 2001 From: Reagan Middlebrook Date: Wed, 11 Dec 2024 12:41:25 -0800 Subject: [PATCH 15/15] Fix bug so it works in Expo too --- package.json | 2 +- src/AnimatedEllipsis.tsx | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index ac98b4e..8dc3db5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@reagankm/rn-animated-ellipsis", - "version": "2.2.11", + "version": "3.0.0", "description": "An updated version of rn-animated-ellipsis, a simple, customizable animated dots component ideal for loading screens in React Native apps.", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/src/AnimatedEllipsis.tsx b/src/AnimatedEllipsis.tsx index e27ed2c..da814d7 100644 --- a/src/AnimatedEllipsis.tsx +++ b/src/AnimatedEllipsis.tsx @@ -71,7 +71,7 @@ const AnimatedEllipsis: React.FC = ({ toValue: targetOpacityRef.current, duration: animationDelay, useNativeDriver: useNativeDriver, - }).start(() => animateDots(nextDot)); + } as Animated.TimingAnimationConfig).start(() => animateDots(nextDot)); }, [ animationDelay, @@ -93,10 +93,6 @@ const AnimatedEllipsis: React.FC = ({ }; }, [animateDots, numberOfDots]); - useEffect(() => { - dotOpacitiesRef.current = initializeDots(validatedNumberOfDots, minOpacity); - }, [numberOfDots, minOpacity]); - return ( {dotOpacitiesRef.current.map((opacity, index) => ( @@ -109,7 +105,7 @@ const AnimatedEllipsis: React.FC = ({ ))} - ) as React.JSX.Element; + ) as React.ReactElement; }; export default AnimatedEllipsis;