react-native-swipeable-actions 0.1.5 → 0.1.7-canary.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/android/src/main/java/expo/modules/swipeable/SwipeableModule.kt +4 -0
- package/android/src/main/java/expo/modules/swipeable/SwipeableView.kt +98 -16
- package/build/Swipeable.d.ts +1 -1
- package/build/Swipeable.d.ts.map +1 -1
- package/build/Swipeable.js +26 -7
- package/build/Swipeable.js.map +1 -1
- package/build/Swipeable.types.d.ts +2 -2
- package/build/Swipeable.types.d.ts.map +1 -1
- package/build/SwipeableActions.d.ts +4 -2
- package/build/SwipeableActions.d.ts.map +1 -1
- package/build/SwipeableActions.js +2 -6
- package/build/SwipeableActions.js.map +1 -1
- package/build/SwipeableView.d.ts +2 -1
- package/build/SwipeableView.d.ts.map +1 -1
- package/build/SwipeableView.js +1 -1
- package/build/SwipeableView.js.map +1 -1
- package/build/index.d.ts +4 -4
- package/build/index.d.ts.map +1 -1
- package/build/index.js +6 -6
- package/build/index.js.map +1 -1
- package/build/nativeModuleUtils.d.ts +1 -0
- package/build/nativeModuleUtils.d.ts.map +1 -1
- package/build/nativeModuleUtils.js +9 -0
- package/build/nativeModuleUtils.js.map +1 -1
- package/ios/OpenStateCache.swift +57 -0
- package/ios/Package.swift +1 -1
- package/ios/SwipeableModule.swift +4 -0
- package/ios/SwipeableView.swift +62 -24
- package/jestSetup.js +2 -1
- package/package.json +8 -13
- package/src/Swipeable.tsx +37 -16
- package/src/Swipeable.types.ts +2 -2
- package/src/SwipeableActions.tsx +5 -2
- package/src/SwipeableView.tsx +4 -3
- package/src/index.ts +8 -11
- package/src/nativeModuleUtils.ts +8 -0
|
@@ -20,6 +20,10 @@ class SwipeableModule : Module() {
|
|
|
20
20
|
SwipeableView.closeAll(animated ?: true)
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
Function("isOpenByKey") { key: String ->
|
|
24
|
+
SwipeableView.isOpenByKey(key)
|
|
25
|
+
}
|
|
26
|
+
|
|
23
27
|
View(SwipeableView::class) {
|
|
24
28
|
Prop("actionsWidth") { view: SwipeableView, width: Float ->
|
|
25
29
|
view.actionsWidth = width
|
|
@@ -89,6 +89,9 @@ class SwipeableView(context: Context, appContext: AppContext) : ExpoView(context
|
|
|
89
89
|
fun clearCache() {
|
|
90
90
|
openStateCache.evictAll()
|
|
91
91
|
}
|
|
92
|
+
|
|
93
|
+
/** Check if a recyclingKey is cached as open (for JS-side state initialization) */
|
|
94
|
+
fun isOpenByKey(key: String): Boolean = getOpenState(key)
|
|
92
95
|
}
|
|
93
96
|
|
|
94
97
|
private val density = context.resources.displayMetrics.density
|
|
@@ -182,7 +185,11 @@ class SwipeableView(context: Context, appContext: AppContext) : ExpoView(context
|
|
|
182
185
|
|
|
183
186
|
close(animated = false)
|
|
184
187
|
|
|
185
|
-
// Apply using local shouldOpen (pendingShouldOpen was cleared by close)
|
|
188
|
+
// Apply using local shouldOpen (pendingShouldOpen was cleared by close).
|
|
189
|
+
// Set recycleSnapPending so onViewAdded can position actions immediately
|
|
190
|
+
// when React renders them (isOpenByKey initializes hasActionsRendered=true
|
|
191
|
+
// before snapToOpen runs on the next frame).
|
|
192
|
+
recycleSnapPending = shouldOpen
|
|
186
193
|
if (shouldOpen) {
|
|
187
194
|
post { snapToOpen() }
|
|
188
195
|
}
|
|
@@ -197,6 +204,9 @@ class SwipeableView(context: Context, appContext: AppContext) : ExpoView(context
|
|
|
197
204
|
private var isGestureActivated: Boolean = false
|
|
198
205
|
private var lastEmittedProgress: Float = -1f
|
|
199
206
|
private var pendingShouldOpen: Boolean = false
|
|
207
|
+
/** Tracks pending snapToOpen from recyclingKey setter. Unlike pendingShouldOpen,
|
|
208
|
+
* this survives close(animated:false) so onViewAdded can position actions correctly. */
|
|
209
|
+
private var recycleSnapPending: Boolean = false
|
|
200
210
|
private var lastEmittedWillEndState: String? = null
|
|
201
211
|
|
|
202
212
|
// Track if open animation was explicitly interrupted by close() for autoClose
|
|
@@ -238,6 +248,41 @@ class SwipeableView(context: Context, appContext: AppContext) : ExpoView(context
|
|
|
238
248
|
// Track when a child (e.g. RNGH gesture handler) claims the touch
|
|
239
249
|
private var childRequestedDisallowIntercept: Boolean = false
|
|
240
250
|
|
|
251
|
+
// Layout guard: re-applies translations for a few frames after a reorder.
|
|
252
|
+
// Fabric may reset child translationX during prop reconciliation after a
|
|
253
|
+
// recyclingKey change. This guard runs for a bounded number of frames to
|
|
254
|
+
// detect and correct mismatches, then stops automatically.
|
|
255
|
+
private var layoutGuardCallback: Choreographer.FrameCallback? = null
|
|
256
|
+
private var layoutGuardFramesRemaining: Int = 0
|
|
257
|
+
|
|
258
|
+
private fun startLayoutGuard(frames: Int = 5) {
|
|
259
|
+
stopLayoutGuard()
|
|
260
|
+
layoutGuardFramesRemaining = frames
|
|
261
|
+
layoutGuardCallback = Choreographer.FrameCallback {
|
|
262
|
+
layoutGuardFramesRemaining--
|
|
263
|
+
if (isOpen && currentTranslation != 0f && !isDragging && !isAnimating) {
|
|
264
|
+
val contentTx = contentView?.translationX ?: 0f
|
|
265
|
+
if (contentTx != currentTranslation) {
|
|
266
|
+
contentView?.translationX = currentTranslation
|
|
267
|
+
updateActionsTransform()
|
|
268
|
+
actionsView?.visibility = View.VISIBLE
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
if (layoutGuardFramesRemaining > 0) {
|
|
272
|
+
layoutGuardCallback?.let { Choreographer.getInstance().postFrameCallback(it) }
|
|
273
|
+
} else {
|
|
274
|
+
layoutGuardCallback = null
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
layoutGuardCallback?.let { Choreographer.getInstance().postFrameCallback(it) }
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
private fun stopLayoutGuard() {
|
|
281
|
+
layoutGuardCallback?.let { Choreographer.getInstance().removeFrameCallback(it) }
|
|
282
|
+
layoutGuardCallback = null
|
|
283
|
+
layoutGuardFramesRemaining = 0
|
|
284
|
+
}
|
|
285
|
+
|
|
241
286
|
init {
|
|
242
287
|
clipChildren = false
|
|
243
288
|
clipToPadding = false
|
|
@@ -283,6 +328,12 @@ class SwipeableView(context: Context, appContext: AppContext) : ExpoView(context
|
|
|
283
328
|
}
|
|
284
329
|
|
|
285
330
|
if (wasActionsNil && actionsView != null) {
|
|
331
|
+
// Prevent child clipping: Yoga lays out children for absoluteFill (full parent width)
|
|
332
|
+
// but our onLayout overrides the actionsView bounds to actionsWidth. Without this,
|
|
333
|
+
// children positioned by Yoga's flex-end would be clipped beyond the narrower bounds.
|
|
334
|
+
(actionsView as? ViewGroup)?.clipChildren = false
|
|
335
|
+
(actionsView as? ViewGroup)?.clipToPadding = false
|
|
336
|
+
|
|
286
337
|
if (isOpen || isAnimating) {
|
|
287
338
|
actionsView?.visibility = View.VISIBLE
|
|
288
339
|
actionsView?.translationX = 0f
|
|
@@ -295,6 +346,13 @@ class SwipeableView(context: Context, appContext: AppContext) : ExpoView(context
|
|
|
295
346
|
pendingShouldOpen = false
|
|
296
347
|
post { snapToOpen() }
|
|
297
348
|
}
|
|
349
|
+
else if (recycleSnapPending) {
|
|
350
|
+
// Actions were rendered by React (via isOpenByKey) before native snapToOpen ran.
|
|
351
|
+
// Position them immediately so they're visible when UIAutomator checks.
|
|
352
|
+
recycleSnapPending = false
|
|
353
|
+
actionsView?.visibility = View.VISIBLE
|
|
354
|
+
actionsView?.translationX = 0f
|
|
355
|
+
}
|
|
298
356
|
}
|
|
299
357
|
}
|
|
300
358
|
|
|
@@ -313,22 +371,30 @@ class SwipeableView(context: Context, appContext: AppContext) : ExpoView(context
|
|
|
313
371
|
|
|
314
372
|
for (i in 0 until childCount) {
|
|
315
373
|
val child = getChildAt(i)
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
}
|
|
323
|
-
} else {
|
|
324
|
-
child.layout(0, 0, width, height)
|
|
325
|
-
}
|
|
374
|
+
// Don't override actionsView layout - Yoga positions it via absoluteFill
|
|
375
|
+
// and lays out children (flex-end) based on full parent width. Overriding
|
|
376
|
+
// the bounds to actionsWidth causes children to be positioned outside the
|
|
377
|
+
// narrower bounds, making them invisible during FlatList reorder.
|
|
378
|
+
// Instead, use translationX to hide/show the actions.
|
|
379
|
+
child.layout(0, 0, width, height)
|
|
326
380
|
}
|
|
327
381
|
|
|
328
|
-
if (!isDragging && !isAnimating && currentTranslation == 0f && actionsWidthPx > 0f) {
|
|
382
|
+
if (!isDragging && !isAnimating && currentTranslation == 0f && actionsWidthPx > 0f && !recycleSnapPending) {
|
|
329
383
|
val actionsOffset = if (isLeading) -actionsWidthPx else actionsWidthPx
|
|
330
384
|
actionsView?.translationX = actionsOffset
|
|
331
385
|
}
|
|
386
|
+
|
|
387
|
+
// Re-apply translations when open. Fabric may reset child translationX during
|
|
388
|
+
// prop reconciliation after a list reorder. The post {} ensures we run after
|
|
389
|
+
// Fabric finishes applying all mutations in the current frame.
|
|
390
|
+
if (isOpen && currentTranslation != 0f && !isDragging && !isAnimating) {
|
|
391
|
+
post {
|
|
392
|
+
if (isOpen && currentTranslation != 0f && !isDragging && !isAnimating) {
|
|
393
|
+
contentView?.translationX = currentTranslation
|
|
394
|
+
updateActionsTransform()
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
332
398
|
}
|
|
333
399
|
|
|
334
400
|
/**
|
|
@@ -728,6 +794,7 @@ class SwipeableView(context: Context, appContext: AppContext) : ExpoView(context
|
|
|
728
794
|
return@addEndListener
|
|
729
795
|
}
|
|
730
796
|
isAnimating = false
|
|
797
|
+
startLayoutGuard()
|
|
731
798
|
emitFinalProgress(1f, targetX)
|
|
732
799
|
// With autoClose, let close() emit onSwipeEnd("open") instead
|
|
733
800
|
if (!autoClose) {
|
|
@@ -763,6 +830,8 @@ class SwipeableView(context: Context, appContext: AppContext) : ExpoView(context
|
|
|
763
830
|
onSwipeStart(emptyMap())
|
|
764
831
|
}
|
|
765
832
|
|
|
833
|
+
startLayoutGuard()
|
|
834
|
+
|
|
766
835
|
// Emit final progress
|
|
767
836
|
emitFinalProgress(1f, targetX)
|
|
768
837
|
}
|
|
@@ -770,6 +839,7 @@ class SwipeableView(context: Context, appContext: AppContext) : ExpoView(context
|
|
|
770
839
|
fun close(animated: Boolean = true, velocity: Float = 0f) {
|
|
771
840
|
cancelAutoClose()
|
|
772
841
|
stopProgressUpdates()
|
|
842
|
+
stopLayoutGuard()
|
|
773
843
|
|
|
774
844
|
// Cancel any running animations
|
|
775
845
|
openAnimationInterruptedByClose = true
|
|
@@ -957,16 +1027,28 @@ class SwipeableView(context: Context, appContext: AppContext) : ExpoView(context
|
|
|
957
1027
|
|
|
958
1028
|
override fun onAttachedToWindow() {
|
|
959
1029
|
super.onAttachedToWindow()
|
|
960
|
-
// Re-register in the view registry after reattach (e.g. returning from navigation).
|
|
961
|
-
// onDetachedFromWindow unregisters the view, but the recyclingKey prop setter
|
|
962
|
-
// short-circuits when the value hasn't changed, so the view is never re-registered
|
|
963
|
-
// unless we do it here.
|
|
964
1030
|
recyclingKey?.let { registerView(this, it) }
|
|
1031
|
+
|
|
1032
|
+
if (isOpen && currentTranslation != 0f) {
|
|
1033
|
+
post {
|
|
1034
|
+
if (isOpen && currentTranslation != 0f && !isDragging && !isAnimating) {
|
|
1035
|
+
contentView?.translationX = currentTranslation
|
|
1036
|
+
updateActionsTransform()
|
|
1037
|
+
actionsView?.visibility = View.VISIBLE
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
// Restart autoClose timer if it was cancelled during detach
|
|
1042
|
+
if (autoClose) {
|
|
1043
|
+
scheduleAutoClose()
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
965
1046
|
}
|
|
966
1047
|
|
|
967
1048
|
override fun onDetachedFromWindow() {
|
|
968
1049
|
super.onDetachedFromWindow()
|
|
969
1050
|
stopProgressUpdates()
|
|
1051
|
+
stopLayoutGuard()
|
|
970
1052
|
cancelGestureTimeout()
|
|
971
1053
|
cancelAutoClose()
|
|
972
1054
|
contentSpringAnimation?.cancel()
|
package/build/Swipeable.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { SwipeableMethods, SwipeableProps } from './Swipeable.types';
|
|
2
2
|
declare const Swipeable: import("react").ForwardRefExoticComponent<Omit<SwipeableProps, "ref"> & import("react").RefAttributes<SwipeableMethods>>;
|
|
3
3
|
type SwipeableWithStatic = typeof Swipeable & {
|
|
4
4
|
open: (recyclingKey: string) => void;
|
package/build/Swipeable.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Swipeable.d.ts","sourceRoot":"","sources":["../src/Swipeable.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Swipeable.d.ts","sourceRoot":"","sources":["../src/Swipeable.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAEV,gBAAgB,EAChB,cAAc,EAGf,MAAM,mBAAmB,CAAA;AAe1B,QAAA,MAAM,SAAS,0HAoJb,CAAA;AAgBF,KAAK,mBAAmB,GAAG,OAAO,SAAS,GAAG;IAC5C,IAAI,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC,KAAK,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;IACzD,QAAQ,EAAE,CAAC,QAAQ,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;CACvC,CAAA;AAED,QAAA,MAAM,eAAe,EAAgB,mBAAmB,CAAA;AAsBxD,eAAe,eAAe,CAAA"}
|
package/build/Swipeable.js
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const react_1 = require("react");
|
|
4
4
|
const react_native_1 = require("react-native");
|
|
5
|
-
const SwipeableView_1 = require("./SwipeableView");
|
|
6
|
-
const SwipeableActions_1 = require("./SwipeableActions");
|
|
7
5
|
const constants_1 = require("./constants");
|
|
8
6
|
const nativeModuleUtils_1 = require("./nativeModuleUtils");
|
|
7
|
+
const SwipeableActions_1 = require("./SwipeableActions");
|
|
8
|
+
const SwipeableView_1 = require("./SwipeableView");
|
|
9
9
|
function normalizeRecyclingKey(key) {
|
|
10
10
|
if (key == null)
|
|
11
11
|
return undefined;
|
|
@@ -20,13 +20,30 @@ function normalizeRecyclingKey(key) {
|
|
|
20
20
|
}
|
|
21
21
|
const Swipeable = (0, react_1.forwardRef)(function Swipeable({ children, actions, actionsWidth, actionsPosition = constants_1.SWIPE_DEFAULTS.ACTIONS_POSITION, friction = constants_1.SWIPE_DEFAULTS.FRICTION, threshold = constants_1.SWIPE_DEFAULTS.THRESHOLD, gestureEnabled = constants_1.SWIPE_DEFAULTS.GESTURE_ENABLED, dragOffsetFromEdge = constants_1.SWIPE_DEFAULTS.DRAG_OFFSET_FROM_EDGE, style, testID, onSwipeStart, onSwipeStateChange, onSwipeEnd, onProgress, autoClose = false, autoCloseTimeout = 0, recyclingKey }, ref) {
|
|
22
22
|
const nativeRef = (0, react_1.useRef)(null);
|
|
23
|
-
|
|
24
|
-
const swipeStartedRef = (0, react_1.useRef)(false);
|
|
25
|
-
// Normalize and sync recyclingKey to ref for use in useImperativeHandle
|
|
26
|
-
// (set during render to ensure it's always current before callbacks fire)
|
|
23
|
+
// Normalize recyclingKey before hooks that depend on it
|
|
27
24
|
const normalizedKey = normalizeRecyclingKey(recyclingKey);
|
|
28
25
|
const recyclingKeyRef = (0, react_1.useRef)(undefined);
|
|
29
26
|
recyclingKeyRef.current = normalizedKey;
|
|
27
|
+
// Initialize from native cache: if this recyclingKey was open, render actions immediately.
|
|
28
|
+
// This handles FlatList remounting components during data reorder - React state is lost
|
|
29
|
+
// but native cache preserves the open state.
|
|
30
|
+
const [hasActionsRendered, setHasActionsRendered] = (0, react_1.useState)(() => {
|
|
31
|
+
return normalizedKey != null && (0, nativeModuleUtils_1.isOpenByKey)(normalizedKey);
|
|
32
|
+
});
|
|
33
|
+
const swipeStartedRef = (0, react_1.useRef)(false);
|
|
34
|
+
const initialKeyRef = (0, react_1.useRef)(normalizedKey);
|
|
35
|
+
// When recyclingKey changes (FlatList reorder/recycle), sync hasActionsRendered
|
|
36
|
+
// with the native cached open state. FlatList reuses component instances without
|
|
37
|
+
// remounting, so useState initializer doesn't re-run. Skip on initial mount
|
|
38
|
+
// since the useState initializer already handled it.
|
|
39
|
+
(0, react_1.useEffect)(() => {
|
|
40
|
+
if (!normalizedKey)
|
|
41
|
+
return;
|
|
42
|
+
if (normalizedKey === initialKeyRef.current)
|
|
43
|
+
return;
|
|
44
|
+
initialKeyRef.current = normalizedKey;
|
|
45
|
+
setHasActionsRendered((0, nativeModuleUtils_1.isOpenByKey)(normalizedKey));
|
|
46
|
+
}, [normalizedKey]);
|
|
30
47
|
// Development-time validation
|
|
31
48
|
if (__DEV__) {
|
|
32
49
|
if (autoClose && recyclingKey == null) {
|
|
@@ -76,7 +93,9 @@ const Swipeable = (0, react_1.forwardRef)(function Swipeable({ children, actions
|
|
|
76
93
|
onSwipeEnd?.(event.nativeEvent.state);
|
|
77
94
|
}, [onSwipeEnd]);
|
|
78
95
|
return (<SwipeableView_1.SwipeableView ref={nativeRef} style={[styles.container, style]} actionsWidth={actionsWidth} actionsPosition={actionsPosition} friction={friction} threshold={threshold} gestureEnabled={gestureEnabled} dragOffsetFromEdge={dragOffsetFromEdge} autoClose={autoClose} autoCloseTimeout={autoCloseTimeout} {...(normalizedKey ? { recyclingKey: normalizedKey } : {})} {...(testID ? { testID } : {})} onSwipeStart={handleSwipeStart} onSwipeProgress={handleSwipeProgress} onSwipeStateChange={handleSwipeStateChange} onSwipeEnd={handleSwipeEnd}>
|
|
79
|
-
{hasActionsRendered && (<SwipeableActions_1.SwipeableActions actionsPosition={actionsPosition}
|
|
96
|
+
{hasActionsRendered && (<SwipeableActions_1.SwipeableActions actionsPosition={actionsPosition} {...(testID ? { testID: `${testID}-actions` } : {})}>
|
|
97
|
+
{actions}
|
|
98
|
+
</SwipeableActions_1.SwipeableActions>)}
|
|
80
99
|
|
|
81
100
|
<react_native_1.View style={styles.content}>{children}</react_native_1.View>
|
|
82
101
|
</SwipeableView_1.SwipeableView>);
|
package/build/Swipeable.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Swipeable.js","sourceRoot":"","sources":["../src/Swipeable.tsx"],"names":[],"mappings":";;AAAA,
|
|
1
|
+
{"version":3,"file":"Swipeable.js","sourceRoot":"","sources":["../src/Swipeable.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ReactNode, Ref } from 'react';
|
|
2
|
-
import {
|
|
1
|
+
import type { ReactNode, Ref } from 'react';
|
|
2
|
+
import type { StyleProp, ViewProps, ViewStyle } from 'react-native';
|
|
3
3
|
export interface SwipeProgressEvent {
|
|
4
4
|
nativeEvent: {
|
|
5
5
|
progress: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Swipeable.types.d.ts","sourceRoot":"","sources":["../src/Swipeable.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"Swipeable.types.d.ts","sourceRoot":"","sources":["../src/Swipeable.types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
2
|
/**
|
|
3
3
|
* Props for SwipeableActions component
|
|
4
4
|
*/
|
|
@@ -7,11 +7,13 @@ export interface SwipeableActionsProps {
|
|
|
7
7
|
children: ReactNode;
|
|
8
8
|
/** Position of actions: 'left' or 'right' */
|
|
9
9
|
actionsPosition: 'left' | 'right';
|
|
10
|
+
/** Test ID for e2e testing */
|
|
11
|
+
testID?: string;
|
|
10
12
|
}
|
|
11
13
|
/**
|
|
12
14
|
* SwipeableActions - Internal layout component for swipeable actions
|
|
13
15
|
*
|
|
14
16
|
* Handles entrance animation when children mount.
|
|
15
17
|
*/
|
|
16
|
-
export declare function SwipeableActions({ children, actionsPosition }: SwipeableActionsProps):
|
|
18
|
+
export declare function SwipeableActions({ children, actionsPosition, testID }: SwipeableActionsProps): import("react").JSX.Element;
|
|
17
19
|
//# sourceMappingURL=SwipeableActions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SwipeableActions.d.ts","sourceRoot":"","sources":["../src/SwipeableActions.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"SwipeableActions.d.ts","sourceRoot":"","sources":["../src/SwipeableActions.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,kCAAkC;IAClC,QAAQ,EAAE,SAAS,CAAA;IACnB,6CAA6C;IAC7C,eAAe,EAAE,MAAM,GAAG,OAAO,CAAA;IACjC,8BAA8B;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,EAAE,EAAE,qBAAqB,+BAU5F"}
|
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.SwipeableActions = SwipeableActions;
|
|
7
|
-
const react_1 = __importDefault(require("react"));
|
|
8
4
|
const react_native_1 = require("react-native");
|
|
9
5
|
/**
|
|
10
6
|
* SwipeableActions - Internal layout component for swipeable actions
|
|
11
7
|
*
|
|
12
8
|
* Handles entrance animation when children mount.
|
|
13
9
|
*/
|
|
14
|
-
function SwipeableActions({ children, actionsPosition }) {
|
|
15
|
-
return (<react_native_1.View style={actionsPosition === 'left' ? styles.actionsLeading : styles.actionsTrailing} pointerEvents={children ? 'auto' : 'none'}>
|
|
10
|
+
function SwipeableActions({ children, actionsPosition, testID }) {
|
|
11
|
+
return (<react_native_1.View style={actionsPosition === 'left' ? styles.actionsLeading : styles.actionsTrailing} pointerEvents={children ? 'auto' : 'none'} testID={testID}>
|
|
16
12
|
{children}
|
|
17
13
|
</react_native_1.View>);
|
|
18
14
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SwipeableActions.js","sourceRoot":"","sources":["../src/SwipeableActions.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SwipeableActions.js","sourceRoot":"","sources":["../src/SwipeableActions.tsx"],"names":[],"mappings":";;AAoBA,4CAUC;AA7BD,+CAA+C;AAc/C;;;;GAIG;AACH,SAAgB,gBAAgB,CAAC,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,EAAyB;IAC3F,OAAO,CACL,CAAC,mBAAI,CACH,KAAK,CAAC,CAAC,eAAe,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,eAAe,CAAC,CACnF,aAAa,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAC1C,MAAM,CAAC,CAAC,MAAM,CAAC,CAEf;MAAA,CAAC,QAAQ,CACX;IAAA,EAAE,mBAAI,CAAC,CACR,CAAA;AACH,CAAC;AAED,MAAM,MAAM,GAAG,yBAAU,CAAC,MAAM,CAAC;IAC/B,eAAe,EAAE;QACf,GAAG,yBAAU,CAAC,YAAY;QAC1B,aAAa,EAAE,KAAK;QACpB,cAAc,EAAE,UAAU;KAC3B;IACD,cAAc,EAAE;QACd,GAAG,yBAAU,CAAC,YAAY;QAC1B,aAAa,EAAE,KAAK;QACpB,cAAc,EAAE,YAAY;KAC7B;CACF,CAAC,CAAA"}
|
package/build/SwipeableView.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { NativeSwipeableRef, SwipeableViewProps } from './Swipeable.types';
|
|
2
2
|
interface SwipeableNativeModule {
|
|
3
3
|
openByKey: (key: string) => void;
|
|
4
4
|
closeByKey: (key: string, animated?: boolean) => void;
|
|
5
5
|
closeAll: (animated?: boolean) => void;
|
|
6
|
+
isOpenByKey: (key: string) => boolean;
|
|
6
7
|
}
|
|
7
8
|
export declare const SwipeableModule: SwipeableNativeModule;
|
|
8
9
|
export declare const SwipeableView: import("react").ForwardRefExoticComponent<SwipeableViewProps & import("react").RefAttributes<NativeSwipeableRef>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SwipeableView.d.ts","sourceRoot":"","sources":["../src/SwipeableView.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"SwipeableView.d.ts","sourceRoot":"","sources":["../src/SwipeableView.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAK/E,UAAU,qBAAqB;IAC7B,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;IACrD,QAAQ,EAAE,CAAC,QAAQ,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;IACtC,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAA;CACtC;AAED,eAAO,MAAM,eAAe,uBAA0D,CAAA;AAEtF,eAAO,MAAM,aAAa,mHAIzB,CAAA"}
|
package/build/SwipeableView.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SwipeableView = exports.SwipeableModule = void 0;
|
|
4
|
-
const react_1 = require("react");
|
|
5
4
|
const expo_modules_core_1 = require("expo-modules-core");
|
|
5
|
+
const react_1 = require("react");
|
|
6
6
|
const NativeView = (0, expo_modules_core_1.requireNativeViewManager)('Swipeable');
|
|
7
7
|
exports.SwipeableModule = (0, expo_modules_core_1.requireNativeModule)('Swipeable');
|
|
8
8
|
exports.SwipeableView = (0, react_1.forwardRef)(function SwipeableView(props, ref) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SwipeableView.js","sourceRoot":"","sources":["../src/SwipeableView.tsx"],"names":[],"mappings":";;;AAAA,
|
|
1
|
+
{"version":3,"file":"SwipeableView.js","sourceRoot":"","sources":["../src/SwipeableView.tsx"],"names":[],"mappings":";;;AAAA,yDAAiF;AACjF,iCAAsE;AAGtE,MAAM,UAAU,GAAG,IAAA,4CAAwB,EAAqB,WAAW,CAAC,CAAA;AAU/D,QAAA,eAAe,GAAG,IAAA,uCAAmB,EAAwB,WAAW,CAAC,CAAA;AAEzE,QAAA,aAAa,GAAG,IAAA,kBAAU,EACrC,SAAS,aAAa,CAAC,KAAK,EAAE,GAAG;IAC/B,OAAO,IAAA,qBAAa,EAAC,UAAU,EAAE,EAAE,GAAG,KAAK,EAAE,GAAG,EAAuC,CAAC,CAAA;AAC1F,CAAC,CACF,CAAA;AAED,qBAAa,CAAC,WAAW,GAAG,eAAe,CAAA"}
|
package/build/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
export { SWIPE_DEFAULTS, SWIPE_PROGRESS } from './constants';
|
|
2
|
+
export type { NativeModuleError, SwipeableErrorHandler } from './nativeModuleUtils';
|
|
3
|
+
export { setGlobalErrorHandler } from './nativeModuleUtils';
|
|
1
4
|
export { default as Swipeable } from './Swipeable';
|
|
5
|
+
export type { NativeSwipeableRef, SwipeableMethods, SwipeableProps, SwipeableStatic, SwipeableViewProps, SwipeProgressEvent, SwipeStateEvent } from './Swipeable.types';
|
|
2
6
|
export { SwipeableView } from './SwipeableView';
|
|
3
|
-
export type { SwipeableProps, SwipeableMethods, SwipeableStatic, SwipeableViewProps, SwipeProgressEvent, SwipeStateEvent, NativeSwipeableRef } from './Swipeable.types';
|
|
4
|
-
export { SWIPE_PROGRESS, SWIPE_DEFAULTS } from './constants';
|
|
5
|
-
export { setGlobalErrorHandler } from './nativeModuleUtils';
|
|
6
|
-
export type { SwipeableErrorHandler, NativeModuleError } from './nativeModuleUtils';
|
|
7
7
|
//# sourceMappingURL=index.d.ts.map
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAC5D,YAAY,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAEnF,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAC3D,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,aAAa,CAAA;AAClD,YAAY,EACV,kBAAkB,EAClB,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EAClB,eAAe,EAChB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA"}
|
package/build/index.js
CHANGED
|
@@ -3,15 +3,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
7
|
-
var Swipeable_1 = require("./Swipeable");
|
|
8
|
-
Object.defineProperty(exports, "Swipeable", { enumerable: true, get: function () { return __importDefault(Swipeable_1).default; } });
|
|
9
|
-
var SwipeableView_1 = require("./SwipeableView");
|
|
10
|
-
Object.defineProperty(exports, "SwipeableView", { enumerable: true, get: function () { return SwipeableView_1.SwipeableView; } });
|
|
6
|
+
exports.SwipeableView = exports.Swipeable = exports.setGlobalErrorHandler = exports.SWIPE_PROGRESS = exports.SWIPE_DEFAULTS = void 0;
|
|
11
7
|
var constants_1 = require("./constants");
|
|
12
|
-
Object.defineProperty(exports, "SWIPE_PROGRESS", { enumerable: true, get: function () { return constants_1.SWIPE_PROGRESS; } });
|
|
13
8
|
Object.defineProperty(exports, "SWIPE_DEFAULTS", { enumerable: true, get: function () { return constants_1.SWIPE_DEFAULTS; } });
|
|
9
|
+
Object.defineProperty(exports, "SWIPE_PROGRESS", { enumerable: true, get: function () { return constants_1.SWIPE_PROGRESS; } });
|
|
14
10
|
// Error handling utilities
|
|
15
11
|
var nativeModuleUtils_1 = require("./nativeModuleUtils");
|
|
16
12
|
Object.defineProperty(exports, "setGlobalErrorHandler", { enumerable: true, get: function () { return nativeModuleUtils_1.setGlobalErrorHandler; } });
|
|
13
|
+
var Swipeable_1 = require("./Swipeable");
|
|
14
|
+
Object.defineProperty(exports, "Swipeable", { enumerable: true, get: function () { return __importDefault(Swipeable_1).default; } });
|
|
15
|
+
var SwipeableView_1 = require("./SwipeableView");
|
|
16
|
+
Object.defineProperty(exports, "SwipeableView", { enumerable: true, get: function () { return SwipeableView_1.SwipeableView; } });
|
|
17
17
|
//# sourceMappingURL=index.js.map
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;AAAA,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;AAAA,yCAA4D;AAAnD,2GAAA,cAAc,OAAA;AAAE,2GAAA,cAAc,OAAA;AAEvC,2BAA2B;AAC3B,yDAA2D;AAAlD,0HAAA,qBAAqB,OAAA;AAC9B,yCAAkD;AAAzC,uHAAA,OAAO,OAAa;AAU7B,iDAA+C;AAAtC,8GAAA,aAAa,OAAA"}
|
|
@@ -8,4 +8,5 @@ export declare function setGlobalErrorHandler(handler: SwipeableErrorHandler | u
|
|
|
8
8
|
export declare function openByKey(key: string): void;
|
|
9
9
|
export declare function closeByKey(key: string, animated?: boolean): void;
|
|
10
10
|
export declare function closeAll(animated?: boolean): void;
|
|
11
|
+
export declare function isOpenByKey(key: string): boolean;
|
|
11
12
|
//# sourceMappingURL=nativeModuleUtils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nativeModuleUtils.d.ts","sourceRoot":"","sources":["../src/nativeModuleUtils.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED,MAAM,MAAM,qBAAqB,GAAG,CAClC,KAAK,EAAE,iBAAiB,EACxB,SAAS,EAAE,MAAM,EACjB,GAAG,CAAC,EAAE,MAAM,KACT,IAAI,CAAA;AAIT,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,qBAAqB,GAAG,SAAS,GAAG,IAAI,CAEtF;AAgBD,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAM3C;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,CAMhE;AAED,wBAAgB,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,CAMjD"}
|
|
1
|
+
{"version":3,"file":"nativeModuleUtils.d.ts","sourceRoot":"","sources":["../src/nativeModuleUtils.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED,MAAM,MAAM,qBAAqB,GAAG,CAClC,KAAK,EAAE,iBAAiB,EACxB,SAAS,EAAE,MAAM,EACjB,GAAG,CAAC,EAAE,MAAM,KACT,IAAI,CAAA;AAIT,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,qBAAqB,GAAG,SAAS,GAAG,IAAI,CAEtF;AAgBD,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAM3C;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,CAMhE;AAED,wBAAgB,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,CAMjD;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAMhD"}
|
|
@@ -4,6 +4,7 @@ exports.setGlobalErrorHandler = setGlobalErrorHandler;
|
|
|
4
4
|
exports.openByKey = openByKey;
|
|
5
5
|
exports.closeByKey = closeByKey;
|
|
6
6
|
exports.closeAll = closeAll;
|
|
7
|
+
exports.isOpenByKey = isOpenByKey;
|
|
7
8
|
const SwipeableView_1 = require("./SwipeableView");
|
|
8
9
|
let globalErrorHandler;
|
|
9
10
|
function setGlobalErrorHandler(handler) {
|
|
@@ -46,4 +47,12 @@ function closeAll(animated) {
|
|
|
46
47
|
handleNativeError(error, 'closeAll');
|
|
47
48
|
}
|
|
48
49
|
}
|
|
50
|
+
function isOpenByKey(key) {
|
|
51
|
+
try {
|
|
52
|
+
return SwipeableView_1.SwipeableModule.isOpenByKey(key);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
49
58
|
//# sourceMappingURL=nativeModuleUtils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nativeModuleUtils.js","sourceRoot":"","sources":["../src/nativeModuleUtils.ts"],"names":[],"mappings":";;AAgBA,sDAEC;AAgBD,8BAMC;AAED,gCAMC;AAED,4BAMC;
|
|
1
|
+
{"version":3,"file":"nativeModuleUtils.js","sourceRoot":"","sources":["../src/nativeModuleUtils.ts"],"names":[],"mappings":";;AAgBA,sDAEC;AAgBD,8BAMC;AAED,gCAMC;AAED,4BAMC;AAED,kCAMC;AAhED,mDAAiD;AAcjD,IAAI,kBAAqD,CAAA;AAEzD,SAAgB,qBAAqB,CAAC,OAA0C;IAC9E,kBAAkB,GAAG,OAAO,CAAA;AAC9B,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAc,EAAE,SAAiB,EAAE,GAAY;IACxE,MAAM,WAAW,GAAsB;QACrC,IAAI,EAAE,wBAAwB;QAC9B,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;QAC/D,GAAG,CAAC,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACpF,CAAA;IAED,IAAI,kBAAkB,EAAE,CAAC;QACvB,kBAAkB,CAAC,WAAW,EAAE,SAAS,EAAE,GAAG,CAAC,CAAA;IACjD,CAAC;SAAM,IAAI,OAAO,EAAE,CAAC;QACnB,OAAO,CAAC,KAAK,CAAC,eAAe,SAAS,UAAU,EAAE,WAAW,CAAC,OAAO,CAAC,CAAA;IACxE,CAAC;AACH,CAAC;AAED,SAAgB,SAAS,CAAC,GAAW;IACnC,IAAI,CAAC;QACH,+BAAe,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;IAChC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,iBAAiB,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,CAAC,CAAA;IAC5C,CAAC;AACH,CAAC;AAED,SAAgB,UAAU,CAAC,GAAW,EAAE,QAAkB;IACxD,IAAI,CAAC;QACH,+BAAe,CAAC,UAAU,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;IAC3C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,iBAAiB,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,CAAC,CAAA;IAC7C,CAAC;AACH,CAAC;AAED,SAAgB,QAAQ,CAAC,QAAkB;IACzC,IAAI,CAAC;QACH,+BAAe,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;IACpC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,iBAAiB,CAAC,KAAK,EAAE,UAAU,CAAC,CAAA;IACtC,CAAC;AACH,CAAC;AAED,SAAgB,WAAW,CAAC,GAAW;IACrC,IAAI,CAAC;QACH,OAAO,+BAAe,CAAC,WAAW,CAAC,GAAG,CAAC,CAAA;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
/// Thread-safe LRU cache for tracking open/closed state of swipeable views by recycling key.
|
|
4
|
+
/// Used by the static registry in SwipeableView to persist state across list recycling.
|
|
5
|
+
public class OpenStateCache {
|
|
6
|
+
private let queue: DispatchQueue
|
|
7
|
+
private let maxSize: Int
|
|
8
|
+
private var cache: [String: Bool] = [:]
|
|
9
|
+
private var insertionOrder: [String] = []
|
|
10
|
+
|
|
11
|
+
public init(maxSize: Int = 1000, queueLabel: String = "com.swipeable.openstate") {
|
|
12
|
+
self.maxSize = maxSize
|
|
13
|
+
self.queue = DispatchQueue(label: queueLabel, qos: .userInteractive)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/// Read the cached open state for a key. Returns false if not found.
|
|
17
|
+
public func get(_ key: String) -> Bool {
|
|
18
|
+
queue.sync { cache[key] ?? false }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/// Set the open state for a key. Promotes existing keys to most-recently-used.
|
|
22
|
+
/// Evicts the least-recently-used entry when cache exceeds max size.
|
|
23
|
+
public func set(_ key: String, isOpen: Bool) {
|
|
24
|
+
queue.async(flags: .barrier) { [self] in
|
|
25
|
+
if cache[key] != nil {
|
|
26
|
+
insertionOrder.removeAll { $0 == key }
|
|
27
|
+
}
|
|
28
|
+
insertionOrder.append(key)
|
|
29
|
+
cache[key] = isOpen
|
|
30
|
+
if insertionOrder.count > maxSize {
|
|
31
|
+
let oldest = insertionOrder.removeFirst()
|
|
32
|
+
cache.removeValue(forKey: oldest)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/// Remove a specific key from the cache.
|
|
38
|
+
public func clear(_ key: String) {
|
|
39
|
+
queue.async(flags: .barrier) { [self] in
|
|
40
|
+
cache.removeValue(forKey: key)
|
|
41
|
+
insertionOrder.removeAll { $0 == key }
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/// Remove all entries from the cache.
|
|
46
|
+
public func clearAll() {
|
|
47
|
+
queue.async(flags: .barrier) { [self] in
|
|
48
|
+
cache.removeAll()
|
|
49
|
+
insertionOrder.removeAll()
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/// Synchronously return the number of entries (for testing).
|
|
54
|
+
public var count: Int {
|
|
55
|
+
queue.sync { cache.count }
|
|
56
|
+
}
|
|
57
|
+
}
|
package/ios/Package.swift
CHANGED
|
@@ -17,7 +17,7 @@ let package = Package(
|
|
|
17
17
|
"SwipeableModule.swift",
|
|
18
18
|
"react-native-swipeable-actions.podspec"
|
|
19
19
|
],
|
|
20
|
-
sources: ["SwipePhysics.swift", "SwipeAnimationConfig.swift"]
|
|
20
|
+
sources: ["SwipePhysics.swift", "SwipeAnimationConfig.swift", "OpenStateCache.swift"]
|
|
21
21
|
),
|
|
22
22
|
.testTarget(
|
|
23
23
|
name: "SwipeableActionsTests",
|
|
@@ -17,6 +17,10 @@ public class SwipeableModule: Module {
|
|
|
17
17
|
SwipeableView.closeAll(animated: animated ?? true)
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
Function("isOpenByKey") { (key: String) -> Bool in
|
|
21
|
+
SwipeableView.getOpenState(for: key)
|
|
22
|
+
}
|
|
23
|
+
|
|
20
24
|
View(SwipeableView.self) {
|
|
21
25
|
// Props
|
|
22
26
|
Prop("actionsWidth") { (view: SwipeableView, width: CGFloat) in
|
package/ios/SwipeableView.swift
CHANGED
|
@@ -31,13 +31,23 @@ public class SwipeableView: ExpoView {
|
|
|
31
31
|
return typeName.contains("SurfaceTouchHandler") || typeName.contains("RCTTouchHandler")
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
///
|
|
35
|
-
///
|
|
36
|
-
|
|
34
|
+
/// Check if a gesture recognizer belongs to react-native-gesture-handler.
|
|
35
|
+
/// RNGH uses custom recognizer classes prefixed with "RNBetter" (e.g. RNBetterPanGestureRecognizer)
|
|
36
|
+
/// and "RNManualActivation" for manual activation recognizers.
|
|
37
|
+
private static func isRNGHGestureHandler(_ recognizer: UIGestureRecognizer) -> Bool {
|
|
38
|
+
let typeName = String(describing: type(of: recognizer))
|
|
39
|
+
return typeName.hasPrefix("RNBetter") || typeName.hasPrefix("RNManualActivation")
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/// Walk from a hit-tested view up to (but not including) self, checking for RNGH recognizers.
|
|
43
|
+
/// Used to prevent our pan gesture from beginning when the touch landed on a RNGH-managed view.
|
|
44
|
+
private func hasRNGHGesture(from view: UIView) -> Bool {
|
|
37
45
|
var current: UIView? = view
|
|
38
46
|
while let v = current, v !== self {
|
|
39
|
-
|
|
40
|
-
|
|
47
|
+
for recognizer in v.gestureRecognizers ?? [] {
|
|
48
|
+
if Self.isRNGHGestureHandler(recognizer) {
|
|
49
|
+
return true
|
|
50
|
+
}
|
|
41
51
|
}
|
|
42
52
|
current = v.superview
|
|
43
53
|
}
|
|
@@ -47,15 +57,15 @@ public class SwipeableView: ExpoView {
|
|
|
47
57
|
// MARK: - Thread-Safe Static Registry
|
|
48
58
|
|
|
49
59
|
private static let registryQueue = DispatchQueue(label: "com.swipeable.registry", qos: .userInteractive)
|
|
50
|
-
|
|
60
|
+
static let openStateCache = OpenStateCache()
|
|
51
61
|
private static var _viewRegistry: [String: WeakRef<SwipeableView>] = [:]
|
|
52
62
|
|
|
53
|
-
|
|
54
|
-
|
|
63
|
+
static func getOpenState(for key: String) -> Bool {
|
|
64
|
+
openStateCache.get(key)
|
|
55
65
|
}
|
|
56
66
|
|
|
57
67
|
private static func setOpenState(for key: String, isOpen: Bool) {
|
|
58
|
-
|
|
68
|
+
openStateCache.set(key, isOpen: isOpen)
|
|
59
69
|
}
|
|
60
70
|
|
|
61
71
|
private static func getView(for key: String) -> SwipeableView? {
|
|
@@ -71,7 +81,7 @@ public class SwipeableView: ExpoView {
|
|
|
71
81
|
}
|
|
72
82
|
|
|
73
83
|
private static func clearOpenState(for key: String) {
|
|
74
|
-
|
|
84
|
+
openStateCache.clear(key)
|
|
75
85
|
}
|
|
76
86
|
|
|
77
87
|
private static func getAllViews() -> [SwipeableView] {
|
|
@@ -215,7 +225,11 @@ public class SwipeableView: ExpoView {
|
|
|
215
225
|
|
|
216
226
|
// Apply using local shouldOpen (pendingShouldOpen was cleared by close)
|
|
217
227
|
if shouldOpen {
|
|
218
|
-
|
|
228
|
+
let keyAtDispatch = recyclingKey
|
|
229
|
+
DispatchQueue.main.async { [weak self] in
|
|
230
|
+
guard let self = self, self.recyclingKey == keyAtDispatch else { return }
|
|
231
|
+
self.snapToOpen()
|
|
232
|
+
}
|
|
219
233
|
}
|
|
220
234
|
}
|
|
221
235
|
}
|
|
@@ -291,17 +305,16 @@ public class SwipeableView: ExpoView {
|
|
|
291
305
|
currentAnimator = nil
|
|
292
306
|
isAnimating = false
|
|
293
307
|
|
|
294
|
-
//
|
|
295
|
-
contentView?.transform = .identity
|
|
296
|
-
contentView?.layer.zPosition = 0
|
|
297
|
-
actionsView?.transform = .identity
|
|
298
|
-
actionsView?.layer.zPosition = 0
|
|
299
|
-
|
|
300
|
-
// Unregister from static registry and clear cached state
|
|
308
|
+
// Unregister from static registry (re-registered in didMoveToWindow)
|
|
301
309
|
if let key = recyclingKey {
|
|
302
310
|
Self.unregisterView(for: key)
|
|
303
|
-
Self.clearOpenState(for: key)
|
|
304
311
|
}
|
|
312
|
+
|
|
313
|
+
// Nil view references so didAddSubview correctly detects new children
|
|
314
|
+
// on re-insertion (wasActionsNil/wasContentNil guards).
|
|
315
|
+
// Do NOT reset transforms - Fabric DELETE+INSERT must preserve open visual state.
|
|
316
|
+
contentView = nil
|
|
317
|
+
actionsView = nil
|
|
305
318
|
}
|
|
306
319
|
super.willMove(toSuperview: newSuperview)
|
|
307
320
|
}
|
|
@@ -329,6 +342,28 @@ public class SwipeableView: ExpoView {
|
|
|
329
342
|
panGesture.view?.removeGestureRecognizer(panGesture)
|
|
330
343
|
addGestureRecognizer(panGesture)
|
|
331
344
|
}
|
|
345
|
+
|
|
346
|
+
// Restore visual state after Fabric reorder (DELETE+INSERT cycle).
|
|
347
|
+
// Fabric may reset child transforms during prop reconciliation. Re-apply
|
|
348
|
+
// on next runloop tick to ensure we run after Fabric finishes mutations.
|
|
349
|
+
if isOpen && currentTranslation != 0 {
|
|
350
|
+
DispatchQueue.main.async { [weak self] in
|
|
351
|
+
guard let self = self,
|
|
352
|
+
self.isOpen,
|
|
353
|
+
self.currentTranslation != 0,
|
|
354
|
+
!self.isDragging,
|
|
355
|
+
!self.isAnimating else { return }
|
|
356
|
+
self.contentView?.transform = CGAffineTransform(translationX: self.currentTranslation, y: 0)
|
|
357
|
+
self.contentView?.layer.zPosition = 1
|
|
358
|
+
self.actionsView?.layer.zPosition = -1
|
|
359
|
+
self.updateActionsTransform()
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Restart autoClose timer if it was cancelled during detach
|
|
363
|
+
if autoClose {
|
|
364
|
+
scheduleAutoClose()
|
|
365
|
+
}
|
|
366
|
+
}
|
|
332
367
|
}
|
|
333
368
|
|
|
334
369
|
public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
|
@@ -422,6 +457,9 @@ public class SwipeableView: ExpoView {
|
|
|
422
457
|
var translation = currentTranslation
|
|
423
458
|
if isAnimating, let presentationLayer = contentView?.layer.presentation() {
|
|
424
459
|
translation = presentationLayer.transform.m41 // tx component of transform
|
|
460
|
+
// Keep currentTranslation in sync so layoutSubviews and other guards
|
|
461
|
+
// see the real position, not a stale value from before animation started.
|
|
462
|
+
currentTranslation = translation
|
|
425
463
|
// Update actions to match content position
|
|
426
464
|
let progress = min(abs(translation) / actionsWidth, 1.0)
|
|
427
465
|
let offset = isLeading ? -actionsWidth : actionsWidth
|
|
@@ -516,7 +554,7 @@ public class SwipeableView: ExpoView {
|
|
|
516
554
|
}
|
|
517
555
|
}
|
|
518
556
|
|
|
519
|
-
if currentTranslation != 0 {
|
|
557
|
+
if isOpen && currentTranslation != 0 {
|
|
520
558
|
contentView?.transform = CGAffineTransform(translationX: currentTranslation, y: 0)
|
|
521
559
|
updateActionsTransform()
|
|
522
560
|
}
|
|
@@ -875,11 +913,11 @@ extension SwipeableView: UIGestureRecognizerDelegate {
|
|
|
875
913
|
let shouldBegin = isOpen || (isLeading ? velocity.x > 0 : velocity.x < 0)
|
|
876
914
|
|
|
877
915
|
if shouldBegin {
|
|
878
|
-
// Prevent our pan from starting when the touch is on a
|
|
879
|
-
//
|
|
880
|
-
//
|
|
916
|
+
// Prevent our pan from starting when the touch is on a RNGH-managed view.
|
|
917
|
+
// Delegate-based conflict resolution is broken with RNGH (it overrides delegates),
|
|
918
|
+
// so we use hit-test prevention instead.
|
|
881
919
|
let touchPoint = pan.location(in: self)
|
|
882
|
-
if let hitView = super.hitTest(touchPoint, with: nil),
|
|
920
|
+
if let hitView = super.hitTest(touchPoint, with: nil), hasRNGHGesture(from: hitView) {
|
|
883
921
|
return false
|
|
884
922
|
}
|
|
885
923
|
cancelFabricTouches()
|
package/jestSetup.js
CHANGED
|
@@ -5,7 +5,8 @@ jest.mock('./src/SwipeableView.tsx', () => {
|
|
|
5
5
|
const SwipeableModule = {
|
|
6
6
|
openByKey: jest.fn(),
|
|
7
7
|
closeByKey: jest.fn(),
|
|
8
|
-
closeAll: jest.fn()
|
|
8
|
+
closeAll: jest.fn(),
|
|
9
|
+
isOpenByKey: jest.fn().mockReturnValue(false)
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
const SwipeableView = React.forwardRef((props, ref) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-swipeable-actions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7-canary.0",
|
|
4
4
|
"description": "High-performance native swipeable row component for React Native using Expo Modules",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -27,10 +27,11 @@
|
|
|
27
27
|
"test:all": "npm test && npm run test:ios && npm run test:android",
|
|
28
28
|
"test:watch": "jest --watch",
|
|
29
29
|
"test:coverage": "jest --coverage",
|
|
30
|
-
"lint": "
|
|
31
|
-
"lint:fix": "
|
|
32
|
-
"format": "
|
|
33
|
-
"format:check": "
|
|
30
|
+
"lint": "biome lint src example e2e",
|
|
31
|
+
"lint:fix": "biome lint --write src example e2e",
|
|
32
|
+
"format": "biome format --write src example e2e",
|
|
33
|
+
"format:check": "biome format src example e2e",
|
|
34
|
+
"check": "biome check src example e2e",
|
|
34
35
|
"typecheck": "tsc --noEmit",
|
|
35
36
|
"android": "expo run:android",
|
|
36
37
|
"ios": "expo run:ios",
|
|
@@ -64,19 +65,13 @@
|
|
|
64
65
|
"react-native": ">=0.73.0"
|
|
65
66
|
},
|
|
66
67
|
"devDependencies": {
|
|
68
|
+
"@biomejs/biome": "2.4.9",
|
|
67
69
|
"@testing-library/react-native": "^12.4.0",
|
|
68
70
|
"@types/jest": "^29.5.0",
|
|
69
71
|
"@types/react": "19.1.1",
|
|
70
|
-
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
71
|
-
"@typescript-eslint/parser": "^6.0.0",
|
|
72
|
-
"eslint": "^8.57.0",
|
|
73
|
-
"eslint-config-prettier": "^9.1.0",
|
|
74
|
-
"eslint-plugin-react": "^7.34.0",
|
|
75
|
-
"eslint-plugin-react-hooks": "^4.6.0",
|
|
76
72
|
"expo-modules-core": "^3.0.26",
|
|
77
73
|
"jest": "^29.7.0",
|
|
78
|
-
"prettier": "^3.
|
|
79
|
-
"prettier-config-holepunch": "^2.0.0",
|
|
74
|
+
"prettier": "^3.8.1",
|
|
80
75
|
"react": "19.1.1",
|
|
81
76
|
"react-native": "0.82.1",
|
|
82
77
|
"react-test-renderer": "19.1.1",
|
package/src/Swipeable.tsx
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
import { forwardRef, useImperativeHandle, useRef,
|
|
2
|
-
import { StyleSheet, View
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
SwipeableProps,
|
|
1
|
+
import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react'
|
|
2
|
+
import { Platform, StyleSheet, View } from 'react-native'
|
|
3
|
+
import { SWIPE_DEFAULTS } from './constants'
|
|
4
|
+
import { closeAll, closeByKey, isOpenByKey, openByKey } from './nativeModuleUtils'
|
|
5
|
+
import type {
|
|
6
|
+
NativeSwipeableRef,
|
|
8
7
|
SwipeableMethods,
|
|
8
|
+
SwipeableProps,
|
|
9
9
|
SwipeProgressEvent,
|
|
10
|
-
SwipeStateEvent
|
|
11
|
-
NativeSwipeableRef
|
|
10
|
+
SwipeStateEvent
|
|
12
11
|
} from './Swipeable.types'
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
12
|
+
import { SwipeableActions } from './SwipeableActions'
|
|
13
|
+
import { SwipeableView } from './SwipeableView'
|
|
15
14
|
|
|
16
15
|
function normalizeRecyclingKey(key: string | number | undefined): string | undefined {
|
|
17
16
|
if (key == null) return undefined
|
|
@@ -47,15 +46,32 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>(function Swipeabl
|
|
|
47
46
|
ref
|
|
48
47
|
) {
|
|
49
48
|
const nativeRef = useRef<NativeSwipeableRef>(null)
|
|
50
|
-
const [hasActionsRendered, setHasActionsRendered] = useState(false)
|
|
51
|
-
const swipeStartedRef = useRef(false)
|
|
52
49
|
|
|
53
|
-
// Normalize
|
|
54
|
-
// (set during render to ensure it's always current before callbacks fire)
|
|
50
|
+
// Normalize recyclingKey before hooks that depend on it
|
|
55
51
|
const normalizedKey = normalizeRecyclingKey(recyclingKey)
|
|
56
52
|
const recyclingKeyRef = useRef<string | undefined>(undefined)
|
|
57
53
|
recyclingKeyRef.current = normalizedKey
|
|
58
54
|
|
|
55
|
+
// Initialize from native cache: if this recyclingKey was open, render actions immediately.
|
|
56
|
+
// This handles FlatList remounting components during data reorder - React state is lost
|
|
57
|
+
// but native cache preserves the open state.
|
|
58
|
+
const [hasActionsRendered, setHasActionsRendered] = useState(() => {
|
|
59
|
+
return normalizedKey != null && isOpenByKey(normalizedKey)
|
|
60
|
+
})
|
|
61
|
+
const swipeStartedRef = useRef(false)
|
|
62
|
+
const initialKeyRef = useRef(normalizedKey)
|
|
63
|
+
|
|
64
|
+
// When recyclingKey changes (FlatList reorder/recycle), sync hasActionsRendered
|
|
65
|
+
// with the native cached open state. FlatList reuses component instances without
|
|
66
|
+
// remounting, so useState initializer doesn't re-run. Skip on initial mount
|
|
67
|
+
// since the useState initializer already handled it.
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
if (!normalizedKey) return
|
|
70
|
+
if (normalizedKey === initialKeyRef.current) return
|
|
71
|
+
initialKeyRef.current = normalizedKey
|
|
72
|
+
setHasActionsRendered(isOpenByKey(normalizedKey))
|
|
73
|
+
}, [normalizedKey])
|
|
74
|
+
|
|
59
75
|
// Development-time validation
|
|
60
76
|
if (__DEV__) {
|
|
61
77
|
if (autoClose && recyclingKey == null) {
|
|
@@ -144,7 +160,12 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>(function Swipeabl
|
|
|
144
160
|
onSwipeEnd={handleSwipeEnd}
|
|
145
161
|
>
|
|
146
162
|
{hasActionsRendered && (
|
|
147
|
-
<SwipeableActions
|
|
163
|
+
<SwipeableActions
|
|
164
|
+
actionsPosition={actionsPosition}
|
|
165
|
+
{...(testID ? { testID: `${testID}-actions` } : {})}
|
|
166
|
+
>
|
|
167
|
+
{actions}
|
|
168
|
+
</SwipeableActions>
|
|
148
169
|
)}
|
|
149
170
|
|
|
150
171
|
<View style={styles.content}>{children}</View>
|
package/src/Swipeable.types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ReactNode, Ref } from 'react'
|
|
2
|
-
import {
|
|
1
|
+
import type { ReactNode, Ref } from 'react'
|
|
2
|
+
import type { StyleProp, ViewProps, ViewStyle } from 'react-native'
|
|
3
3
|
|
|
4
4
|
export interface SwipeProgressEvent {
|
|
5
5
|
nativeEvent: {
|
package/src/SwipeableActions.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
2
|
import { StyleSheet, View } from 'react-native'
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -9,6 +9,8 @@ export interface SwipeableActionsProps {
|
|
|
9
9
|
children: ReactNode
|
|
10
10
|
/** Position of actions: 'left' or 'right' */
|
|
11
11
|
actionsPosition: 'left' | 'right'
|
|
12
|
+
/** Test ID for e2e testing */
|
|
13
|
+
testID?: string
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
/**
|
|
@@ -16,11 +18,12 @@ export interface SwipeableActionsProps {
|
|
|
16
18
|
*
|
|
17
19
|
* Handles entrance animation when children mount.
|
|
18
20
|
*/
|
|
19
|
-
export function SwipeableActions({ children, actionsPosition }: SwipeableActionsProps) {
|
|
21
|
+
export function SwipeableActions({ children, actionsPosition, testID }: SwipeableActionsProps) {
|
|
20
22
|
return (
|
|
21
23
|
<View
|
|
22
24
|
style={actionsPosition === 'left' ? styles.actionsLeading : styles.actionsTrailing}
|
|
23
25
|
pointerEvents={children ? 'auto' : 'none'}
|
|
26
|
+
testID={testID}
|
|
24
27
|
>
|
|
25
28
|
{children}
|
|
26
29
|
</View>
|
package/src/SwipeableView.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { requireNativeModule, requireNativeViewManager } from 'expo-modules-core'
|
|
2
|
+
import { type ComponentProps, createElement, forwardRef } from 'react'
|
|
3
|
+
import type { NativeSwipeableRef, SwipeableViewProps } from './Swipeable.types'
|
|
4
4
|
|
|
5
5
|
const NativeView = requireNativeViewManager<SwipeableViewProps>('Swipeable')
|
|
6
6
|
|
|
@@ -9,6 +9,7 @@ interface SwipeableNativeModule {
|
|
|
9
9
|
openByKey: (key: string) => void
|
|
10
10
|
closeByKey: (key: string, animated?: boolean) => void
|
|
11
11
|
closeAll: (animated?: boolean) => void
|
|
12
|
+
isOpenByKey: (key: string) => boolean
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
export const SwipeableModule = requireNativeModule<SwipeableNativeModule>('Swipeable')
|
package/src/index.ts
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
|
+
export { SWIPE_DEFAULTS, SWIPE_PROGRESS } from './constants'
|
|
2
|
+
export type { NativeModuleError, SwipeableErrorHandler } from './nativeModuleUtils'
|
|
3
|
+
// Error handling utilities
|
|
4
|
+
export { setGlobalErrorHandler } from './nativeModuleUtils'
|
|
1
5
|
export { default as Swipeable } from './Swipeable'
|
|
2
|
-
export { SwipeableView } from './SwipeableView'
|
|
3
|
-
|
|
4
6
|
export type {
|
|
5
|
-
|
|
7
|
+
NativeSwipeableRef,
|
|
6
8
|
SwipeableMethods,
|
|
9
|
+
SwipeableProps,
|
|
7
10
|
SwipeableStatic,
|
|
8
11
|
SwipeableViewProps,
|
|
9
12
|
SwipeProgressEvent,
|
|
10
|
-
SwipeStateEvent
|
|
11
|
-
NativeSwipeableRef
|
|
13
|
+
SwipeStateEvent
|
|
12
14
|
} from './Swipeable.types'
|
|
13
|
-
|
|
14
|
-
export { SWIPE_PROGRESS, SWIPE_DEFAULTS } from './constants'
|
|
15
|
-
|
|
16
|
-
// Error handling utilities
|
|
17
|
-
export { setGlobalErrorHandler } from './nativeModuleUtils'
|
|
18
|
-
export type { SwipeableErrorHandler, NativeModuleError } from './nativeModuleUtils'
|
|
15
|
+
export { SwipeableView } from './SwipeableView'
|
package/src/nativeModuleUtils.ts
CHANGED