@rific/timer 0.1.3 → 0.2.0

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 CHANGED
@@ -51,6 +51,6 @@ Pass an ISO timestamp string to `started` to begin the countdown. Set `started`
51
51
 
52
52
  ## Peer dependencies
53
53
 
54
- - `react >= 17.0.0`
55
- - `react-native >= 0.70.0`
54
+ - `react >= 18.0.0`
55
+ - `react-native >= 0.76.0`
56
56
  - `react-native-svg >= 13.0.0`
package/dist/index.js CHANGED
@@ -35,9 +35,10 @@ __export(index_exports, {
35
35
  module.exports = __toCommonJS(index_exports);
36
36
 
37
37
  // src/Timer.tsx
38
- var import_react = __toESM(require("react"));
38
+ var import_react = require("react");
39
39
  var import_react_native = require("react-native");
40
40
  var import_react_native_svg = __toESM(require("react-native-svg"));
41
+ var import_jsx_runtime = require("react/jsx-runtime");
41
42
  var AnimatedCircle = import_react_native.Animated.createAnimatedComponent(import_react_native_svg.Circle);
42
43
  var useNativeDriver = true;
43
44
  var Timer = ({ children, color, duration, onStart, onStop, radius, startProgress = 0, started, style, width = 6 }) => {
@@ -97,7 +98,10 @@ var Timer = ({ children, color, duration, onStart, onStop, radius, startProgress
97
98
  progressRef.current = startProgress;
98
99
  startedRef.current = started;
99
100
  }, [animation, duration, startProgress, started, startTimer, stopTimer]);
100
- return /* @__PURE__ */ import_react.default.createElement(import_react_native.View, { style: [style, styles.root] }, /* @__PURE__ */ import_react.default.createElement(import_react_native_svg.default, { width: radius * 2 + width, height: radius * 2 + width, viewBox: `0 0 ${radius * 2 + width} ${radius * 2 + width}`, style: styles.svg }, /* @__PURE__ */ import_react.default.createElement(import_react_native_svg.G, { rotation: "-90", origin: `${radius + width / 2}, ${radius + width / 2}` }, /* @__PURE__ */ import_react.default.createElement(AnimatedCircle, { cx: radius + width / 2, cy: radius + width / 2, r: radius, stroke: color, strokeWidth: width, fill: "none", strokeDasharray: circumference, ...animatedProps }))), /* @__PURE__ */ import_react.default.createElement(import_react_native.View, { style: styles.children }, children));
101
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native.View, { style: [style, styles.root], children: [
102
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_svg.default, { width: radius * 2 + width, height: radius * 2 + width, viewBox: `0 0 ${radius * 2 + width} ${radius * 2 + width}`, style: styles.svg, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_svg.G, { rotation: "-90", origin: `${radius + width / 2}, ${radius + width / 2}`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AnimatedCircle, { cx: radius + width / 2, cy: radius + width / 2, r: radius, stroke: color, strokeWidth: width, fill: "none", strokeDasharray: circumference, ...animatedProps }) }) }),
103
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.View, { style: styles.children, children })
104
+ ] });
101
105
  };
102
106
  var styles = import_react_native.StyleSheet.create({
103
107
  children: {
package/dist/index.mjs CHANGED
@@ -1,7 +1,8 @@
1
1
  // src/Timer.tsx
2
- import React, { useCallback, useEffect, useRef } from "react";
2
+ import { useCallback, useEffect, useRef } from "react";
3
3
  import { Animated, Easing, StyleSheet, View } from "react-native";
4
4
  import Svg, { Circle, G } from "react-native-svg";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
5
6
  var AnimatedCircle = Animated.createAnimatedComponent(Circle);
6
7
  var useNativeDriver = true;
7
8
  var Timer = ({ children, color, duration, onStart, onStop, radius, startProgress = 0, started, style, width = 6 }) => {
@@ -61,7 +62,10 @@ var Timer = ({ children, color, duration, onStart, onStop, radius, startProgress
61
62
  progressRef.current = startProgress;
62
63
  startedRef.current = started;
63
64
  }, [animation, duration, startProgress, started, startTimer, stopTimer]);
64
- return /* @__PURE__ */ React.createElement(View, { style: [style, styles.root] }, /* @__PURE__ */ React.createElement(Svg, { width: radius * 2 + width, height: radius * 2 + width, viewBox: `0 0 ${radius * 2 + width} ${radius * 2 + width}`, style: styles.svg }, /* @__PURE__ */ React.createElement(G, { rotation: "-90", origin: `${radius + width / 2}, ${radius + width / 2}` }, /* @__PURE__ */ React.createElement(AnimatedCircle, { cx: radius + width / 2, cy: radius + width / 2, r: radius, stroke: color, strokeWidth: width, fill: "none", strokeDasharray: circumference, ...animatedProps }))), /* @__PURE__ */ React.createElement(View, { style: styles.children }, children));
65
+ return /* @__PURE__ */ jsxs(View, { style: [style, styles.root], children: [
66
+ /* @__PURE__ */ jsx(Svg, { width: radius * 2 + width, height: radius * 2 + width, viewBox: `0 0 ${radius * 2 + width} ${radius * 2 + width}`, style: styles.svg, children: /* @__PURE__ */ jsx(G, { rotation: "-90", origin: `${radius + width / 2}, ${radius + width / 2}`, children: /* @__PURE__ */ jsx(AnimatedCircle, { cx: radius + width / 2, cy: radius + width / 2, r: radius, stroke: color, strokeWidth: width, fill: "none", strokeDasharray: circumference, ...animatedProps }) }) }),
67
+ /* @__PURE__ */ jsx(View, { style: styles.children, children })
68
+ ] });
65
69
  };
66
70
  var styles = StyleSheet.create({
67
71
  children: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rific/timer",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
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": "./src/index.ts",
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": ">=17.0.0",
81
- "react-native": ">=0.70.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
@@ -0,0 +1,2 @@
1
+ export type { TimerProps } from './Timer'
2
+ export { Timer } from './Timer'