@honeypathkar/react-native-predictive-back-gesture 1.0.4 → 1.0.5

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.
@@ -9,4 +9,4 @@ const syncBackMode = React.useCallback(() => {
9
9
  : PredictiveBack_1.BACK_MODE_SYSTEM);
10
10
  }, []);
11
11
  // On the NavigationContainer:
12
- <NavigationContainer ref={navigationRef} onReady={syncBackMode} onStateChange={syncBackMode}></NavigationContainer>;
12
+ React.createElement(NavigationContainer, { ref: navigationRef, onReady: syncBackMode, onStateChange: syncBackMode });
@@ -38,7 +38,6 @@ const react_native_1 = require("react-native");
38
38
  const react_native_gesture_handler_1 = require("react-native-gesture-handler");
39
39
  const react_native_reanimated_1 = __importStar(require("react-native-reanimated"));
40
40
  const native_1 = require("@react-navigation/native");
41
- const react_native_paper_1 = require("react-native-paper");
42
41
  const PredectiveBack_1 = require("./PredectiveBack");
43
42
  const { width, height } = react_native_1.Dimensions.get("window");
44
43
  // Material predictive-back peek: the card shrinks and drifts a little way to the right
@@ -63,9 +62,14 @@ const DRAG_RANGE = width * 0.6;
63
62
  const PEEK_THRESHOLD = 0.35;
64
63
  const VELOCITY_THRESHOLD = 900;
65
64
  const MIN_VELOCITY_DISTANCE = 50;
66
- const SwipeableScreen = ({ children, enabled = true, style, onHaptic }) => {
67
- const navigation = (0, native_1.useNavigation)();
68
- const theme = (0, react_native_paper_1.useTheme)();
65
+ const SwipeableScreen = ({ children, enabled = true, style, onHaptic, backgroundColor = '#000000', onGoBack }) => {
66
+ let navigation = null;
67
+ try {
68
+ navigation = (0, native_1.useNavigation)();
69
+ }
70
+ catch (e) {
71
+ // Component rendered outside React Navigation container
72
+ }
69
73
  // Gesture progress (0…1) — drives the peek: shrink, drift right, rounded corners.
70
74
  const peek = (0, react_native_reanimated_1.useSharedValue)(0);
71
75
  // Dismissal progress (0…1) — slides the card clear to the right. Also runs in
@@ -97,18 +101,22 @@ const SwipeableScreen = ({ children, enabled = true, style, onHaptic }) => {
97
101
  exit.value = (0, react_native_reanimated_1.withTiming)(0, { duration: 200, easing: EASING });
98
102
  }, [peek, exit, nativeActive]);
99
103
  const goBack = (0, react_1.useCallback)(() => {
100
- if (!navigation.canGoBack()) {
104
+ if (onGoBack) {
105
+ onGoBack();
106
+ return;
107
+ }
108
+ if (!navigation || !navigation.canGoBack()) {
101
109
  settle();
102
110
  return;
103
111
  }
104
112
  navigation.goBack();
105
113
  // goBack dispatches through `beforeRemove`, so it may not actually pop.
106
114
  requestAnimationFrame(() => {
107
- if (navigation.isFocused()) {
115
+ if (navigation && navigation.isFocused && navigation.isFocused()) {
108
116
  settle();
109
117
  }
110
118
  });
111
- }, [navigation, settle]);
119
+ }, [navigation, settle, onGoBack]);
112
120
  // ─── Commit / cancel ─────────────────────────────────────────────────────
113
121
  const commit = (0, react_1.useCallback)((duration = EXIT_DURATION) => {
114
122
  if (isDismissing.current) {
@@ -131,8 +139,9 @@ const SwipeableScreen = ({ children, enabled = true, style, onHaptic }) => {
131
139
  }, [peek, nativeActive]);
132
140
  // ─── System back gesture (Android) ───────────────────────────────────────
133
141
  const token = (0, react_1.useRef)({}).current;
134
- const isActive = enabled && navigation.canGoBack();
135
- (0, native_1.useFocusEffect)((0, react_1.useCallback)(() => {
142
+ const canGoBackInNav = navigation && navigation.canGoBack ? navigation.canGoBack() : false;
143
+ const isActive = enabled && (canGoBackInNav || !!onGoBack);
144
+ const focusEffectCallback = (0, react_1.useCallback)(() => {
136
145
  if (!PredectiveBack_1.PREDICTIVE_BACK_SUPPORTED || !isActive) {
137
146
  return undefined;
138
147
  }
@@ -160,7 +169,15 @@ const SwipeableScreen = ({ children, enabled = true, style, onHaptic }) => {
160
169
  onCommit: () => commit(PredectiveBack_1.PREDICTIVE_BACK_HAS_PROGRESS ? EXIT_DURATION : ENTER_DURATION),
161
170
  });
162
171
  return () => (0, PredectiveBack_1.releasePredictiveBack)(token);
163
- }, [token, isActive, peek, pivot, nativeActive, cancel, commit]));
172
+ }, [token, isActive, peek, pivot, nativeActive, cancel, commit]);
173
+ if (navigation) {
174
+ // eslint-disable-next-line react-hooks/rules-of-hooks
175
+ (0, native_1.useFocusEffect)(focusEffectCallback);
176
+ }
177
+ else {
178
+ // eslint-disable-next-line react-hooks/rules-of-hooks
179
+ (0, react_1.useEffect)(focusEffectCallback, [focusEffectCallback]);
180
+ }
164
181
  // ─── Drag-to-close from the left edge ────────────────────────────────────
165
182
  // On Android this covers the strip just inside the system gesture zone; on iOS it
166
183
  // is the only way back. Both drive the same peek, so the card looks identical
@@ -222,20 +239,15 @@ const SwipeableScreen = ({ children, enabled = true, style, onHaptic }) => {
222
239
  opacity: (0, react_native_reanimated_1.interpolate)(revealed, [0, 1], [MAX_DIM, 0], react_native_reanimated_1.Extrapolation.CLAMP),
223
240
  };
224
241
  });
225
- return (<react_native_1.View style={styles.outerWrapper}>
226
- <react_native_reanimated_1.default.View pointerEvents="none" style={[styles.backdrop, backdropStyle]}/>
227
-
228
- <react_native_gesture_handler_1.GestureDetector gesture={panGesture}>
229
- <react_native_reanimated_1.default.View style={[
230
- styles.screen,
231
- { backgroundColor: theme.colors.background },
232
- screenStyle,
233
- style,
234
- ]}>
235
- {children}
236
- </react_native_reanimated_1.default.View>
237
- </react_native_gesture_handler_1.GestureDetector>
238
- </react_native_1.View>);
242
+ return (react_1.default.createElement(react_native_1.View, { style: styles.outerWrapper },
243
+ react_1.default.createElement(react_native_reanimated_1.default.View, { pointerEvents: "none", style: [styles.backdrop, backdropStyle] }),
244
+ react_1.default.createElement(react_native_gesture_handler_1.GestureDetector, { gesture: panGesture },
245
+ react_1.default.createElement(react_native_reanimated_1.default.View, { style: [
246
+ styles.screen,
247
+ { backgroundColor },
248
+ screenStyle,
249
+ style,
250
+ ] }, children))));
239
251
  };
240
252
  exports.default = SwipeableScreen;
241
253
  const styles = react_native_1.StyleSheet.create({
@@ -0,0 +1 @@
1
+ {"root":["../src/appnavigator.jsx","../src/predectiveback.js","../src/swipablescreen.jsx","../src/index.js"],"version":"5.9.3"}
@@ -1,8 +1,10 @@
1
1
  export default SwipeableScreen;
2
- declare function SwipeableScreen({ children, enabled, style, onHaptic }: {
2
+ declare function SwipeableScreen({ children, enabled, style, onHaptic, backgroundColor, onGoBack }: {
3
3
  children: any;
4
4
  enabled?: boolean;
5
5
  style: any;
6
6
  onHaptic: any;
7
+ backgroundColor?: string;
8
+ onGoBack: any;
7
9
  }): React.JSX.Element;
8
10
  import React from "react";
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@honeypathkar/react-native-predictive-back-gesture",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "Android 14+ Predictive Back gesture animations and swipeable screen component for React Native.",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
8
- "main": "src/index.js",
9
- "module": "src/index.js",
8
+ "main": "lib/commonjs/index.js",
9
+ "module": "lib/module/index.js",
10
10
  "types": "lib/typescript/index.d.ts",
11
11
  "files": [
12
12
  "src",
@@ -34,13 +34,12 @@
34
34
  "react": "*",
35
35
  "react-native": "*",
36
36
  "react-native-gesture-handler": "*",
37
- "react-native-reanimated": "*",
38
- "react-native-paper": "*"
37
+ "react-native-reanimated": "*"
39
38
  },
40
39
  "devDependencies": {
41
40
  "@types/react": "^18.3.31",
42
41
  "react": "^18.2.0",
43
- "react-native": "^0.72.0",
42
+ "react-native": "^0.72.17",
44
43
  "react-native-gesture-handler": "^2.12.0",
45
44
  "react-native-reanimated": "^3.3.0",
46
45
  "typescript": "^5.9.3"
@@ -13,7 +13,6 @@ import Animated, {
13
13
  cancelAnimation,
14
14
  } from "react-native-reanimated";
15
15
  import { useNavigation, useFocusEffect } from "@react-navigation/native";
16
- import { useTheme } from "react-native-paper";
17
16
  import {
18
17
  PREDICTIVE_BACK_HAS_PROGRESS,
19
18
  PREDICTIVE_BACK_SUPPORTED,
@@ -48,9 +47,13 @@ const PEEK_THRESHOLD = 0.35;
48
47
  const VELOCITY_THRESHOLD = 900;
49
48
  const MIN_VELOCITY_DISTANCE = 50;
50
49
 
51
- const SwipeableScreen = ({ children, enabled = true, style, onHaptic }) => {
52
- const navigation = useNavigation();
53
- const theme = useTheme();
50
+ const SwipeableScreen = ({ children, enabled = true, style, onHaptic, backgroundColor = '#000000', onGoBack }) => {
51
+ let navigation = null;
52
+ try {
53
+ navigation = useNavigation();
54
+ } catch (e) {
55
+ // Component rendered outside React Navigation container
56
+ }
54
57
 
55
58
  // Gesture progress (0…1) — drives the peek: shrink, drift right, rounded corners.
56
59
  const peek = useSharedValue(0);
@@ -86,18 +89,22 @@ const SwipeableScreen = ({ children, enabled = true, style, onHaptic }) => {
86
89
  }, [peek, exit, nativeActive]);
87
90
 
88
91
  const goBack = useCallback(() => {
89
- if (!navigation.canGoBack()) {
92
+ if (onGoBack) {
93
+ onGoBack();
94
+ return;
95
+ }
96
+ if (!navigation || !navigation.canGoBack()) {
90
97
  settle();
91
98
  return;
92
99
  }
93
100
  navigation.goBack();
94
101
  // goBack dispatches through `beforeRemove`, so it may not actually pop.
95
102
  requestAnimationFrame(() => {
96
- if (navigation.isFocused()) {
103
+ if (navigation && navigation.isFocused && navigation.isFocused()) {
97
104
  settle();
98
105
  }
99
106
  });
100
- }, [navigation, settle]);
107
+ }, [navigation, settle, onGoBack]);
101
108
 
102
109
  // ─── Commit / cancel ─────────────────────────────────────────────────────
103
110
  const commit = useCallback(
@@ -126,43 +133,50 @@ const SwipeableScreen = ({ children, enabled = true, style, onHaptic }) => {
126
133
 
127
134
  // ─── System back gesture (Android) ───────────────────────────────────────
128
135
  const token = useRef({}).current;
129
- const isActive = enabled && navigation.canGoBack();
136
+ const canGoBackInNav = navigation && navigation.canGoBack ? navigation.canGoBack() : false;
137
+ const isActive = enabled && (canGoBackInNav || !!onGoBack);
130
138
 
131
- useFocusEffect(
132
- useCallback(() => {
133
- if (!PREDICTIVE_BACK_SUPPORTED || !isActive) {
134
- return undefined;
135
- }
139
+ const focusEffectCallback = useCallback(() => {
140
+ if (!PREDICTIVE_BACK_SUPPORTED || !isActive) {
141
+ return undefined;
142
+ }
136
143
 
137
- const track = (event) => {
138
- peek.value = event.progress;
139
- pivot.value = (event.touchY / height) * 2 - 1;
140
- };
144
+ const track = (event) => {
145
+ peek.value = event.progress;
146
+ pivot.value = (event.touchY / height) * 2 - 1;
147
+ };
141
148
 
142
- acquirePredictiveBack(token, {
143
- onStart: (event) => {
144
- if (isDismissing.current) {
145
- return;
146
- }
147
- nativeActive.value = 1;
148
- cancelAnimation(peek);
149
+ acquirePredictiveBack(token, {
150
+ onStart: (event) => {
151
+ if (isDismissing.current) {
152
+ return;
153
+ }
154
+ nativeActive.value = 1;
155
+ cancelAnimation(peek);
156
+ track(event);
157
+ },
158
+ onProgress: (event) => {
159
+ if (!isDismissing.current) {
149
160
  track(event);
150
- },
151
- onProgress: (event) => {
152
- if (!isDismissing.current) {
153
- track(event);
154
- }
155
- },
156
- onCancel: cancel,
157
- // Without live progress (pre-Android 14, or a 3-button back press) the card
158
- // has not moved yet, so give the slide-out a little more room to breathe.
159
- onCommit: () =>
160
- commit(PREDICTIVE_BACK_HAS_PROGRESS ? EXIT_DURATION : ENTER_DURATION),
161
- });
161
+ }
162
+ },
163
+ onCancel: cancel,
164
+ // Without live progress (pre-Android 14, or a 3-button back press) the card
165
+ // has not moved yet, so give the slide-out a little more room to breathe.
166
+ onCommit: () =>
167
+ commit(PREDICTIVE_BACK_HAS_PROGRESS ? EXIT_DURATION : ENTER_DURATION),
168
+ });
162
169
 
163
- return () => releasePredictiveBack(token);
164
- }, [token, isActive, peek, pivot, nativeActive, cancel, commit]),
165
- );
170
+ return () => releasePredictiveBack(token);
171
+ }, [token, isActive, peek, pivot, nativeActive, cancel, commit]);
172
+
173
+ if (navigation) {
174
+ // eslint-disable-next-line react-hooks/rules-of-hooks
175
+ useFocusEffect(focusEffectCallback);
176
+ } else {
177
+ // eslint-disable-next-line react-hooks/rules-of-hooks
178
+ useEffect(focusEffectCallback, [focusEffectCallback]);
179
+ }
166
180
 
167
181
  // ─── Drag-to-close from the left edge ────────────────────────────────────
168
182
  // On Android this covers the strip just inside the system gesture zone; on iOS it
@@ -246,7 +260,7 @@ const SwipeableScreen = ({ children, enabled = true, style, onHaptic }) => {
246
260
  <Animated.View
247
261
  style={[
248
262
  styles.screen,
249
- { backgroundColor: theme.colors.background },
263
+ { backgroundColor },
250
264
  screenStyle,
251
265
  style,
252
266
  ]}
@@ -1 +0,0 @@
1
- {"root":["../../src/appnavigator.jsx","../../src/predectiveback.js","../../src/swipablescreen.jsx","../../src/index.js"],"version":"5.9.3"}