@rific/timer 0.1.4 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/index.d.mts +2 -3
- package/dist/index.d.ts +2 -3
- package/package.json +9 -5
- package/src/Timer.tsx +101 -0
- package/src/index.ts +2 -0
package/README.md
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { ReactNode } from 'react';
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
3
2
|
import { ViewStyle } from 'react-native';
|
|
4
3
|
|
|
5
4
|
type TimerProps = {
|
|
@@ -14,6 +13,6 @@ type TimerProps = {
|
|
|
14
13
|
style?: ViewStyle;
|
|
15
14
|
width?: number;
|
|
16
15
|
};
|
|
17
|
-
declare const Timer: ({ children, color, duration, onStart, onStop, radius, startProgress, started, style, width }: TimerProps) =>
|
|
16
|
+
declare const Timer: ({ children, color, duration, onStart, onStop, radius, startProgress, started, style, width }: TimerProps) => React.JSX.Element;
|
|
18
17
|
|
|
19
18
|
export { Timer, type TimerProps };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { ReactNode } from 'react';
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
3
2
|
import { ViewStyle } from 'react-native';
|
|
4
3
|
|
|
5
4
|
type TimerProps = {
|
|
@@ -14,6 +13,6 @@ type TimerProps = {
|
|
|
14
13
|
style?: ViewStyle;
|
|
15
14
|
width?: number;
|
|
16
15
|
};
|
|
17
|
-
declare const Timer: ({ children, color, duration, onStart, onStop, radius, startProgress, started, style, width }: TimerProps) =>
|
|
16
|
+
declare const Timer: ({ children, color, duration, onStart, onStop, radius, startProgress, started, style, width }: TimerProps) => React.JSX.Element;
|
|
18
17
|
|
|
19
18
|
export { Timer, type TimerProps };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rific/timer",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Animated SVG progress ring timer for React Native",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react-native",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"type": "commonjs",
|
|
27
27
|
"exports": {
|
|
28
28
|
".": {
|
|
29
|
+
"react-native": "./dist/index.js",
|
|
29
30
|
"types": "./dist/index.d.ts",
|
|
30
31
|
"import": "./dist/index.mjs",
|
|
31
32
|
"require": "./dist/index.js"
|
|
@@ -35,7 +36,10 @@
|
|
|
35
36
|
"module": "dist/index.mjs",
|
|
36
37
|
"types": "dist/index.d.ts",
|
|
37
38
|
"files": [
|
|
38
|
-
"dist"
|
|
39
|
+
"dist",
|
|
40
|
+
"src",
|
|
41
|
+
"!src/__tests__",
|
|
42
|
+
"!src/__mocks__"
|
|
39
43
|
],
|
|
40
44
|
"scripts": {
|
|
41
45
|
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
@@ -49,7 +53,7 @@
|
|
|
49
53
|
"test": "jest",
|
|
50
54
|
"test:watch": "jest --watchAll",
|
|
51
55
|
"typecheck": "tsc --noEmit",
|
|
52
|
-
"preversion": "npm run lint && npm test"
|
|
56
|
+
"preversion": "npm run lint && npm test && npm run build"
|
|
53
57
|
},
|
|
54
58
|
"devDependencies": {
|
|
55
59
|
"@testing-library/dom": "^10.4.1",
|
|
@@ -77,8 +81,8 @@
|
|
|
77
81
|
"typescript-eslint": "^8.59.3"
|
|
78
82
|
},
|
|
79
83
|
"peerDependencies": {
|
|
80
|
-
"react": ">=
|
|
81
|
-
"react-native": ">=0.
|
|
84
|
+
"react": ">=18.0.0",
|
|
85
|
+
"react-native": ">=0.76.0",
|
|
82
86
|
"react-native-svg": ">=13.0.0"
|
|
83
87
|
},
|
|
84
88
|
"publishConfig": {
|
package/src/Timer.tsx
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import React, { type ReactNode, useCallback, useEffect, useRef } from 'react'
|
|
2
|
+
import { Animated, Easing, StyleSheet, View, ViewStyle } from 'react-native'
|
|
3
|
+
import Svg, { Circle, G } from 'react-native-svg'
|
|
4
|
+
|
|
5
|
+
const AnimatedCircle = Animated.createAnimatedComponent(Circle)
|
|
6
|
+
|
|
7
|
+
const useNativeDriver = true
|
|
8
|
+
|
|
9
|
+
export type TimerProps = {
|
|
10
|
+
children?: ReactNode
|
|
11
|
+
color: string
|
|
12
|
+
duration: number
|
|
13
|
+
onStart?: () => void
|
|
14
|
+
onStop?: () => void
|
|
15
|
+
radius: number
|
|
16
|
+
startProgress?: number
|
|
17
|
+
started: string | null
|
|
18
|
+
style?: ViewStyle
|
|
19
|
+
width?: number
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const Timer = ({ children, color, duration, onStart, onStop, radius, startProgress = 0, started, style, width = 6 }: TimerProps) => {
|
|
23
|
+
const durationRef = useRef(duration)
|
|
24
|
+
const progressRef = useRef(startProgress)
|
|
25
|
+
const startedRef = useRef<string | null>(null)
|
|
26
|
+
const circumference = 2 * Math.PI * radius
|
|
27
|
+
const animation = useRef(new Animated.Value(0)).current
|
|
28
|
+
const animatedProps = {
|
|
29
|
+
strokeDashoffset: animation.interpolate({
|
|
30
|
+
inputRange: [0, 1],
|
|
31
|
+
outputRange: [circumference, 0]
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
const startTimer = useCallback(
|
|
35
|
+
(callback?: () => void) => {
|
|
36
|
+
const clampedProgress = Math.max(0, Math.min(1, startProgress))
|
|
37
|
+
const remainingDurationMs = Math.max(0, duration * 1000 * (1 - clampedProgress))
|
|
38
|
+
animation.setValue(clampedProgress)
|
|
39
|
+
if (onStart) onStart()
|
|
40
|
+
Animated.timing(animation, {
|
|
41
|
+
toValue: 1,
|
|
42
|
+
duration: remainingDurationMs,
|
|
43
|
+
easing: Easing.linear,
|
|
44
|
+
useNativeDriver
|
|
45
|
+
}).start(({ finished }) => {
|
|
46
|
+
if (finished && onStop) onStop()
|
|
47
|
+
if (callback) callback()
|
|
48
|
+
})
|
|
49
|
+
},
|
|
50
|
+
[animation, duration, onStart, onStop, startProgress]
|
|
51
|
+
)
|
|
52
|
+
const stopTimer = useCallback(
|
|
53
|
+
(callback?: () => void) => {
|
|
54
|
+
animation.stopAnimation()
|
|
55
|
+
Animated.spring(animation, {
|
|
56
|
+
toValue: 0,
|
|
57
|
+
useNativeDriver
|
|
58
|
+
}).start(callback)
|
|
59
|
+
},
|
|
60
|
+
[animation]
|
|
61
|
+
)
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
const startedChanged = started !== startedRef.current
|
|
64
|
+
const durationChanged = duration !== durationRef.current
|
|
65
|
+
const progressChanged = startProgress !== progressRef.current
|
|
66
|
+
|
|
67
|
+
if (duration === 0) {
|
|
68
|
+
stopTimer()
|
|
69
|
+
} else if (started) {
|
|
70
|
+
if (startedChanged || durationChanged || progressChanged) {
|
|
71
|
+
animation.stopAnimation(() => startTimer())
|
|
72
|
+
}
|
|
73
|
+
} else {
|
|
74
|
+
stopTimer()
|
|
75
|
+
}
|
|
76
|
+
durationRef.current = duration
|
|
77
|
+
progressRef.current = startProgress
|
|
78
|
+
startedRef.current = started
|
|
79
|
+
}, [animation, duration, startProgress, started, startTimer, stopTimer])
|
|
80
|
+
return (
|
|
81
|
+
<View style={[style, styles.root]}>
|
|
82
|
+
<Svg width={radius * 2 + width} height={radius * 2 + width} viewBox={`0 0 ${radius * 2 + width} ${radius * 2 + width}`} style={styles.svg}>
|
|
83
|
+
<G rotation='-90' origin={`${radius + width / 2}, ${radius + width / 2}`}>
|
|
84
|
+
<AnimatedCircle cx={radius + width / 2} cy={radius + width / 2} r={radius} stroke={color} strokeWidth={width} fill='none' strokeDasharray={circumference} {...animatedProps} />
|
|
85
|
+
</G>
|
|
86
|
+
</Svg>
|
|
87
|
+
<View style={styles.children}>{children}</View>
|
|
88
|
+
</View>
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const styles = StyleSheet.create({
|
|
93
|
+
children: {
|
|
94
|
+
position: 'absolute'
|
|
95
|
+
},
|
|
96
|
+
root: {
|
|
97
|
+
alignItems: 'center',
|
|
98
|
+
justifyContent: 'center'
|
|
99
|
+
},
|
|
100
|
+
svg: {}
|
|
101
|
+
})
|
package/src/index.ts
ADDED