react-native-swipeable-actions 0.1.5 → 0.1.6

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.
@@ -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
- if (child === actionsView && actionsWidthPx > 0f) {
317
- val actionsWidthInt = actionsWidthPx.toInt()
318
- if (isLeading) {
319
- child.layout(0, 0, actionsWidthInt, height)
320
- } else {
321
- child.layout(width - actionsWidthInt, 0, width, height)
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()
@@ -1 +1 @@
1
- {"version":3,"file":"Swipeable.d.ts","sourceRoot":"","sources":["../src/Swipeable.tsx"],"names":[],"mappings":"AAKA,OAAO,EACL,cAAc,EACd,gBAAgB,EAIjB,MAAM,mBAAmB,CAAA;AAe1B,QAAA,MAAM,SAAS,0HA8Hb,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"}
1
+ {"version":3,"file":"Swipeable.d.ts","sourceRoot":"","sources":["../src/Swipeable.tsx"],"names":[],"mappings":"AAKA,OAAO,EACL,cAAc,EACd,gBAAgB,EAIjB,MAAM,mBAAmB,CAAA;AAe1B,QAAA,MAAM,SAAS,0HAiJb,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"}
@@ -20,13 +20,26 @@ 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
- const [hasActionsRendered, setHasActionsRendered] = (0, react_1.useState)(false);
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
+ // When recyclingKey changes (FlatList reorder/recycle), sync hasActionsRendered
35
+ // with the native cached open state. FlatList reuses component instances without
36
+ // remounting, so useState initializer doesn't re-run.
37
+ (0, react_1.useEffect)(() => {
38
+ if (!normalizedKey)
39
+ return;
40
+ const cachedOpen = (0, nativeModuleUtils_1.isOpenByKey)(normalizedKey);
41
+ setHasActionsRendered(cachedOpen);
42
+ }, [normalizedKey]);
30
43
  // Development-time validation
31
44
  if (__DEV__) {
32
45
  if (autoClose && recyclingKey == null) {
@@ -76,7 +89,9 @@ const Swipeable = (0, react_1.forwardRef)(function Swipeable({ children, actions
76
89
  onSwipeEnd?.(event.nativeEvent.state);
77
90
  }, [onSwipeEnd]);
78
91
  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}>{actions}</SwipeableActions_1.SwipeableActions>)}
92
+ {hasActionsRendered && (<SwipeableActions_1.SwipeableActions actionsPosition={actionsPosition} {...(testID ? { testID: `${testID}-actions` } : {})}>
93
+ {actions}
94
+ </SwipeableActions_1.SwipeableActions>)}
80
95
 
81
96
  <react_native_1.View style={styles.content}>{children}</react_native_1.View>
82
97
  </SwipeableView_1.SwipeableView>);
@@ -1 +1 @@
1
- {"version":3,"file":"Swipeable.js","sourceRoot":"","sources":["../src/Swipeable.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Swipeable.js","sourceRoot":"","sources":["../src/Swipeable.tsx"],"names":[],"mappings":"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"}
@@ -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): React.JSX.Element;
18
+ export declare function SwipeableActions({ children, actionsPosition, testID }: SwipeableActionsProps): 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,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGxC;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,kCAAkC;IAClC,QAAQ,EAAE,SAAS,CAAA;IACnB,6CAA6C;IAC7C,eAAe,EAAE,MAAM,GAAG,OAAO,CAAA;CAClC;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,QAAQ,EAAE,eAAe,EAAE,EAAE,qBAAqB,qBASpF"}
1
+ {"version":3,"file":"SwipeableActions.d.ts","sourceRoot":"","sources":["../src/SwipeableActions.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGxC;;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,qBAU5F"}
@@ -11,8 +11,8 @@ const react_native_1 = require("react-native");
11
11
  *
12
12
  * Handles entrance animation when children mount.
13
13
  */
14
- function SwipeableActions({ children, actionsPosition }) {
15
- return (<react_native_1.View style={actionsPosition === 'left' ? styles.actionsLeading : styles.actionsTrailing} pointerEvents={children ? 'auto' : 'none'}>
14
+ function SwipeableActions({ children, actionsPosition, testID }) {
15
+ return (<react_native_1.View style={actionsPosition === 'left' ? styles.actionsLeading : styles.actionsTrailing} pointerEvents={children ? 'auto' : 'none'} testID={testID}>
16
16
  {children}
17
17
  </react_native_1.View>);
18
18
  }
@@ -1 +1 @@
1
- {"version":3,"file":"SwipeableActions.js","sourceRoot":"","sources":["../src/SwipeableActions.tsx"],"names":[],"mappings":";;;;;AAkBA,4CASC;AA3BD,kDAAwC;AACxC,+CAA+C;AAY/C;;;;GAIG;AACH,SAAgB,gBAAgB,CAAC,EAAE,QAAQ,EAAE,eAAe,EAAyB;IACnF,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,CAE1C;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"}
1
+ {"version":3,"file":"SwipeableActions.js","sourceRoot":"","sources":["../src/SwipeableActions.tsx"],"names":[],"mappings":";;;;;AAoBA,4CAUC;AA9BD,kDAAwC;AACxC,+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"}
@@ -3,6 +3,7 @@ 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;AAK1E,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;CACvC;AAED,eAAO,MAAM,eAAe,uBAA0D,CAAA;AAEtF,eAAO,MAAM,aAAa,mHAIzB,CAAA"}
1
+ {"version":3,"file":"SwipeableView.d.ts","sourceRoot":"","sources":["../src/SwipeableView.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAK1E,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"}
@@ -1 +1 @@
1
- {"version":3,"file":"SwipeableView.js","sourceRoot":"","sources":["../src/SwipeableView.tsx"],"names":[],"mappings":";;;AAAA,iCAAiE;AACjE,yDAAiF;AAGjF,MAAM,UAAU,GAAG,IAAA,4CAAwB,EAAqB,WAAW,CAAC,CAAA;AAS/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"}
1
+ {"version":3,"file":"SwipeableView.js","sourceRoot":"","sources":["../src/SwipeableView.tsx"],"names":[],"mappings":";;;AAAA,iCAAiE;AACjE,yDAAiF;AAGjF,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"}
@@ -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;AAxDD,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"}
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"}
@@ -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
@@ -31,13 +31,23 @@ public class SwipeableView: ExpoView {
31
31
  return typeName.contains("SurfaceTouchHandler") || typeName.contains("RCTTouchHandler")
32
32
  }
33
33
 
34
- /// Walk from a hit-tested view up to (but not including) self, checking for gesture recognizers.
35
- /// Any child view with a gesture recognizer (RNGH, custom, etc.) takes priority over our pan.
36
- private func hasChildGesture(from view: UIView) -> Bool {
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
- if !(v.gestureRecognizers ?? []).isEmpty {
40
- return true
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,27 @@ 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)
60
+ private static let maxCacheSize = 1000
50
61
  private static var _openStateCache: [String: Bool] = [:]
62
+ private static var _openStateCacheOrder: [String] = []
51
63
  private static var _viewRegistry: [String: WeakRef<SwipeableView>] = [:]
52
64
 
53
- private static func getOpenState(for key: String) -> Bool {
65
+ static func getOpenState(for key: String) -> Bool {
54
66
  registryQueue.sync { _openStateCache[key] ?? false }
55
67
  }
56
68
 
57
69
  private static func setOpenState(for key: String, isOpen: Bool) {
58
- registryQueue.async(flags: .barrier) { _openStateCache[key] = isOpen }
70
+ registryQueue.async(flags: .barrier) {
71
+ if _openStateCache[key] == nil {
72
+ _openStateCacheOrder.append(key)
73
+ }
74
+ _openStateCache[key] = isOpen
75
+ // Evict oldest entries when cache exceeds max size
76
+ while _openStateCacheOrder.count > maxCacheSize {
77
+ let oldest = _openStateCacheOrder.removeFirst()
78
+ _openStateCache.removeValue(forKey: oldest)
79
+ }
80
+ }
59
81
  }
60
82
 
61
83
  private static func getView(for key: String) -> SwipeableView? {
@@ -291,17 +313,17 @@ public class SwipeableView: ExpoView {
291
313
  currentAnimator = nil
292
314
  isAnimating = false
293
315
 
294
- // Reset layer properties to prevent corruption
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
316
+ // Unregister from static registry (re-registered in didMoveToWindow)
301
317
  if let key = recyclingKey {
302
318
  Self.unregisterView(for: key)
303
- Self.clearOpenState(for: key)
304
319
  }
320
+
321
+ // Note: transforms and zPositions are NOT reset here.
322
+ // Fabric may remove and re-insert views during list reorder (DELETE+INSERT),
323
+ // which triggers this callback. Resetting transforms would cause open
324
+ // swipeables to visually close even though native state (isOpen,
325
+ // currentTranslation) remains correct. The recyclingKey setter handles
326
+ // resetting transforms when the view is actually recycled for a new item.
305
327
  }
306
328
  super.willMove(toSuperview: newSuperview)
307
329
  }
@@ -329,6 +351,28 @@ public class SwipeableView: ExpoView {
329
351
  panGesture.view?.removeGestureRecognizer(panGesture)
330
352
  addGestureRecognizer(panGesture)
331
353
  }
354
+
355
+ // Restore visual state after Fabric reorder (DELETE+INSERT cycle).
356
+ // Fabric may reset child transforms during prop reconciliation. Re-apply
357
+ // on next runloop tick to ensure we run after Fabric finishes mutations.
358
+ if isOpen && currentTranslation != 0 {
359
+ DispatchQueue.main.async { [weak self] in
360
+ guard let self = self,
361
+ self.isOpen,
362
+ self.currentTranslation != 0,
363
+ !self.isDragging,
364
+ !self.isAnimating else { return }
365
+ self.contentView?.transform = CGAffineTransform(translationX: self.currentTranslation, y: 0)
366
+ self.contentView?.layer.zPosition = 1
367
+ self.actionsView?.layer.zPosition = -1
368
+ self.updateActionsTransform()
369
+ }
370
+
371
+ // Restart autoClose timer if it was cancelled during detach
372
+ if autoClose {
373
+ scheduleAutoClose()
374
+ }
375
+ }
332
376
  }
333
377
 
334
378
  public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
@@ -519,6 +563,17 @@ public class SwipeableView: ExpoView {
519
563
  if currentTranslation != 0 {
520
564
  contentView?.transform = CGAffineTransform(translationX: currentTranslation, y: 0)
521
565
  updateActionsTransform()
566
+
567
+ // Fabric may reset child transforms during prop reconciliation after a list
568
+ // reorder. The async dispatch ensures we run after Fabric finishes mutations.
569
+ DispatchQueue.main.async { [weak self] in
570
+ guard let self = self,
571
+ self.currentTranslation != 0,
572
+ !self.isDragging,
573
+ !self.isAnimating else { return }
574
+ self.contentView?.transform = CGAffineTransform(translationX: self.currentTranslation, y: 0)
575
+ self.updateActionsTransform()
576
+ }
522
577
  }
523
578
  }
524
579
 
@@ -875,11 +930,11 @@ extension SwipeableView: UIGestureRecognizerDelegate {
875
930
  let shouldBegin = isOpen || (isLeading ? velocity.x > 0 : velocity.x < 0)
876
931
 
877
932
  if shouldBegin {
878
- // Prevent our pan from starting when the touch is on a child view that has
879
- // its own gesture recognizers (e.g. RNGH). Delegate-based conflict resolution
880
- // doesn't work with RNGH (it overrides delegates), so we yield via hit-test.
933
+ // Prevent our pan from starting when the touch is on a RNGH-managed view.
934
+ // Delegate-based conflict resolution is broken with RNGH (it overrides delegates),
935
+ // so we use hit-test prevention instead.
881
936
  let touchPoint = pan.location(in: self)
882
- if let hitView = super.hitTest(touchPoint, with: nil), hasChildGesture(from: hitView) {
937
+ if let hitView = super.hitTest(touchPoint, with: nil), hasRNGHGesture(from: hitView) {
883
938
  return false
884
939
  }
885
940
  cancelFabricTouches()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-swipeable-actions",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
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",
package/src/Swipeable.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import { forwardRef, useImperativeHandle, useRef, useCallback, useState } from 'react'
1
+ import { forwardRef, useImperativeHandle, useRef, useCallback, useState, useEffect } from 'react'
2
2
  import { StyleSheet, View, Platform } from 'react-native'
3
3
 
4
4
  import { SwipeableView } from './SwipeableView'
@@ -11,7 +11,7 @@ import {
11
11
  NativeSwipeableRef
12
12
  } from './Swipeable.types'
13
13
  import { SWIPE_DEFAULTS } from './constants'
14
- import { openByKey, closeByKey, closeAll } from './nativeModuleUtils'
14
+ import { openByKey, closeByKey, closeAll, isOpenByKey } from './nativeModuleUtils'
15
15
 
16
16
  function normalizeRecyclingKey(key: string | number | undefined): string | undefined {
17
17
  if (key == null) return undefined
@@ -47,15 +47,29 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>(function Swipeabl
47
47
  ref
48
48
  ) {
49
49
  const nativeRef = useRef<NativeSwipeableRef>(null)
50
- const [hasActionsRendered, setHasActionsRendered] = useState(false)
51
- const swipeStartedRef = useRef(false)
52
50
 
53
- // Normalize and sync recyclingKey to ref for use in useImperativeHandle
54
- // (set during render to ensure it's always current before callbacks fire)
51
+ // Normalize recyclingKey before hooks that depend on it
55
52
  const normalizedKey = normalizeRecyclingKey(recyclingKey)
56
53
  const recyclingKeyRef = useRef<string | undefined>(undefined)
57
54
  recyclingKeyRef.current = normalizedKey
58
55
 
56
+ // Initialize from native cache: if this recyclingKey was open, render actions immediately.
57
+ // This handles FlatList remounting components during data reorder - React state is lost
58
+ // but native cache preserves the open state.
59
+ const [hasActionsRendered, setHasActionsRendered] = useState(() => {
60
+ return normalizedKey != null && isOpenByKey(normalizedKey)
61
+ })
62
+ const swipeStartedRef = useRef(false)
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.
67
+ useEffect(() => {
68
+ if (!normalizedKey) return
69
+ const cachedOpen = isOpenByKey(normalizedKey)
70
+ setHasActionsRendered(cachedOpen)
71
+ }, [normalizedKey])
72
+
59
73
  // Development-time validation
60
74
  if (__DEV__) {
61
75
  if (autoClose && recyclingKey == null) {
@@ -144,7 +158,12 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>(function Swipeabl
144
158
  onSwipeEnd={handleSwipeEnd}
145
159
  >
146
160
  {hasActionsRendered && (
147
- <SwipeableActions actionsPosition={actionsPosition}>{actions}</SwipeableActions>
161
+ <SwipeableActions
162
+ actionsPosition={actionsPosition}
163
+ {...(testID ? { testID: `${testID}-actions` } : {})}
164
+ >
165
+ {actions}
166
+ </SwipeableActions>
148
167
  )}
149
168
 
150
169
  <View style={styles.content}>{children}</View>
@@ -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>
@@ -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')
@@ -55,3 +55,11 @@ export function closeAll(animated?: boolean): void {
55
55
  handleNativeError(error, 'closeAll')
56
56
  }
57
57
  }
58
+
59
+ export function isOpenByKey(key: string): boolean {
60
+ try {
61
+ return SwipeableModule.isOpenByKey(key)
62
+ } catch {
63
+ return false
64
+ }
65
+ }