@souscheflabs/reanimated-flashlist 0.5.3 → 0.5.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.
Files changed (52) hide show
  1. package/lib/AnimatedFlashList.js +1077 -498
  2. package/lib/AnimatedFlashListItem.js +197 -178
  3. package/lib/__tests__/components/AnimatedFlashList.test.js +461 -0
  4. package/lib/__tests__/components/AnimatedFlashListItem.test.js +460 -0
  5. package/lib/__tests__/contexts/DragStateContext.test.js +327 -0
  6. package/lib/__tests__/contexts/ListAnimationContext.test.js +715 -0
  7. package/lib/__tests__/hooks/useDragAnimatedStyle.test.js +206 -0
  8. package/lib/__tests__/hooks/useDragGesture.test.js +313 -0
  9. package/lib/__tests__/hooks/useDragShift.test.js +239 -0
  10. package/lib/__tests__/hooks/useDropCompensation.test.js +285 -0
  11. package/lib/__tests__/hooks/useListEntryAnimation.test.js +282 -0
  12. package/lib/__tests__/hooks/useListExitAnimation.test.js +386 -0
  13. package/lib/__tests__/hooks/useOverlayCrossfade.test.js +123 -0
  14. package/lib/__tests__/utils/test-utils.js +295 -140
  15. package/lib/constants/animations.js +85 -64
  16. package/lib/constants/drag.js +80 -49
  17. package/lib/constants/index.js +25 -16
  18. package/lib/contexts/DragStateContext.js +422 -476
  19. package/lib/contexts/ListAnimationContext.js +188 -194
  20. package/lib/contexts/index.js +36 -9
  21. package/lib/hooks/animations/index.js +18 -12
  22. package/lib/hooks/animations/useListEntryAnimation.js +125 -73
  23. package/lib/hooks/animations/useListExitAnimation.js +201 -119
  24. package/lib/hooks/drag/index.js +60 -34
  25. package/lib/hooks/drag/useAutoscroll.js +84 -34
  26. package/lib/hooks/drag/useDragAnimatedStyle.js +927 -337
  27. package/lib/hooks/drag/useDragCleanup.js +95 -82
  28. package/lib/hooks/drag/useDragGesture.js +746 -284
  29. package/lib/hooks/drag/useDragOverlay.js +149 -74
  30. package/lib/hooks/drag/useDragShift.js +161 -78
  31. package/lib/hooks/drag/useDropCompensation.js +82 -56
  32. package/lib/hooks/drag/useDropHandler.js +330 -201
  33. package/lib/hooks/drag/useOverlayCrossfade.js +125 -39
  34. package/lib/hooks/index.js +42 -17
  35. package/lib/index.js +167 -108
  36. package/lib/performance/hooks/index.js +32 -16
  37. package/lib/performance/hooks/useBenchmarkComparison.js +140 -124
  38. package/lib/performance/hooks/useFPSMonitor.js +352 -267
  39. package/lib/performance/hooks/useMemoryMonitor.js +120 -100
  40. package/lib/performance/hooks/useRenderProfiler.js +72 -86
  41. package/lib/performance/index.js +133 -70
  42. package/lib/performance/types.js +106 -49
  43. package/lib/performance/utils/compareResults.js +155 -121
  44. package/lib/performance/utils/index.js +72 -18
  45. package/lib/performance/utils/loadBaseline.js +110 -90
  46. package/lib/types/animations.js +4 -1
  47. package/lib/types/drag.js +4 -1
  48. package/lib/types/index.js +36 -17
  49. package/lib/types/list.js +4 -1
  50. package/lib/utils/dragStateReset.js +178 -29
  51. package/lib/utils/hoverCalculation.js +111 -44
  52. package/package.json +4 -3
@@ -1,394 +1,893 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
17
5
  });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
6
  exports.AnimatedFlashList = void 0;
37
- const react_1 = __importStar(require("react"));
38
- const react_native_1 = require("react-native");
39
- const react_native_reanimated_1 = __importStar(require("react-native-reanimated"));
40
- const react_native_gesture_handler_1 = require("react-native-gesture-handler");
41
- const react_native_worklets_1 = require("react-native-worklets");
42
- const flash_list_1 = require("@shopify/flash-list");
43
- const DragStateContext_1 = require("./contexts/DragStateContext");
44
- const useOverlayCrossfade_1 = require("./hooks/drag/useOverlayCrossfade");
45
- const ListAnimationContext_1 = require("./contexts/ListAnimationContext");
46
- const AnimatedFlashListItem_1 = require("./AnimatedFlashListItem");
47
- const drag_1 = require("./constants/drag");
48
- class AnimationErrorBoundary extends react_1.default.Component {
49
- state = { hasError: false };
50
- static getDerivedStateFromError() {
51
- return { hasError: true };
7
+ var _react = _interopRequireWildcard(require("react"));
8
+ var _reactNative = require("react-native");
9
+ var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reanimated"));
10
+ var _reactNativeGestureHandler = require("react-native-gesture-handler");
11
+ var _reactNativeWorklets = require("react-native-worklets");
12
+ var _flashList = require("@shopify/flash-list");
13
+ var _DragStateContext = require("./contexts/DragStateContext");
14
+ var _useOverlayCrossfade = require("./hooks/drag/useOverlayCrossfade");
15
+ var _ListAnimationContext = require("./contexts/ListAnimationContext");
16
+ var _AnimatedFlashListItem = require("./AnimatedFlashListItem");
17
+ var _drag = require("./constants/drag");
18
+ var _jsxRuntime = require("react/jsx-runtime");
19
+ var _excluded = ["data", "keyExtractor", "renderItem", "dragEnabled", "onReorder", "onReorderByNeighbors", "canDrag", "onHapticFeedback", "config", "onPrepareLayoutAnimation", "ListFooterComponent", "ListEmptyComponent", "onRefresh", "refreshing", "onEndReached", "onEndReachedThreshold", "contentContainerStyle"],
20
+ _excluded2 = ["extraData", "onCommitLayoutEffect"];
21
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, "default": e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
22
+ function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
23
+ function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
24
+ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
25
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; }
26
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
27
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
28
+ function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
29
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
30
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
31
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
32
+ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
33
+ function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
34
+ function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
35
+ function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
36
+ function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
37
+ function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
38
+ function _possibleConstructorReturn(t, e) { if (e && ("object" == _typeof(e) || "function" == typeof e)) return e; if (void 0 !== e) throw new TypeError("Derived constructors may only return object or undefined"); return _assertThisInitialized(t); }
39
+ function _assertThisInitialized(e) { if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return e; }
40
+ function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
41
+ function _getPrototypeOf(t) { return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) { return t.__proto__ || Object.getPrototypeOf(t); }, _getPrototypeOf(t); }
42
+ function _inherits(t, e) { if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function"); t.prototype = Object.create(e && e.prototype, { constructor: { value: t, writable: !0, configurable: !0 } }), Object.defineProperty(t, "prototype", { writable: !1 }), e && _setPrototypeOf(t, e); }
43
+ function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) { return t.__proto__ = e, t; }, _setPrototypeOf(t, e); }
44
+ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
45
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
46
+ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } /**
47
+ * Error boundary that catches JS errors in animation hooks and falls back
48
+ * to rendering children without animation wrappers.
49
+ * @internal
50
+ */
51
+ var AnimationErrorBoundary = /*#__PURE__*/function (_React$Component) {
52
+ function AnimationErrorBoundary() {
53
+ var _this;
54
+ _classCallCheck(this, AnimationErrorBoundary);
55
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
56
+ args[_key] = arguments[_key];
52
57
  }
53
- componentDidCatch(error) {
54
- if (__DEV__) {
55
- console.warn("[AnimatedFlashList] Animation error caught by error boundary. " +
56
- "Falling back to non-animated list.", error);
57
- }
58
+ _this = _callSuper(this, AnimationErrorBoundary, [].concat(args));
59
+ _defineProperty(_this, "state", {
60
+ hasError: false
61
+ });
62
+ return _this;
63
+ }
64
+ _inherits(AnimationErrorBoundary, _React$Component);
65
+ return _createClass(AnimationErrorBoundary, [{
66
+ key: "componentDidCatch",
67
+ value: function componentDidCatch(error) {
68
+ if (__DEV__) {
69
+ console.warn("[AnimatedFlashList] Animation error caught by error boundary. " + "Falling back to non-animated list.", error);
70
+ }
58
71
  }
59
- render() {
60
- if (this.state.hasError) {
61
- return this.props.fallback;
62
- }
63
- return this.props.children;
72
+ }, {
73
+ key: "render",
74
+ value: function render() {
75
+ if (this.state.hasError) {
76
+ return this.props.fallback;
77
+ }
78
+ return this.props.children;
64
79
  }
65
- }
66
- const DragOverlay = react_1.default.memo(function DragOverlay({ data, totalItemsRef, renderItem }) {
67
- const { overlayActive, overlayItemId, overlayBaseX, overlayBaseY, overlayWidth, overlayHeight, overlayTranslateY, overlayReady, overlayVisible, scrollOffset, dragStartScrollOffset, draggedScale, isDragging, isDropping, overlayTouchOffset, overlayAbsoluteY, overlayContainerY, dragOverlayOpacity, overlayFrozenItemId, overlayRenderedOnUI, } = (0, DragStateContext_1.useDragState)();
68
- // Drive overlay crossfade animation on UI thread
69
- // This ensures smooth visibility transitions without JS scheduling races
70
- (0, useOverlayCrossfade_1.useOverlayCrossfade)();
71
- const [overlayId, setOverlayId] = (0, react_1.useState)(null);
72
- const [frozenId, setFrozenId] = (0, react_1.useState)(null);
73
- // O(1) lookup map for items by ID
74
- const itemMap = (0, react_1.useMemo)(() => {
75
- return new Map(data.map(item => [item.id, item]));
76
- }, [data]);
77
- const setOverlayIdSafe = (0, react_1.useCallback)((nextId) => {
78
- setOverlayId(nextId);
79
- }, []);
80
- const setFrozenIdSafe = (0, react_1.useCallback)((nextId) => {
81
- setFrozenId(nextId);
82
- }, []);
83
- (0, react_native_reanimated_1.useAnimatedReaction)(() => overlayItemId.value, (current, prev) => {
84
- "worklet";
85
- if (current === prev)
86
- return;
87
- (0, react_native_worklets_1.scheduleOnRN)(setOverlayIdSafe, current || null);
88
- }, [setOverlayIdSafe]);
89
- // Track frozen ID for rendering during fade-out
90
- (0, react_native_reanimated_1.useAnimatedReaction)(() => overlayFrozenItemId.value, (current, prev) => {
91
- "worklet";
92
- if (current === prev)
93
- return;
94
- (0, react_native_worklets_1.scheduleOnRN)(setFrozenIdSafe, current || null);
95
- }, [setFrozenIdSafe]);
96
- (0, react_1.useEffect)(() => {
97
- overlayVisible.value = Boolean(overlayId);
98
- }, [overlayId, overlayVisible]);
99
- // Use frozenId to keep overlay rendered during fade-out
100
- // Uses O(1) Map lookup instead of O(n) find()
101
- const overlayItem = (0, react_1.useMemo)(() => {
102
- const id = frozenId || overlayId;
103
- if (!id)
104
- return null;
105
- return itemMap.get(id) ?? null;
106
- }, [itemMap, frozenId, overlayId]);
107
- // Create disabled gesture and false isDragging for overlay rendering
108
- // This allows renderItem to show drag handle UI without functional gesture
109
- const disabledGesture = (0, react_1.useMemo)(() => react_native_gesture_handler_1.Gesture.Pan().enabled(false), []);
110
- const falseIsDragging = (0, react_native_reanimated_1.useSharedValue)(false);
111
- const overlayStyle = (0, react_native_reanimated_1.useAnimatedStyle)(() => {
112
- // Use frozenItemId so overlay stays renderable during fade-out
113
- const itemId = overlayFrozenItemId.value || overlayItemId.value;
114
- // Keep rendering if frozenItemId is set (crossfade in progress) even if overlayReady is false.
115
- // This prevents the overlay from becoming invisible before the crossfade completes,
116
- // which would cause a brief moment where both overlay AND original are invisible.
117
- // FIXED: Also require overlayVisible to ensure JS-side overlayItem exists before fading.
118
- // overlayVisible is only cleared AFTER fade-out completes (in useOverlayCrossfade),
119
- // so this doesn't break fade-out.
120
- if (!itemId || (!overlayFrozenItemId.value && !overlayReady.value) || !overlayVisible.value) {
121
- // Mark overlay as NOT rendered when returning invisible style
122
- overlayRenderedOnUI.value = false;
123
- return { opacity: 0, position: "absolute" };
124
- }
125
- // Mark overlay as rendered on UI thread - this is the KEY fix for the vanish bug.
126
- // Setting this here (in useAnimatedStyle, which runs on UI thread) instead of
127
- // in useEffect (JS thread) ensures the original item knows the overlay has
128
- // actually rendered BEFORE the crossfade starts. This prevents the race condition
129
- // where the original starts fading out before the overlay appears.
130
- overlayRenderedOnUI.value = true;
131
- // Include isDropping so scale animates smoothly during drop phase
132
- // Without this, scale jumps from 1.03 to 1.0 instantly when isDragging becomes false,
133
- // causing a ~1-2px visual jump due to the scale transform's center pivot point.
134
- const scale = (isDragging.value || isDropping.value) ? draggedScale.value : 1;
135
- // During active drag, use absolute finger position for reliable overlay tracking.
136
- // This avoids lag from JS-thread scroll offset updates during simultaneous scroll+drag.
137
- // Formula: top = absoluteY - touchOffset - containerY
138
- // Where:
139
- // - absoluteY = finger's current screen Y position
140
- // - touchOffset = how far down in the item the finger started
141
- // - containerY = overlay container's screen Y position
142
- //
143
- // During drop animation (isDropping), switch back to slot-based positioning
144
- // since the finger is no longer tracking and we need to animate to final slot.
145
- let top;
146
- if (isDragging.value) {
147
- // Active drag: follow finger precisely
148
- top = overlayAbsoluteY.value - overlayTouchOffset.value - overlayContainerY.value;
149
- }
150
- else {
151
- // Drop animation: use slot-based positioning with scroll compensation
152
- // Note: scrollDelta is SUBTRACTED because overlayBaseY was measured at drag start
153
- // (when scroll was dragStartScrollOffset). As user scrolls down (scrollDelta > 0),
154
- // the target slot moves UP visually, so we subtract.
155
- const scrollDelta = scrollOffset.value - dragStartScrollOffset.value;
156
- top = overlayBaseY.value + overlayTranslateY.value - scrollDelta;
157
- }
80
+ }], [{
81
+ key: "getDerivedStateFromError",
82
+ value: function getDerivedStateFromError() {
83
+ return {
84
+ hasError: true
85
+ };
86
+ }
87
+ }]);
88
+ }(_react["default"].Component);
89
+ var _worklet_14753868910793_init_data = {
90
+ code: "function AnimatedFlashListTsx1(){const{overlayItemId}=this.__closure;return overlayItemId.value;}",
91
+ location: "/Users/tani/Desktop/sous-chef-labs/reanimated-flashlist/src/AnimatedFlashList.tsx",
92
+ sourceMap: "{\"version\":3,\"names\":[\"AnimatedFlashListTsx1\",\"overlayItemId\",\"__closure\",\"value\"],\"sources\":[\"/Users/tani/Desktop/sous-chef-labs/reanimated-flashlist/src/AnimatedFlashList.tsx\"],\"mappings\":\"AAqII,SAAAA,sBAAA,QAAAC,aAAA,OAAAC,SAAA,OAAM,CAAAD,aAAc,CAAAE,KAAA\",\"ignoreList\":[]}"
93
+ };
94
+ var _worklet_15313668423224_init_data = {
95
+ code: "function AnimatedFlashListTsx2(current,prev){const{scheduleOnRN,setOverlayIdSafe}=this.__closure;if(current===prev)return;scheduleOnRN(setOverlayIdSafe,current||null);}",
96
+ location: "/Users/tani/Desktop/sous-chef-labs/reanimated-flashlist/src/AnimatedFlashList.tsx",
97
+ sourceMap: "{\"version\":3,\"names\":[\"AnimatedFlashListTsx2\",\"current\",\"prev\",\"scheduleOnRN\",\"setOverlayIdSafe\",\"__closure\"],\"sources\":[\"/Users/tani/Desktop/sous-chef-labs/reanimated-flashlist/src/AnimatedFlashList.tsx\"],\"mappings\":\"AAsII,QAAC,CAAAA,qBAAkBA,CAAAC,OAAA,CAAAC,IAAA,QAAAC,YAAA,CAAAC,gBAAA,OAAAC,SAAA,CAEjB,GAAIJ,OAAO,GAAKC,IAAI,CAAE,OACtBC,YAAY,CAACC,gBAAgB,CAAEH,OAAO,EAAI,IAAI,CAAC,CACjD\",\"ignoreList\":[]}"
98
+ };
99
+ var _worklet_17236566315019_init_data = {
100
+ code: "function AnimatedFlashListTsx3(){const{overlayFrozenItemId}=this.__closure;return overlayFrozenItemId.value;}",
101
+ location: "/Users/tani/Desktop/sous-chef-labs/reanimated-flashlist/src/AnimatedFlashList.tsx",
102
+ sourceMap: "{\"version\":3,\"names\":[\"AnimatedFlashListTsx3\",\"overlayFrozenItemId\",\"__closure\",\"value\"],\"sources\":[\"/Users/tani/Desktop/sous-chef-labs/reanimated-flashlist/src/AnimatedFlashList.tsx\"],\"mappings\":\"AAgJI,SAAAA,sBAAA,QAAAC,mBAAA,OAAAC,SAAA,OAAM,CAAAD,mBAAoB,CAAAE,KAAA\",\"ignoreList\":[]}"
103
+ };
104
+ var _worklet_12695437420222_init_data = {
105
+ code: "function AnimatedFlashListTsx4(current,prev){const{scheduleOnRN,setFrozenIdSafe}=this.__closure;if(current===prev)return;scheduleOnRN(setFrozenIdSafe,current||null);}",
106
+ location: "/Users/tani/Desktop/sous-chef-labs/reanimated-flashlist/src/AnimatedFlashList.tsx",
107
+ sourceMap: "{\"version\":3,\"names\":[\"AnimatedFlashListTsx4\",\"current\",\"prev\",\"scheduleOnRN\",\"setFrozenIdSafe\",\"__closure\"],\"sources\":[\"/Users/tani/Desktop/sous-chef-labs/reanimated-flashlist/src/AnimatedFlashList.tsx\"],\"mappings\":\"AAiJI,QAAC,CAAAA,qBAAkBA,CAAAC,OAAA,CAAAC,IAAA,QAAAC,YAAA,CAAAC,eAAA,OAAAC,SAAA,CAEjB,GAAIJ,OAAO,GAAKC,IAAI,CAAE,OACtBC,YAAY,CAACC,eAAe,CAAEH,OAAO,EAAI,IAAI,CAAC,CAChD\",\"ignoreList\":[]}"
108
+ };
109
+ var _worklet_3465813210122_init_data = {
110
+ code: "function AnimatedFlashListTsx5(){const{overlayFrozenItemId,overlayItemId,overlayReady,overlayVisible,overlayRenderedOnUI,isDragging,isDropping,draggedScale,overlayAbsoluteY,overlayTouchOffset,overlayContainerY,scrollOffset,dragStartScrollOffset,overlayBaseY,overlayTranslateY,overlayBaseX,overlayWidth,overlayHeight,dragOverlayOpacity}=this.__closure;const itemId=overlayFrozenItemId.value||overlayItemId.value;if(!itemId||!overlayFrozenItemId.value&&!overlayReady.value||!overlayVisible.value){overlayRenderedOnUI.value=false;return{opacity:0,position:\"absolute\"};}overlayRenderedOnUI.value=true;const scale=isDragging.value||isDropping.value?draggedScale.value:1;let top;if(isDragging.value){top=overlayAbsoluteY.value-overlayTouchOffset.value-overlayContainerY.value;}else{const scrollDelta=scrollOffset.value-dragStartScrollOffset.value;top=overlayBaseY.value+overlayTranslateY.value-scrollDelta;}return{position:\"absolute\",left:overlayBaseX.value,top:top,width:overlayWidth.value,height:overlayHeight.value,zIndex:1000,transform:[{scale:scale}],opacity:dragOverlayOpacity.value};}",
111
+ location: "/Users/tani/Desktop/sous-chef-labs/reanimated-flashlist/src/AnimatedFlashList.tsx",
112
+ sourceMap: "{\"version\":3,\"names\":[\"AnimatedFlashListTsx5\",\"overlayFrozenItemId\",\"overlayItemId\",\"overlayReady\",\"overlayVisible\",\"overlayRenderedOnUI\",\"isDragging\",\"isDropping\",\"draggedScale\",\"overlayAbsoluteY\",\"overlayTouchOffset\",\"overlayContainerY\",\"scrollOffset\",\"dragStartScrollOffset\",\"overlayBaseY\",\"overlayTranslateY\",\"overlayBaseX\",\"overlayWidth\",\"overlayHeight\",\"dragOverlayOpacity\",\"__closure\",\"itemId\",\"value\",\"opacity\",\"position\",\"scale\",\"top\",\"scrollDelta\",\"left\",\"width\",\"height\",\"zIndex\",\"transform\"],\"sources\":[\"/Users/tani/Desktop/sous-chef-labs/reanimated-flashlist/src/AnimatedFlashList.tsx\"],\"mappings\":\"AA0KwC,SAAAA,qBAAMA,CAAA,QAAAC,mBAAA,CAAAC,aAAA,CAAAC,YAAA,CAAAC,cAAA,CAAAC,mBAAA,CAAAC,UAAA,CAAAC,UAAA,CAAAC,YAAA,CAAAC,gBAAA,CAAAC,kBAAA,CAAAC,iBAAA,CAAAC,YAAA,CAAAC,qBAAA,CAAAC,YAAA,CAAAC,iBAAA,CAAAC,YAAA,CAAAC,YAAA,CAAAC,aAAA,CAAAC,kBAAA,OAAAC,SAAA,CAE1C,KAAM,CAAAC,MAAM,CAAGpB,mBAAmB,CAACqB,KAAK,EAAIpB,aAAa,CAACoB,KAAK,CAQ/D,GAAI,CAACD,MAAM,EAAK,CAACpB,mBAAmB,CAACqB,KAAK,EAAI,CAACnB,YAAY,CAACmB,KAAM,EAAI,CAAClB,cAAc,CAACkB,KAAK,CAAE,CAE3FjB,mBAAmB,CAACiB,KAAK,CAAG,KAAK,CACjC,MAAO,CAAEC,OAAO,CAAE,CAAC,CAAEC,QAAQ,CAAE,UAAW,CAAC,CAC7C,CAOAnB,mBAAmB,CAACiB,KAAK,CAAG,IAAI,CAKhC,KAAM,CAAAG,KAAK,CAAInB,UAAU,CAACgB,KAAK,EAAIf,UAAU,CAACe,KAAK,CAAId,YAAY,CAACc,KAAK,CAAG,CAAC,CAY7E,GAAI,CAAAI,GAAW,CACf,GAAIpB,UAAU,CAACgB,KAAK,CAAE,CAEpBI,GAAG,CAAGjB,gBAAgB,CAACa,KAAK,CAAGZ,kBAAkB,CAACY,KAAK,CAAGX,iBAAiB,CAACW,KAAK,CACnF,CAAC,IAAM,CAKL,KAAM,CAAAK,WAAW,CAAGf,YAAY,CAACU,KAAK,CAAGT,qBAAqB,CAACS,KAAK,CACpEI,GAAG,CAAGZ,YAAY,CAACQ,KAAK,CAAGP,iBAAiB,CAACO,KAAK,CAAGK,WAAW,CAClE,CAEA,MAAO,CACLH,QAAQ,CAAE,UAAU,CACpBI,IAAI,CAAEZ,YAAY,CAACM,KAAK,CACxBI,GAAG,CAAHA,GAAG,CACHG,KAAK,CAAEZ,YAAY,CAACK,KAAK,CACzBQ,MAAM,CAAEZ,aAAa,CAACI,KAAK,CAC3BS,MAAM,CAAE,IAAI,CACZC,SAAS,CAAE,CAAC,CAAEP,KAAA,CAAAA,KAAM,CAAC,CAAC,CACtBF,OAAO,CAAEJ,kBAAkB,CAACG,KAC9B,CAAC,CACH\",\"ignoreList\":[]}"
113
+ };
114
+ var DragOverlay = /*#__PURE__*/_react["default"].memo(function DragOverlay(_ref) {
115
+ var _totalItemsRef$curren;
116
+ var data = _ref.data,
117
+ totalItemsRef = _ref.totalItemsRef,
118
+ renderItem = _ref.renderItem;
119
+ var _useDragState = (0, _DragStateContext.useDragState)(),
120
+ overlayActive = _useDragState.overlayActive,
121
+ overlayItemId = _useDragState.overlayItemId,
122
+ overlayBaseX = _useDragState.overlayBaseX,
123
+ overlayBaseY = _useDragState.overlayBaseY,
124
+ overlayWidth = _useDragState.overlayWidth,
125
+ overlayHeight = _useDragState.overlayHeight,
126
+ overlayTranslateY = _useDragState.overlayTranslateY,
127
+ overlayReady = _useDragState.overlayReady,
128
+ overlayVisible = _useDragState.overlayVisible,
129
+ scrollOffset = _useDragState.scrollOffset,
130
+ dragStartScrollOffset = _useDragState.dragStartScrollOffset,
131
+ draggedScale = _useDragState.draggedScale,
132
+ isDragging = _useDragState.isDragging,
133
+ isDropping = _useDragState.isDropping,
134
+ overlayTouchOffset = _useDragState.overlayTouchOffset,
135
+ overlayAbsoluteY = _useDragState.overlayAbsoluteY,
136
+ overlayContainerY = _useDragState.overlayContainerY,
137
+ dragOverlayOpacity = _useDragState.dragOverlayOpacity,
138
+ overlayFrozenItemId = _useDragState.overlayFrozenItemId,
139
+ overlayRenderedOnUI = _useDragState.overlayRenderedOnUI;
140
+
141
+ // Drive overlay crossfade animation on UI thread
142
+ // This ensures smooth visibility transitions without JS scheduling races
143
+ (0, _useOverlayCrossfade.useOverlayCrossfade)();
144
+ var _useState = (0, _react.useState)(null),
145
+ _useState2 = _slicedToArray(_useState, 2),
146
+ overlayId = _useState2[0],
147
+ setOverlayId = _useState2[1];
148
+ var _useState3 = (0, _react.useState)(null),
149
+ _useState4 = _slicedToArray(_useState3, 2),
150
+ frozenId = _useState4[0],
151
+ setFrozenId = _useState4[1];
152
+
153
+ // O(1) lookup map for items by ID
154
+ var itemMap = (0, _react.useMemo)(function () {
155
+ return new Map(data.map(function (item) {
156
+ return [item.id, item];
157
+ }));
158
+ }, [data]);
159
+ var setOverlayIdSafe = (0, _react.useCallback)(function (nextId) {
160
+ setOverlayId(nextId);
161
+ }, []);
162
+ var setFrozenIdSafe = (0, _react.useCallback)(function (nextId) {
163
+ setFrozenId(nextId);
164
+ }, []);
165
+ (0, _reactNativeReanimated.useAnimatedReaction)(function AnimatedFlashListTsx1Factory(_ref2) {
166
+ var _worklet_14753868910793_init_data = _ref2._worklet_14753868910793_init_data,
167
+ overlayItemId = _ref2.overlayItemId;
168
+ var _e = [new global.Error(), -2, -27];
169
+ var AnimatedFlashListTsx1 = function AnimatedFlashListTsx1() {
170
+ return overlayItemId.value;
171
+ };
172
+ AnimatedFlashListTsx1.__closure = {
173
+ overlayItemId: overlayItemId
174
+ };
175
+ AnimatedFlashListTsx1.__workletHash = 14753868910793;
176
+ AnimatedFlashListTsx1.__pluginVersion = "0.7.2";
177
+ AnimatedFlashListTsx1.__initData = _worklet_14753868910793_init_data;
178
+ AnimatedFlashListTsx1.__stackDetails = _e;
179
+ return AnimatedFlashListTsx1;
180
+ }({
181
+ _worklet_14753868910793_init_data: _worklet_14753868910793_init_data,
182
+ overlayItemId: overlayItemId
183
+ }), function AnimatedFlashListTsx2Factory(_ref3) {
184
+ var _worklet_15313668423224_init_data = _ref3._worklet_15313668423224_init_data,
185
+ scheduleOnRN = _ref3.scheduleOnRN,
186
+ setOverlayIdSafe = _ref3.setOverlayIdSafe;
187
+ var _e = [new global.Error(), -3, -27];
188
+ var AnimatedFlashListTsx2 = function AnimatedFlashListTsx2(current, prev) {
189
+ if (current === prev) return;
190
+ scheduleOnRN(setOverlayIdSafe, current || null);
191
+ };
192
+ AnimatedFlashListTsx2.__closure = {
193
+ scheduleOnRN: scheduleOnRN,
194
+ setOverlayIdSafe: setOverlayIdSafe
195
+ };
196
+ AnimatedFlashListTsx2.__workletHash = 15313668423224;
197
+ AnimatedFlashListTsx2.__pluginVersion = "0.7.2";
198
+ AnimatedFlashListTsx2.__initData = _worklet_15313668423224_init_data;
199
+ AnimatedFlashListTsx2.__stackDetails = _e;
200
+ return AnimatedFlashListTsx2;
201
+ }({
202
+ _worklet_15313668423224_init_data: _worklet_15313668423224_init_data,
203
+ scheduleOnRN: _reactNativeWorklets.scheduleOnRN,
204
+ setOverlayIdSafe: setOverlayIdSafe
205
+ }), [setOverlayIdSafe]);
206
+
207
+ // Track frozen ID for rendering during fade-out
208
+ (0, _reactNativeReanimated.useAnimatedReaction)(function AnimatedFlashListTsx3Factory(_ref4) {
209
+ var _worklet_17236566315019_init_data = _ref4._worklet_17236566315019_init_data,
210
+ overlayFrozenItemId = _ref4.overlayFrozenItemId;
211
+ var _e = [new global.Error(), -2, -27];
212
+ var AnimatedFlashListTsx3 = function AnimatedFlashListTsx3() {
213
+ return overlayFrozenItemId.value;
214
+ };
215
+ AnimatedFlashListTsx3.__closure = {
216
+ overlayFrozenItemId: overlayFrozenItemId
217
+ };
218
+ AnimatedFlashListTsx3.__workletHash = 17236566315019;
219
+ AnimatedFlashListTsx3.__pluginVersion = "0.7.2";
220
+ AnimatedFlashListTsx3.__initData = _worklet_17236566315019_init_data;
221
+ AnimatedFlashListTsx3.__stackDetails = _e;
222
+ return AnimatedFlashListTsx3;
223
+ }({
224
+ _worklet_17236566315019_init_data: _worklet_17236566315019_init_data,
225
+ overlayFrozenItemId: overlayFrozenItemId
226
+ }), function AnimatedFlashListTsx4Factory(_ref5) {
227
+ var _worklet_12695437420222_init_data = _ref5._worklet_12695437420222_init_data,
228
+ scheduleOnRN = _ref5.scheduleOnRN,
229
+ setFrozenIdSafe = _ref5.setFrozenIdSafe;
230
+ var _e = [new global.Error(), -3, -27];
231
+ var AnimatedFlashListTsx4 = function AnimatedFlashListTsx4(current, prev) {
232
+ if (current === prev) return;
233
+ scheduleOnRN(setFrozenIdSafe, current || null);
234
+ };
235
+ AnimatedFlashListTsx4.__closure = {
236
+ scheduleOnRN: scheduleOnRN,
237
+ setFrozenIdSafe: setFrozenIdSafe
238
+ };
239
+ AnimatedFlashListTsx4.__workletHash = 12695437420222;
240
+ AnimatedFlashListTsx4.__pluginVersion = "0.7.2";
241
+ AnimatedFlashListTsx4.__initData = _worklet_12695437420222_init_data;
242
+ AnimatedFlashListTsx4.__stackDetails = _e;
243
+ return AnimatedFlashListTsx4;
244
+ }({
245
+ _worklet_12695437420222_init_data: _worklet_12695437420222_init_data,
246
+ scheduleOnRN: _reactNativeWorklets.scheduleOnRN,
247
+ setFrozenIdSafe: setFrozenIdSafe
248
+ }), [setFrozenIdSafe]);
249
+ (0, _react.useEffect)(function () {
250
+ overlayVisible.value = Boolean(overlayId);
251
+ }, [overlayId, overlayVisible]);
252
+
253
+ // Use frozenId to keep overlay rendered during fade-out
254
+ // Uses O(1) Map lookup instead of O(n) find()
255
+ var overlayItem = (0, _react.useMemo)(function () {
256
+ var _itemMap$get;
257
+ var id = frozenId || overlayId;
258
+ if (!id) return null;
259
+ return (_itemMap$get = itemMap.get(id)) !== null && _itemMap$get !== void 0 ? _itemMap$get : null;
260
+ }, [itemMap, frozenId, overlayId]);
261
+
262
+ // Create disabled gesture and false isDragging for overlay rendering
263
+ // This allows renderItem to show drag handle UI without functional gesture
264
+ var disabledGesture = (0, _react.useMemo)(function () {
265
+ return _reactNativeGestureHandler.Gesture.Pan().enabled(false);
266
+ }, []);
267
+ var falseIsDragging = (0, _reactNativeReanimated.useSharedValue)(false);
268
+ var overlayStyle = (0, _reactNativeReanimated.useAnimatedStyle)(function AnimatedFlashListTsx5Factory(_ref6) {
269
+ var _worklet_3465813210122_init_data = _ref6._worklet_3465813210122_init_data,
270
+ overlayFrozenItemId = _ref6.overlayFrozenItemId,
271
+ overlayItemId = _ref6.overlayItemId,
272
+ overlayReady = _ref6.overlayReady,
273
+ overlayVisible = _ref6.overlayVisible,
274
+ overlayRenderedOnUI = _ref6.overlayRenderedOnUI,
275
+ isDragging = _ref6.isDragging,
276
+ isDropping = _ref6.isDropping,
277
+ draggedScale = _ref6.draggedScale,
278
+ overlayAbsoluteY = _ref6.overlayAbsoluteY,
279
+ overlayTouchOffset = _ref6.overlayTouchOffset,
280
+ overlayContainerY = _ref6.overlayContainerY,
281
+ scrollOffset = _ref6.scrollOffset,
282
+ dragStartScrollOffset = _ref6.dragStartScrollOffset,
283
+ overlayBaseY = _ref6.overlayBaseY,
284
+ overlayTranslateY = _ref6.overlayTranslateY,
285
+ overlayBaseX = _ref6.overlayBaseX,
286
+ overlayWidth = _ref6.overlayWidth,
287
+ overlayHeight = _ref6.overlayHeight,
288
+ dragOverlayOpacity = _ref6.dragOverlayOpacity;
289
+ var _e = [new global.Error(), -20, -27];
290
+ var AnimatedFlashListTsx5 = function AnimatedFlashListTsx5() {
291
+ // Use frozenItemId so overlay stays renderable during fade-out
292
+ var itemId = overlayFrozenItemId.value || overlayItemId.value;
293
+
294
+ // Keep rendering if frozenItemId is set (crossfade in progress) even if overlayReady is false.
295
+ // This prevents the overlay from becoming invisible before the crossfade completes,
296
+ // which would cause a brief moment where both overlay AND original are invisible.
297
+ // FIXED: Also require overlayVisible to ensure JS-side overlayItem exists before fading.
298
+ // overlayVisible is only cleared AFTER fade-out completes (in useOverlayCrossfade),
299
+ // so this doesn't break fade-out.
300
+ if (!itemId || !overlayFrozenItemId.value && !overlayReady.value || !overlayVisible.value) {
301
+ // Mark overlay as NOT rendered when returning invisible style
302
+ overlayRenderedOnUI.value = false;
158
303
  return {
159
- position: "absolute",
160
- left: overlayBaseX.value,
161
- top,
162
- width: overlayWidth.value,
163
- height: overlayHeight.value,
164
- zIndex: 1000,
165
- transform: [{ scale }],
166
- opacity: dragOverlayOpacity.value, // Uses shared crossfade value
304
+ opacity: 0,
305
+ position: "absolute"
167
306
  };
168
- });
169
- // Only return null when BOTH overlayItem is null AND frozenId is empty
170
- // This keeps the Animated.View mounted during fade-out (while frozenId is set)
171
- if (!overlayItem && !frozenId)
172
- return null;
173
- const overlayIndex = overlayItem
174
- ? data.findIndex((item) => item.id === overlayItem.id)
175
- : -1;
176
- const totalItems = totalItemsRef.current ?? data.length;
177
- return (<react_native_reanimated_1.default.View pointerEvents="none" style={overlayStyle}>
178
- {overlayItem &&
179
- renderItem({
180
- item: overlayItem,
181
- index: overlayIndex,
182
- totalItems,
183
- animatedStyle: {},
184
- dragHandleProps: {
185
- gesture: disabledGesture,
186
- isDragging: falseIsDragging,
187
- },
188
- isDragging: false,
189
- isDragEnabled: false,
190
- triggerExitAnimation: () => { },
191
- resetExitAnimation: () => { },
192
- })}
193
- </react_native_reanimated_1.default.View>);
307
+ }
308
+
309
+ // Mark overlay as rendered on UI thread - this is the KEY fix for the vanish bug.
310
+ // Setting this here (in useAnimatedStyle, which runs on UI thread) instead of
311
+ // in useEffect (JS thread) ensures the original item knows the overlay has
312
+ // actually rendered BEFORE the crossfade starts. This prevents the race condition
313
+ // where the original starts fading out before the overlay appears.
314
+ overlayRenderedOnUI.value = true;
315
+
316
+ // Include isDropping so scale animates smoothly during drop phase
317
+ // Without this, scale jumps from 1.03 to 1.0 instantly when isDragging becomes false,
318
+ // causing a ~1-2px visual jump due to the scale transform's center pivot point.
319
+ var scale = isDragging.value || isDropping.value ? draggedScale.value : 1;
320
+
321
+ // During active drag, use absolute finger position for reliable overlay tracking.
322
+ // This avoids lag from JS-thread scroll offset updates during simultaneous scroll+drag.
323
+ // Formula: top = absoluteY - touchOffset - containerY
324
+ // Where:
325
+ // - absoluteY = finger's current screen Y position
326
+ // - touchOffset = how far down in the item the finger started
327
+ // - containerY = overlay container's screen Y position
328
+ //
329
+ // During drop animation (isDropping), switch back to slot-based positioning
330
+ // since the finger is no longer tracking and we need to animate to final slot.
331
+ var top;
332
+ if (isDragging.value) {
333
+ // Active drag: follow finger precisely
334
+ top = overlayAbsoluteY.value - overlayTouchOffset.value - overlayContainerY.value;
335
+ } else {
336
+ // Drop animation: use slot-based positioning with scroll compensation
337
+ // Note: scrollDelta is SUBTRACTED because overlayBaseY was measured at drag start
338
+ // (when scroll was dragStartScrollOffset). As user scrolls down (scrollDelta > 0),
339
+ // the target slot moves UP visually, so we subtract.
340
+ var scrollDelta = scrollOffset.value - dragStartScrollOffset.value;
341
+ top = overlayBaseY.value + overlayTranslateY.value - scrollDelta;
342
+ }
343
+ return {
344
+ position: "absolute",
345
+ left: overlayBaseX.value,
346
+ top: top,
347
+ width: overlayWidth.value,
348
+ height: overlayHeight.value,
349
+ zIndex: 1000,
350
+ transform: [{
351
+ scale: scale
352
+ }],
353
+ opacity: dragOverlayOpacity.value // Uses shared crossfade value
354
+ };
355
+ };
356
+ AnimatedFlashListTsx5.__closure = {
357
+ overlayFrozenItemId: overlayFrozenItemId,
358
+ overlayItemId: overlayItemId,
359
+ overlayReady: overlayReady,
360
+ overlayVisible: overlayVisible,
361
+ overlayRenderedOnUI: overlayRenderedOnUI,
362
+ isDragging: isDragging,
363
+ isDropping: isDropping,
364
+ draggedScale: draggedScale,
365
+ overlayAbsoluteY: overlayAbsoluteY,
366
+ overlayTouchOffset: overlayTouchOffset,
367
+ overlayContainerY: overlayContainerY,
368
+ scrollOffset: scrollOffset,
369
+ dragStartScrollOffset: dragStartScrollOffset,
370
+ overlayBaseY: overlayBaseY,
371
+ overlayTranslateY: overlayTranslateY,
372
+ overlayBaseX: overlayBaseX,
373
+ overlayWidth: overlayWidth,
374
+ overlayHeight: overlayHeight,
375
+ dragOverlayOpacity: dragOverlayOpacity
376
+ };
377
+ AnimatedFlashListTsx5.__workletHash = 3465813210122;
378
+ AnimatedFlashListTsx5.__pluginVersion = "0.7.2";
379
+ AnimatedFlashListTsx5.__initData = _worklet_3465813210122_init_data;
380
+ AnimatedFlashListTsx5.__stackDetails = _e;
381
+ return AnimatedFlashListTsx5;
382
+ }({
383
+ _worklet_3465813210122_init_data: _worklet_3465813210122_init_data,
384
+ overlayFrozenItemId: overlayFrozenItemId,
385
+ overlayItemId: overlayItemId,
386
+ overlayReady: overlayReady,
387
+ overlayVisible: overlayVisible,
388
+ overlayRenderedOnUI: overlayRenderedOnUI,
389
+ isDragging: isDragging,
390
+ isDropping: isDropping,
391
+ draggedScale: draggedScale,
392
+ overlayAbsoluteY: overlayAbsoluteY,
393
+ overlayTouchOffset: overlayTouchOffset,
394
+ overlayContainerY: overlayContainerY,
395
+ scrollOffset: scrollOffset,
396
+ dragStartScrollOffset: dragStartScrollOffset,
397
+ overlayBaseY: overlayBaseY,
398
+ overlayTranslateY: overlayTranslateY,
399
+ overlayBaseX: overlayBaseX,
400
+ overlayWidth: overlayWidth,
401
+ overlayHeight: overlayHeight,
402
+ dragOverlayOpacity: dragOverlayOpacity
403
+ }));
404
+
405
+ // Only return null when BOTH overlayItem is null AND frozenId is empty
406
+ // This keeps the Animated.View mounted during fade-out (while frozenId is set)
407
+ if (!overlayItem && !frozenId) return null;
408
+ var overlayIndex = overlayItem ? data.findIndex(function (item) {
409
+ return item.id === overlayItem.id;
410
+ }) : -1;
411
+ var totalItems = (_totalItemsRef$curren = totalItemsRef.current) !== null && _totalItemsRef$curren !== void 0 ? _totalItemsRef$curren : data.length;
412
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated["default"].View, {
413
+ pointerEvents: "none",
414
+ style: overlayStyle,
415
+ children: overlayItem && renderItem({
416
+ item: overlayItem,
417
+ index: overlayIndex,
418
+ totalItems: totalItems,
419
+ animatedStyle: {},
420
+ dragHandleProps: {
421
+ gesture: disabledGesture,
422
+ isDragging: falseIsDragging
423
+ },
424
+ isDragging: false,
425
+ isDragEnabled: false,
426
+ triggerExitAnimation: function triggerExitAnimation() {},
427
+ resetExitAnimation: function resetExitAnimation() {}
428
+ })
429
+ });
194
430
  });
195
- const ShiftedItemOverlays = react_1.default.memo(function ShiftedItemOverlays({ data, totalItemsRef, renderItem }) {
196
- const { shiftedOverlays, shiftedOverlaysActive, shiftedOverlaysRendered } = (0, DragStateContext_1.useDragState)();
197
- // State to hold overlay items on JS thread
198
- const [overlayItems, setOverlayItems] = (0, react_1.useState)([]);
199
- // O(1) lookup map for items by ID - stored in ref to break cascade dependency
200
- // This avoids O(n) data.find() for each overlay item during drop rendering
201
- const itemMapRef = (0, react_1.useRef)(new Map());
202
- (0, react_1.useEffect)(() => {
203
- itemMapRef.current = new Map(data.map(item => [item.id, item]));
204
- }, [data]);
205
- // Callback to update overlay items from JS thread
206
- // v40.1: Y position is now viewport-relative (captured with scroll offset subtracted)
207
- // Uses itemMapRef for O(1) lookups and empty deps to break cascade dependency
208
- const updateOverlayItems = (0, react_1.useCallback)((overlays, active) => {
209
- if (active && Object.keys(overlays).length > 0) {
210
- const itemMap = itemMapRef.current;
211
- const items = Object.entries(overlays)
212
- .map(([id, pos]) => ({
213
- item: itemMap.get(id), // O(1) lookup instead of O(n) find()
214
- position: { y: pos.baseY, height: pos.height },
215
- }))
216
- .filter((x) => x.item !== undefined);
217
- setOverlayItems(items);
218
- }
219
- else {
220
- setOverlayItems([]);
221
- }
222
- }, []);
223
- // Watch for overlay changes
224
- (0, react_native_reanimated_1.useAnimatedReaction)(() => ({
431
+
432
+ /**
433
+ * v40.1: Shifted item overlays - renders copies of shifted items at their visual positions
434
+ * during the drop transition to mask the FlashList repositioning glitch.
435
+ * Uses viewport-relative Y positions captured at drop start.
436
+ */
437
+ var _worklet_11522541576392_init_data = {
438
+ code: "function AnimatedFlashListTsx6(){const{shiftedOverlaysActive,shiftedOverlays}=this.__closure;return{active:shiftedOverlaysActive.value,overlays:shiftedOverlays.value};}",
439
+ location: "/Users/tani/Desktop/sous-chef-labs/reanimated-flashlist/src/AnimatedFlashList.tsx",
440
+ sourceMap: "{\"version\":3,\"names\":[\"AnimatedFlashListTsx6\",\"shiftedOverlaysActive\",\"shiftedOverlays\",\"__closure\",\"active\",\"value\",\"overlays\"],\"sources\":[\"/Users/tani/Desktop/sous-chef-labs/reanimated-flashlist/src/AnimatedFlashList.tsx\"],\"mappings\":\"AA+UI,SAAAA,sBAAA,QAAAC,qBAAA,CAAAC,eAAA,OAAAC,SAAA,OAAO,CACLC,MAAM,CAAEH,qBAAqB,CAACI,KAAK,CACnCC,QAAQ,CAAEJ,eAAe,CAACG,KAC5B,CAAC\",\"ignoreList\":[]}"
441
+ };
442
+ var _worklet_8730041342224_init_data = {
443
+ code: "function AnimatedFlashListTsx7(state,prev){const{scheduleOnRN,updateOverlayItems}=this.__closure;if(prev!==null&&state.active===prev.active&&Object.keys(state.overlays).length===Object.keys(prev.overlays).length){return;}scheduleOnRN(updateOverlayItems,state.overlays,state.active);}",
444
+ location: "/Users/tani/Desktop/sous-chef-labs/reanimated-flashlist/src/AnimatedFlashList.tsx",
445
+ sourceMap: "{\"version\":3,\"names\":[\"AnimatedFlashListTsx7\",\"state\",\"prev\",\"scheduleOnRN\",\"updateOverlayItems\",\"__closure\",\"active\",\"Object\",\"keys\",\"overlays\",\"length\"],\"sources\":[\"/Users/tani/Desktop/sous-chef-labs/reanimated-flashlist/src/AnimatedFlashList.tsx\"],\"mappings\":\"AAmVI,QAAC,CAAAA,qBAAgBA,CAAAC,KAAA,CAAAC,IAAA,QAAAC,YAAA,CAAAC,kBAAA,OAAAC,SAAA,CAGf,GACEH,IAAI,GAAK,IAAI,EACbD,KAAK,CAACK,MAAM,GAAKJ,IAAI,CAACI,MAAM,EAC5BC,MAAM,CAACC,IAAI,CAACP,KAAK,CAACQ,QAAQ,CAAC,CAACC,MAAM,GAAKH,MAAM,CAACC,IAAI,CAACN,IAAI,CAACO,QAAQ,CAAC,CAACC,MAAM,CACxE,CACA,OACF,CACAP,YAAY,CAACC,kBAAkB,CAAEH,KAAK,CAACQ,QAAQ,CAAER,KAAK,CAACK,MAAM,CAAC,CAChE\",\"ignoreList\":[]}"
446
+ };
447
+ var ShiftedItemOverlays = /*#__PURE__*/_react["default"].memo(function ShiftedItemOverlays(_ref7) {
448
+ var _totalItemsRef$curren2;
449
+ var data = _ref7.data,
450
+ totalItemsRef = _ref7.totalItemsRef,
451
+ renderItem = _ref7.renderItem;
452
+ var _useDragState2 = (0, _DragStateContext.useDragState)(),
453
+ shiftedOverlays = _useDragState2.shiftedOverlays,
454
+ shiftedOverlaysActive = _useDragState2.shiftedOverlaysActive,
455
+ shiftedOverlaysRendered = _useDragState2.shiftedOverlaysRendered;
456
+
457
+ // State to hold overlay items on JS thread
458
+ var _useState5 = (0, _react.useState)([]),
459
+ _useState6 = _slicedToArray(_useState5, 2),
460
+ overlayItems = _useState6[0],
461
+ setOverlayItems = _useState6[1];
462
+
463
+ // O(1) lookup map for items by ID - stored in ref to break cascade dependency
464
+ // This avoids O(n) data.find() for each overlay item during drop rendering
465
+ var itemMapRef = (0, _react.useRef)(new Map());
466
+ (0, _react.useEffect)(function () {
467
+ itemMapRef.current = new Map(data.map(function (item) {
468
+ return [item.id, item];
469
+ }));
470
+ }, [data]);
471
+
472
+ // Callback to update overlay items from JS thread
473
+ // v40.1: Y position is now viewport-relative (captured with scroll offset subtracted)
474
+ // Uses itemMapRef for O(1) lookups and empty deps to break cascade dependency
475
+ var updateOverlayItems = (0, _react.useCallback)(function (overlays, active) {
476
+ if (active && Object.keys(overlays).length > 0) {
477
+ var itemMap = itemMapRef.current;
478
+ var items = Object.entries(overlays).map(function (_ref8) {
479
+ var _ref9 = _slicedToArray(_ref8, 2),
480
+ id = _ref9[0],
481
+ pos = _ref9[1];
482
+ return {
483
+ item: itemMap.get(id),
484
+ // O(1) lookup instead of O(n) find()
485
+ position: {
486
+ y: pos.baseY,
487
+ height: pos.height
488
+ }
489
+ };
490
+ }).filter(function (x) {
491
+ return x.item !== undefined;
492
+ });
493
+ setOverlayItems(items);
494
+ } else {
495
+ setOverlayItems([]);
496
+ }
497
+ }, [] // Empty deps - uses ref to break cascade
498
+ );
499
+
500
+ // Watch for overlay changes
501
+ (0, _reactNativeReanimated.useAnimatedReaction)(function AnimatedFlashListTsx6Factory(_ref0) {
502
+ var _worklet_11522541576392_init_data = _ref0._worklet_11522541576392_init_data,
503
+ shiftedOverlaysActive = _ref0.shiftedOverlaysActive,
504
+ shiftedOverlays = _ref0.shiftedOverlays;
505
+ var _e = [new global.Error(), -3, -27];
506
+ var AnimatedFlashListTsx6 = function AnimatedFlashListTsx6() {
507
+ return {
225
508
  active: shiftedOverlaysActive.value,
226
- overlays: shiftedOverlays.value,
227
- }), (state, prev) => {
228
- "worklet";
229
- // Only update when state changes
230
- if (prev !== null &&
231
- state.active === prev.active &&
232
- Object.keys(state.overlays).length === Object.keys(prev.overlays).length) {
233
- return;
234
- }
235
- (0, react_native_worklets_1.scheduleOnRN)(updateOverlayItems, state.overlays, state.active);
236
- }, [updateOverlayItems]);
237
- // v41.6: Use useLayoutEffect for earlier signal (runs after commit, before paint)
238
- // This reduces the timing gap between overlay render and originals being hidden
239
- (0, react_1.useLayoutEffect)(() => {
240
- shiftedOverlaysRendered.value = overlayItems.length > 0;
241
- }, [overlayItems.length, shiftedOverlaysRendered]);
242
- // Create disabled gesture and false isDragging for overlay rendering
243
- const disabledGesture = (0, react_1.useMemo)(() => react_native_gesture_handler_1.Gesture.Pan().enabled(false), []);
244
- const falseIsDragging = (0, react_native_reanimated_1.useSharedValue)(false);
245
- if (overlayItems.length === 0)
246
- return null;
247
- const totalItems = totalItemsRef.current ?? data.length;
248
- return (<>
249
- {overlayItems.map(({ item, position }) => (<react_native_1.View key={`shifted-overlay-${item.id}`} style={{
250
- position: "absolute",
251
- top: position.y,
252
- left: 0,
253
- right: 0,
254
- height: position.height,
255
- zIndex: 999,
256
- }} pointerEvents="none">
257
- {renderItem({
258
- item,
259
- index: data.findIndex((d) => d.id === item.id),
260
- totalItems,
261
- animatedStyle: {},
262
- dragHandleProps: {
263
- gesture: disabledGesture,
264
- isDragging: falseIsDragging,
265
- },
266
- isDragging: false,
267
- isDragEnabled: false,
268
- triggerExitAnimation: () => { },
269
- resetExitAnimation: () => { },
270
- })}
271
- </react_native_1.View>))}
272
- </>);
509
+ overlays: shiftedOverlays.value
510
+ };
511
+ };
512
+ AnimatedFlashListTsx6.__closure = {
513
+ shiftedOverlaysActive: shiftedOverlaysActive,
514
+ shiftedOverlays: shiftedOverlays
515
+ };
516
+ AnimatedFlashListTsx6.__workletHash = 11522541576392;
517
+ AnimatedFlashListTsx6.__pluginVersion = "0.7.2";
518
+ AnimatedFlashListTsx6.__initData = _worklet_11522541576392_init_data;
519
+ AnimatedFlashListTsx6.__stackDetails = _e;
520
+ return AnimatedFlashListTsx6;
521
+ }({
522
+ _worklet_11522541576392_init_data: _worklet_11522541576392_init_data,
523
+ shiftedOverlaysActive: shiftedOverlaysActive,
524
+ shiftedOverlays: shiftedOverlays
525
+ }), function AnimatedFlashListTsx7Factory(_ref1) {
526
+ var _worklet_8730041342224_init_data = _ref1._worklet_8730041342224_init_data,
527
+ scheduleOnRN = _ref1.scheduleOnRN,
528
+ updateOverlayItems = _ref1.updateOverlayItems;
529
+ var _e = [new global.Error(), -3, -27];
530
+ var AnimatedFlashListTsx7 = function AnimatedFlashListTsx7(state, prev) {
531
+ // Only update when state changes
532
+ if (prev !== null && state.active === prev.active && Object.keys(state.overlays).length === Object.keys(prev.overlays).length) {
533
+ return;
534
+ }
535
+ scheduleOnRN(updateOverlayItems, state.overlays, state.active);
536
+ };
537
+ AnimatedFlashListTsx7.__closure = {
538
+ scheduleOnRN: scheduleOnRN,
539
+ updateOverlayItems: updateOverlayItems
540
+ };
541
+ AnimatedFlashListTsx7.__workletHash = 8730041342224;
542
+ AnimatedFlashListTsx7.__pluginVersion = "0.7.2";
543
+ AnimatedFlashListTsx7.__initData = _worklet_8730041342224_init_data;
544
+ AnimatedFlashListTsx7.__stackDetails = _e;
545
+ return AnimatedFlashListTsx7;
546
+ }({
547
+ _worklet_8730041342224_init_data: _worklet_8730041342224_init_data,
548
+ scheduleOnRN: _reactNativeWorklets.scheduleOnRN,
549
+ updateOverlayItems: updateOverlayItems
550
+ }), [updateOverlayItems]);
551
+
552
+ // v41.6: Use useLayoutEffect for earlier signal (runs after commit, before paint)
553
+ // This reduces the timing gap between overlay render and originals being hidden
554
+ (0, _react.useLayoutEffect)(function () {
555
+ shiftedOverlaysRendered.value = overlayItems.length > 0;
556
+ }, [overlayItems.length, shiftedOverlaysRendered]);
557
+
558
+ // Create disabled gesture and false isDragging for overlay rendering
559
+ var disabledGesture = (0, _react.useMemo)(function () {
560
+ return _reactNativeGestureHandler.Gesture.Pan().enabled(false);
561
+ }, []);
562
+ var falseIsDragging = (0, _reactNativeReanimated.useSharedValue)(false);
563
+ if (overlayItems.length === 0) return null;
564
+ var totalItems = (_totalItemsRef$curren2 = totalItemsRef.current) !== null && _totalItemsRef$curren2 !== void 0 ? _totalItemsRef$curren2 : data.length;
565
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
566
+ children: overlayItems.map(function (_ref10) {
567
+ var item = _ref10.item,
568
+ position = _ref10.position;
569
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
570
+ style: {
571
+ position: "absolute",
572
+ top: position.y,
573
+ left: 0,
574
+ right: 0,
575
+ height: position.height,
576
+ zIndex: 999
577
+ },
578
+ pointerEvents: "none",
579
+ children: renderItem({
580
+ item: item,
581
+ index: data.findIndex(function (d) {
582
+ return d.id === item.id;
583
+ }),
584
+ totalItems: totalItems,
585
+ animatedStyle: {},
586
+ dragHandleProps: {
587
+ gesture: disabledGesture,
588
+ isDragging: falseIsDragging
589
+ },
590
+ isDragging: false,
591
+ isDragEnabled: false,
592
+ triggerExitAnimation: function triggerExitAnimation() {},
593
+ resetExitAnimation: function resetExitAnimation() {}
594
+ })
595
+ }, "shifted-overlay-".concat(item.id));
596
+ })
597
+ });
273
598
  });
274
- const DragContainer = react_1.default.memo(function DragContainer({ children, }) {
275
- const { overlayContainerX, overlayContainerY, overlayContainerYLayout, overlayContainerReady, dragContainerRef, } = (0, DragStateContext_1.useDragState)();
276
- const handleLayout = (0, react_1.useCallback)(() => {
277
- const node = dragContainerRef.current;
278
- if (!node)
279
- return;
280
- node.measureInWindow((x, y) => {
281
- overlayContainerX.value = x;
282
- overlayContainerY.value = y;
283
- overlayContainerYLayout.value = y; // Keep measureInWindow value for shifted overlay calculations
284
- overlayContainerReady.value = true;
285
- });
286
- }, [
287
- overlayContainerX,
288
- overlayContainerY,
289
- overlayContainerYLayout,
290
- overlayContainerReady,
291
- dragContainerRef,
292
- ]);
293
- return (<react_native_reanimated_1.default.View ref={dragContainerRef} style={containerStyle} onLayout={handleLayout}>
294
- {children}
295
- </react_native_reanimated_1.default.View>);
599
+ var DragContainer = /*#__PURE__*/_react["default"].memo(function DragContainer(_ref11) {
600
+ var children = _ref11.children;
601
+ var _useDragState3 = (0, _DragStateContext.useDragState)(),
602
+ overlayContainerX = _useDragState3.overlayContainerX,
603
+ overlayContainerY = _useDragState3.overlayContainerY,
604
+ overlayContainerYLayout = _useDragState3.overlayContainerYLayout,
605
+ overlayContainerReady = _useDragState3.overlayContainerReady,
606
+ dragContainerRef = _useDragState3.dragContainerRef;
607
+ var handleLayout = (0, _react.useCallback)(function () {
608
+ var node = dragContainerRef.current;
609
+ if (!node) return;
610
+ node.measureInWindow(function (x, y) {
611
+ overlayContainerX.value = x;
612
+ overlayContainerY.value = y;
613
+ overlayContainerYLayout.value = y; // Keep measureInWindow value for shifted overlay calculations
614
+ overlayContainerReady.value = true;
615
+ });
616
+ }, [overlayContainerX, overlayContainerY, overlayContainerYLayout, overlayContainerReady, dragContainerRef]);
617
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated["default"].View, {
618
+ ref: dragContainerRef,
619
+ style: containerStyle,
620
+ onLayout: handleLayout,
621
+ children: children
622
+ });
296
623
  });
297
- const ItemWrapper = react_1.default.memo(function ItemWrapper({ item, index, totalItemsRef, renderItem, canDrag, dragEnabled, onReorderByDelta, onHapticFeedback, }) {
298
- const isDragEnabled = dragEnabled && (canDrag ? canDrag(item, index) : true);
299
- return (<AnimatedFlashListItem_1.AnimatedFlashListItem item={item} index={index} totalItems={totalItemsRef.current ?? 0} isDragEnabled={isDragEnabled} renderItem={renderItem} onReorderByDelta={onReorderByDelta} onHapticFeedback={onHapticFeedback}/>);
624
+
625
+ /**
626
+ * Item wrapper component for FlashList
627
+ * Uses ref for totalItems to avoid renderItem callback recreation
628
+ */
629
+
630
+ var ItemWrapper = /*#__PURE__*/_react["default"].memo(function ItemWrapper(_ref12) {
631
+ var _totalItemsRef$curren3;
632
+ var item = _ref12.item,
633
+ index = _ref12.index,
634
+ totalItemsRef = _ref12.totalItemsRef,
635
+ renderItem = _ref12.renderItem,
636
+ canDrag = _ref12.canDrag,
637
+ dragEnabled = _ref12.dragEnabled,
638
+ onReorderByDelta = _ref12.onReorderByDelta,
639
+ onHapticFeedback = _ref12.onHapticFeedback;
640
+ var isDragEnabled = dragEnabled && (canDrag ? canDrag(item, index) : true);
641
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_AnimatedFlashListItem.AnimatedFlashListItem, {
642
+ item: item,
643
+ index: index,
644
+ totalItems: (_totalItemsRef$curren3 = totalItemsRef.current) !== null && _totalItemsRef$curren3 !== void 0 ? _totalItemsRef$curren3 : 0,
645
+ isDragEnabled: isDragEnabled,
646
+ renderItem: renderItem,
647
+ onReorderByDelta: onReorderByDelta,
648
+ onHapticFeedback: onHapticFeedback
649
+ });
300
650
  });
301
- function InnerFlashList({ data, totalItemsRef, flashListRef, renderItem, keyExtractor, canDrag, dragEnabled, onReorderByDelta, onHapticFeedback, itemHeight, ListFooterComponent, ListEmptyComponent, onEndReached, onEndReachedThreshold, onRefresh, refreshing, refreshTintColor, contentContainerStyle, drawDistance = 500, showsVerticalScrollIndicator = true, extraData, onCommitLayoutEffect, }) {
302
- // Get drag state context for scroll tracking
303
- const { scrollOffset, contentHeight, visibleHeight, listTopY, listLeftX, setListRef, totalItems, notifyLayoutCommit, isDragging, isDropping, itemIndexRegistry, } = (0, DragStateContext_1.useDragState)();
304
- // v40.1: Disable scroll while dropping to prevent overlay drift
305
- // FlashList needs scrollEnabled as a regular boolean, not a SharedValue
306
- const [scrollEnabled, setScrollEnabled] = (0, react_1.useState)(true);
307
- const setScrollEnabledSafe = (0, react_1.useCallback)((enabled) => {
308
- setScrollEnabled(enabled);
309
- }, []);
310
- (0, react_native_reanimated_1.useAnimatedReaction)(() => isDragging.value || isDropping.value, (draggingOrDropping, prevDraggingOrDropping) => {
311
- "worklet";
312
- if (draggingOrDropping !== prevDraggingOrDropping) {
313
- (0, react_native_worklets_1.scheduleOnRN)(setScrollEnabledSafe, !draggingOrDropping);
314
- }
315
- }, [setScrollEnabledSafe]);
316
- // Callback ref that notifies DragStateContext when FlashList is available.
317
- // This fires immediately when FlashList sets the ref, unlike useEffect which
318
- // would run after render with flashListRef.current potentially still null.
319
- const handleFlashListRef = (0, react_1.useCallback)((instance) => {
320
- // Update the ref object for other uses (scrollToOffset, prepareForLayoutAnimationRender, etc.)
321
- flashListRef.current =
322
- instance;
323
- // Notify DragStateContext - this triggers native layout observation setup
324
- setListRef(instance);
325
- }, [flashListRef, setListRef]);
326
- // Sync totalItems SharedValue with data.length for worklet access
327
- (0, react_1.useEffect)(() => {
328
- totalItems.value = data.length;
329
- }, [data.length, totalItems]);
330
- // Prune stale entries from itemIndexRegistry when data changes.
331
- // Skip during active drag/drop to avoid corrupting indices mid-operation.
332
- (0, react_1.useEffect)(() => {
333
- if (isDragging.value || isDropping.value)
334
- return;
335
- const currentIds = new Set(data.map((item) => item.id));
336
- const registry = itemIndexRegistry.value;
337
- const staleIds = Object.keys(registry).filter((id) => !currentIds.has(id));
338
- if (staleIds.length > 0) {
339
- const pruned = { ...registry };
340
- for (const id of staleIds) {
341
- delete pruned[id];
342
- }
343
- itemIndexRegistry.value = pruned;
344
- }
345
- }, [data, itemIndexRegistry, isDragging, isDropping]);
346
- // Update scroll offset on scroll
347
- const handleScroll = (0, react_1.useCallback)((event) => {
348
- scrollOffset.value = event.nativeEvent.contentOffset.y;
349
- }, [scrollOffset]);
350
- // Update content height when list content changes
351
- const handleContentSizeChange = (0, react_1.useCallback)((_width, height) => {
352
- contentHeight.value = height;
353
- }, [contentHeight]);
354
- // Update visible height and list position when layout changes
355
- const handleLayout = (0, react_1.useCallback)((event) => {
356
- visibleHeight.value = event.nativeEvent.layout.height;
357
- // Get native scroll ref and validate it has measureInWindow before using
358
- const nativeRef = flashListRef.current?.getNativeScrollRef?.();
359
- if (nativeRef &&
360
- typeof nativeRef === "object" &&
361
- "measureInWindow" in nativeRef &&
362
- typeof nativeRef.measureInWindow === "function") {
363
- nativeRef.measureInWindow((x, y) => {
364
- listTopY.value = y;
365
- listLeftX.value = x;
366
- });
651
+
652
+ /**
653
+ * Inner FlashList component that uses DragStateContext for scroll tracking
654
+ */
655
+ var _worklet_883533745161_init_data = {
656
+ code: "function AnimatedFlashListTsx8(){const{isDragging,isDropping}=this.__closure;return isDragging.value||isDropping.value;}",
657
+ location: "/Users/tani/Desktop/sous-chef-labs/reanimated-flashlist/src/AnimatedFlashList.tsx",
658
+ sourceMap: "{\"version\":3,\"names\":[\"AnimatedFlashListTsx8\",\"isDragging\",\"isDropping\",\"__closure\",\"value\"],\"sources\":[\"/Users/tani/Desktop/sous-chef-labs/reanimated-flashlist/src/AnimatedFlashList.tsx\"],\"mappings\":\"AAqjBI,SAAAA,sBAAA,QAAAC,UAAA,CAAAC,UAAA,OAAAC,SAAA,OAAM,CAAAF,UAAW,CAAAG,KAAK,EAAIF,UAAW,CAAAE,KAAA\",\"ignoreList\":[]}"
659
+ };
660
+ var _worklet_10826337305048_init_data = {
661
+ code: "function AnimatedFlashListTsx9(draggingOrDropping,prevDraggingOrDropping){const{scheduleOnRN,setScrollEnabledSafe}=this.__closure;if(draggingOrDropping!==prevDraggingOrDropping){scheduleOnRN(setScrollEnabledSafe,!draggingOrDropping);}}",
662
+ location: "/Users/tani/Desktop/sous-chef-labs/reanimated-flashlist/src/AnimatedFlashList.tsx",
663
+ sourceMap: "{\"version\":3,\"names\":[\"AnimatedFlashListTsx9\",\"draggingOrDropping\",\"prevDraggingOrDropping\",\"scheduleOnRN\",\"setScrollEnabledSafe\",\"__closure\"],\"sources\":[\"/Users/tani/Desktop/sous-chef-labs/reanimated-flashlist/src/AnimatedFlashList.tsx\"],\"mappings\":\"AAsjBI,QAAC,CAAAA,qBAAkBA,CAAEC,kBAAA,CAAAC,sBAA2B,QAAAC,YAAA,CAAAC,oBAAA,OAAAC,SAAA,CAE9C,GAAIJ,kBAAkB,GAAKC,sBAAsB,CAAE,CACjDC,YAAY,CAACC,oBAAoB,CAAE,CAACH,kBAAkB,CAAC,CACzD,CACF\",\"ignoreList\":[]}"
664
+ };
665
+ function InnerFlashList(_ref13) {
666
+ var data = _ref13.data,
667
+ totalItemsRef = _ref13.totalItemsRef,
668
+ flashListRef = _ref13.flashListRef,
669
+ renderItem = _ref13.renderItem,
670
+ keyExtractor = _ref13.keyExtractor,
671
+ canDrag = _ref13.canDrag,
672
+ dragEnabled = _ref13.dragEnabled,
673
+ onReorderByDelta = _ref13.onReorderByDelta,
674
+ onHapticFeedback = _ref13.onHapticFeedback,
675
+ itemHeight = _ref13.itemHeight,
676
+ ListFooterComponent = _ref13.ListFooterComponent,
677
+ ListEmptyComponent = _ref13.ListEmptyComponent,
678
+ onEndReached = _ref13.onEndReached,
679
+ onEndReachedThreshold = _ref13.onEndReachedThreshold,
680
+ onRefresh = _ref13.onRefresh,
681
+ refreshing = _ref13.refreshing,
682
+ refreshTintColor = _ref13.refreshTintColor,
683
+ contentContainerStyle = _ref13.contentContainerStyle,
684
+ _ref13$drawDistance = _ref13.drawDistance,
685
+ drawDistance = _ref13$drawDistance === void 0 ? 500 : _ref13$drawDistance,
686
+ _ref13$showsVerticalS = _ref13.showsVerticalScrollIndicator,
687
+ showsVerticalScrollIndicator = _ref13$showsVerticalS === void 0 ? true : _ref13$showsVerticalS,
688
+ extraData = _ref13.extraData,
689
+ onCommitLayoutEffect = _ref13.onCommitLayoutEffect;
690
+ // Get drag state context for scroll tracking
691
+ var _useDragState4 = (0, _DragStateContext.useDragState)(),
692
+ scrollOffset = _useDragState4.scrollOffset,
693
+ contentHeight = _useDragState4.contentHeight,
694
+ visibleHeight = _useDragState4.visibleHeight,
695
+ listTopY = _useDragState4.listTopY,
696
+ listLeftX = _useDragState4.listLeftX,
697
+ setListRef = _useDragState4.setListRef,
698
+ totalItems = _useDragState4.totalItems,
699
+ notifyLayoutCommit = _useDragState4.notifyLayoutCommit,
700
+ isDragging = _useDragState4.isDragging,
701
+ isDropping = _useDragState4.isDropping,
702
+ itemIndexRegistry = _useDragState4.itemIndexRegistry;
703
+
704
+ // v40.1: Disable scroll while dropping to prevent overlay drift
705
+ // FlashList needs scrollEnabled as a regular boolean, not a SharedValue
706
+ var _useState7 = (0, _react.useState)(true),
707
+ _useState8 = _slicedToArray(_useState7, 2),
708
+ scrollEnabled = _useState8[0],
709
+ setScrollEnabled = _useState8[1];
710
+ var setScrollEnabledSafe = (0, _react.useCallback)(function (enabled) {
711
+ setScrollEnabled(enabled);
712
+ }, []);
713
+ (0, _reactNativeReanimated.useAnimatedReaction)(function AnimatedFlashListTsx8Factory(_ref14) {
714
+ var _worklet_883533745161_init_data = _ref14._worklet_883533745161_init_data,
715
+ isDragging = _ref14.isDragging,
716
+ isDropping = _ref14.isDropping;
717
+ var _e = [new global.Error(), -3, -27];
718
+ var AnimatedFlashListTsx8 = function AnimatedFlashListTsx8() {
719
+ return isDragging.value || isDropping.value;
720
+ };
721
+ AnimatedFlashListTsx8.__closure = {
722
+ isDragging: isDragging,
723
+ isDropping: isDropping
724
+ };
725
+ AnimatedFlashListTsx8.__workletHash = 883533745161;
726
+ AnimatedFlashListTsx8.__pluginVersion = "0.7.2";
727
+ AnimatedFlashListTsx8.__initData = _worklet_883533745161_init_data;
728
+ AnimatedFlashListTsx8.__stackDetails = _e;
729
+ return AnimatedFlashListTsx8;
730
+ }({
731
+ _worklet_883533745161_init_data: _worklet_883533745161_init_data,
732
+ isDragging: isDragging,
733
+ isDropping: isDropping
734
+ }), function AnimatedFlashListTsx9Factory(_ref15) {
735
+ var _worklet_10826337305048_init_data = _ref15._worklet_10826337305048_init_data,
736
+ scheduleOnRN = _ref15.scheduleOnRN,
737
+ setScrollEnabledSafe = _ref15.setScrollEnabledSafe;
738
+ var _e = [new global.Error(), -3, -27];
739
+ var AnimatedFlashListTsx9 = function AnimatedFlashListTsx9(draggingOrDropping, prevDraggingOrDropping) {
740
+ if (draggingOrDropping !== prevDraggingOrDropping) {
741
+ scheduleOnRN(setScrollEnabledSafe, !draggingOrDropping);
742
+ }
743
+ };
744
+ AnimatedFlashListTsx9.__closure = {
745
+ scheduleOnRN: scheduleOnRN,
746
+ setScrollEnabledSafe: setScrollEnabledSafe
747
+ };
748
+ AnimatedFlashListTsx9.__workletHash = 10826337305048;
749
+ AnimatedFlashListTsx9.__pluginVersion = "0.7.2";
750
+ AnimatedFlashListTsx9.__initData = _worklet_10826337305048_init_data;
751
+ AnimatedFlashListTsx9.__stackDetails = _e;
752
+ return AnimatedFlashListTsx9;
753
+ }({
754
+ _worklet_10826337305048_init_data: _worklet_10826337305048_init_data,
755
+ scheduleOnRN: _reactNativeWorklets.scheduleOnRN,
756
+ setScrollEnabledSafe: setScrollEnabledSafe
757
+ }), [setScrollEnabledSafe]);
758
+
759
+ // Callback ref that notifies DragStateContext when FlashList is available.
760
+ // This fires immediately when FlashList sets the ref, unlike useEffect which
761
+ // would run after render with flashListRef.current potentially still null.
762
+ var handleFlashListRef = (0, _react.useCallback)(function (instance) {
763
+ // Update the ref object for other uses (scrollToOffset, prepareForLayoutAnimationRender, etc.)
764
+ flashListRef.current = instance;
765
+ // Notify DragStateContext - this triggers native layout observation setup
766
+ setListRef(instance);
767
+ }, [flashListRef, setListRef]);
768
+
769
+ // Sync totalItems SharedValue with data.length for worklet access
770
+ (0, _react.useEffect)(function () {
771
+ totalItems.value = data.length;
772
+ }, [data.length, totalItems]);
773
+
774
+ // Prune stale entries from itemIndexRegistry when data changes.
775
+ // Skip during active drag/drop to avoid corrupting indices mid-operation.
776
+ (0, _react.useEffect)(function () {
777
+ if (isDragging.value || isDropping.value) return;
778
+ var currentIds = new Set(data.map(function (item) {
779
+ return item.id;
780
+ }));
781
+ var registry = itemIndexRegistry.value;
782
+ var staleIds = Object.keys(registry).filter(function (id) {
783
+ return !currentIds.has(id);
784
+ });
785
+ if (staleIds.length > 0) {
786
+ var pruned = _objectSpread({}, registry);
787
+ var _iterator = _createForOfIteratorHelper(staleIds),
788
+ _step;
789
+ try {
790
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
791
+ var id = _step.value;
792
+ delete pruned[id];
367
793
  }
368
- }, [visibleHeight, listTopY, listLeftX, flashListRef]);
369
- const handleCommitLayoutEffect = (0, react_1.useCallback)(() => {
370
- notifyLayoutCommit();
371
- onCommitLayoutEffect?.();
372
- }, [notifyLayoutCommit, onCommitLayoutEffect]);
373
- // Render item for FlashList
374
- const flashListRenderItem = (0, react_1.useCallback)(({ item, index }) => (<ItemWrapper item={item} index={index} totalItemsRef={totalItemsRef} renderItem={renderItem} canDrag={canDrag} dragEnabled={dragEnabled} onReorderByDelta={onReorderByDelta} onHapticFeedback={onHapticFeedback}/>), [
375
- totalItemsRef,
376
- renderItem,
377
- canDrag,
378
- dragEnabled,
379
- onReorderByDelta,
380
- onHapticFeedback,
381
- ]);
382
- // getItemType for FlashList recycling optimization
383
- const getItemType = (0, react_1.useCallback)(() => "animated-item", []);
384
- // Override item layout for consistent drag calculations
385
- // Note: We cast the layout to include size for drag calculations
386
- const overrideItemLayout = (0, react_1.useCallback)((layout, _item, _index) => {
387
- // FlashList v2 uses this for span, but we extend for size in drag calculations
388
- layout.size = itemHeight;
389
- }, [itemHeight]);
390
- return (<flash_list_1.FlashList ref={handleFlashListRef} data={data} renderItem={flashListRenderItem} keyExtractor={keyExtractor} getItemType={getItemType} overrideItemLayout={overrideItemLayout} onScroll={handleScroll} onContentSizeChange={handleContentSizeChange} onLayout={handleLayout} scrollEventThrottle={16} drawDistance={drawDistance} maintainVisibleContentPosition={{ disabled: true }} showsVerticalScrollIndicator={showsVerticalScrollIndicator} onCommitLayoutEffect={handleCommitLayoutEffect} extraData={extraData} contentContainerStyle={contentContainerStyle} ListFooterComponent={ListFooterComponent ?? undefined} ListEmptyComponent={ListEmptyComponent ?? undefined} onEndReached={onEndReached} onEndReachedThreshold={onEndReachedThreshold} scrollEnabled={scrollEnabled} refreshControl={onRefresh ? (<react_native_1.RefreshControl refreshing={refreshing ?? false} onRefresh={onRefresh} tintColor={refreshTintColor} colors={refreshTintColor ? [refreshTintColor] : undefined}/>) : undefined}/>);
794
+ } catch (err) {
795
+ _iterator.e(err);
796
+ } finally {
797
+ _iterator.f();
798
+ }
799
+ itemIndexRegistry.value = pruned;
800
+ }
801
+ }, [data, itemIndexRegistry, isDragging, isDropping]);
802
+
803
+ // Update scroll offset on scroll
804
+ var handleScroll = (0, _react.useCallback)(function (event) {
805
+ scrollOffset.value = event.nativeEvent.contentOffset.y;
806
+ }, [scrollOffset]);
807
+
808
+ // Update content height when list content changes
809
+ var handleContentSizeChange = (0, _react.useCallback)(function (_width, height) {
810
+ contentHeight.value = height;
811
+ }, [contentHeight]);
812
+
813
+ // Update visible height and list position when layout changes
814
+ var handleLayout = (0, _react.useCallback)(function (event) {
815
+ var _flashListRef$current, _flashListRef$current2;
816
+ visibleHeight.value = event.nativeEvent.layout.height;
817
+ // Get native scroll ref and validate it has measureInWindow before using
818
+ var nativeRef = (_flashListRef$current = flashListRef.current) === null || _flashListRef$current === void 0 || (_flashListRef$current2 = _flashListRef$current.getNativeScrollRef) === null || _flashListRef$current2 === void 0 ? void 0 : _flashListRef$current2.call(_flashListRef$current);
819
+ if (nativeRef && _typeof(nativeRef) === "object" && "measureInWindow" in nativeRef && typeof nativeRef.measureInWindow === "function") {
820
+ nativeRef.measureInWindow(function (x, y) {
821
+ listTopY.value = y;
822
+ listLeftX.value = x;
823
+ });
824
+ }
825
+ }, [visibleHeight, listTopY, listLeftX, flashListRef]);
826
+ var handleCommitLayoutEffect = (0, _react.useCallback)(function () {
827
+ notifyLayoutCommit();
828
+ onCommitLayoutEffect === null || onCommitLayoutEffect === void 0 || onCommitLayoutEffect();
829
+ }, [notifyLayoutCommit, onCommitLayoutEffect]);
830
+
831
+ // Render item for FlashList
832
+ var flashListRenderItem = (0, _react.useCallback)(function (_ref16) {
833
+ var item = _ref16.item,
834
+ index = _ref16.index;
835
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(ItemWrapper, {
836
+ item: item,
837
+ index: index,
838
+ totalItemsRef: totalItemsRef,
839
+ renderItem: renderItem,
840
+ canDrag: canDrag,
841
+ dragEnabled: dragEnabled,
842
+ onReorderByDelta: onReorderByDelta,
843
+ onHapticFeedback: onHapticFeedback
844
+ });
845
+ }, [totalItemsRef, renderItem, canDrag, dragEnabled, onReorderByDelta, onHapticFeedback]);
846
+
847
+ // getItemType for FlashList recycling optimization
848
+ var getItemType = (0, _react.useCallback)(function () {
849
+ return "animated-item";
850
+ }, []);
851
+
852
+ // Override item layout for consistent drag calculations
853
+ // Note: We cast the layout to include size for drag calculations
854
+ var overrideItemLayout = (0, _react.useCallback)(function (layout, _item, _index) {
855
+ // FlashList v2 uses this for span, but we extend for size in drag calculations
856
+ layout.size = itemHeight;
857
+ }, [itemHeight]);
858
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_flashList.FlashList, {
859
+ ref: handleFlashListRef,
860
+ data: data,
861
+ renderItem: flashListRenderItem,
862
+ keyExtractor: keyExtractor,
863
+ getItemType: getItemType,
864
+ overrideItemLayout: overrideItemLayout,
865
+ onScroll: handleScroll,
866
+ onContentSizeChange: handleContentSizeChange,
867
+ onLayout: handleLayout,
868
+ scrollEventThrottle: 16,
869
+ drawDistance: drawDistance,
870
+ maintainVisibleContentPosition: {
871
+ disabled: true
872
+ },
873
+ showsVerticalScrollIndicator: showsVerticalScrollIndicator,
874
+ onCommitLayoutEffect: handleCommitLayoutEffect,
875
+ extraData: extraData,
876
+ contentContainerStyle: contentContainerStyle,
877
+ ListFooterComponent: ListFooterComponent !== null && ListFooterComponent !== void 0 ? ListFooterComponent : undefined,
878
+ ListEmptyComponent: ListEmptyComponent !== null && ListEmptyComponent !== void 0 ? ListEmptyComponent : undefined,
879
+ onEndReached: onEndReached,
880
+ onEndReachedThreshold: onEndReachedThreshold,
881
+ scrollEnabled: scrollEnabled,
882
+ refreshControl: onRefresh ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.RefreshControl, {
883
+ refreshing: refreshing !== null && refreshing !== void 0 ? refreshing : false,
884
+ onRefresh: onRefresh,
885
+ tintColor: refreshTintColor,
886
+ colors: refreshTintColor ? [refreshTintColor] : undefined
887
+ }) : undefined
888
+ });
391
889
  }
890
+
392
891
  /**
393
892
  * AnimatedFlashList - High-performance animated list with drag-to-reorder
394
893
  *
@@ -419,129 +918,209 @@ function InnerFlashList({ data, totalItemsRef, flashListRef, renderItem, keyExtr
419
918
  * ```
420
919
  */
421
920
  function AnimatedFlashListInner(props, ref) {
422
- const { data, keyExtractor, renderItem, dragEnabled = false, onReorder, onReorderByNeighbors, canDrag, onHapticFeedback, config, onPrepareLayoutAnimation, ListFooterComponent, ListEmptyComponent, onRefresh, refreshing = false, onEndReached, onEndReachedThreshold = 0.5, contentContainerStyle, ...flashListProps } = props;
423
- const { extraData: userExtraData, onCommitLayoutEffect: userOnCommitLayoutEffect, ...restFlashListProps } = flashListProps;
424
- // Merge config with defaults
425
- // Note: estimatedItemSize was removed in FlashList v2
426
- // Users should configure itemHeight via config.drag.itemHeight
427
- const dragConfig = (0, react_1.useMemo)(() => ({
428
- ...drag_1.DEFAULT_DRAG_CONFIG,
429
- ...config?.drag,
430
- }), [config?.drag]);
431
- // Ref to FlashList
432
- const flashListRef = (0, react_1.useRef)(null);
433
- // Expose methods to parent via ref
434
- (0, react_1.useImperativeHandle)(ref, () => ({
435
- prepareForLayoutAnimation: () => {
436
- flashListRef.current?.prepareForLayoutAnimationRender();
437
- onPrepareLayoutAnimation?.();
438
- },
439
- scrollToOffset: (offset, animated = true) => {
440
- flashListRef.current?.scrollToOffset({ offset, animated });
441
- },
442
- scrollToIndex: (index, animated = true) => {
443
- flashListRef.current?.scrollToIndex({ index, animated });
444
- },
445
- }));
446
- // Keep valid items in ref for reorder callback
447
- const dataRef = (0, react_1.useRef)([]);
448
- dataRef.current = data;
449
- // Force FlashList to re-render visible cells after reorder to avoid
450
- // 1-frame gaps when internal recycling repositions without a React render.
451
- const [layoutVersion, setLayoutVersion] = (0, react_1.useState)(0);
452
- const bumpLayoutVersion = (0, react_1.useCallback)(() => {
453
- setLayoutVersion((version) => version + 1);
454
- }, []);
455
- // Handle reorder by index delta - converts to various callback formats
456
- // NOTE: No LayoutAnimation needed - layout commit compensation handles visual transitions
457
- const handleReorderByDelta = (0, react_1.useCallback)((itemId, indexDelta) => {
458
- if (indexDelta === 0)
459
- return;
460
- const currentItems = dataRef.current;
461
- const currentIndex = currentItems.findIndex((item) => item.id === itemId);
462
- if (currentIndex === -1)
463
- return;
464
- const newIndex = Math.max(0, Math.min(currentItems.length - 1, currentIndex + indexDelta));
465
- if (newIndex === currentIndex)
466
- return;
467
- // Call onReorder if provided
468
- if (onReorder) {
469
- onReorder(itemId, currentIndex, newIndex);
470
- }
471
- // Call onReorderByNeighbors if provided (for fractional indexing)
472
- if (onReorderByNeighbors) {
473
- let afterItemId = null;
474
- let beforeItemId = null;
475
- if (indexDelta > 0) {
476
- afterItemId = currentItems[newIndex]?.id ?? null;
477
- beforeItemId =
478
- newIndex < currentItems.length - 1
479
- ? (currentItems[newIndex + 1]?.id ?? null)
480
- : null;
481
- }
482
- else {
483
- afterItemId =
484
- newIndex > 0 ? (currentItems[newIndex - 1]?.id ?? null) : null;
485
- beforeItemId = currentItems[newIndex]?.id ?? null;
486
- }
487
- onReorderByNeighbors(itemId, afterItemId, beforeItemId);
488
- }
489
- bumpLayoutVersion();
490
- }, [onReorder, onReorderByNeighbors, bumpLayoutVersion]);
491
- const combinedExtraData = (0, react_1.useMemo)(() => [userExtraData, layoutVersion], [userExtraData, layoutVersion]);
492
- // Use ref for totalItems to avoid renderItem callback recreation
493
- const totalItemsRef = (0, react_1.useRef)(data.length);
494
- totalItemsRef.current = data.length;
495
- // Early return for empty data
496
- if (!data || !Array.isArray(data) || data.length === 0) {
497
- const emptyContent = ListEmptyComponent
498
- ? react_1.default.isValidElement(ListEmptyComponent)
499
- ? ListEmptyComponent
500
- : react_1.default.createElement(ListEmptyComponent)
501
- : null;
502
- const footerContent = ListFooterComponent
503
- ? react_1.default.isValidElement(ListFooterComponent)
504
- ? ListFooterComponent
505
- : react_1.default.createElement(ListFooterComponent)
506
- : null;
507
- if (emptyContent || footerContent) {
508
- return (<react_native_1.View style={containerStyle}>
509
- {emptyContent}
510
- {footerContent}
511
- </react_native_1.View>);
512
- }
513
- return null;
921
+ var data = props.data,
922
+ keyExtractor = props.keyExtractor,
923
+ _renderItem = props.renderItem,
924
+ _props$dragEnabled = props.dragEnabled,
925
+ dragEnabled = _props$dragEnabled === void 0 ? false : _props$dragEnabled,
926
+ onReorder = props.onReorder,
927
+ onReorderByNeighbors = props.onReorderByNeighbors,
928
+ canDrag = props.canDrag,
929
+ onHapticFeedback = props.onHapticFeedback,
930
+ config = props.config,
931
+ onPrepareLayoutAnimation = props.onPrepareLayoutAnimation,
932
+ ListFooterComponent = props.ListFooterComponent,
933
+ ListEmptyComponent = props.ListEmptyComponent,
934
+ onRefresh = props.onRefresh,
935
+ _props$refreshing = props.refreshing,
936
+ refreshing = _props$refreshing === void 0 ? false : _props$refreshing,
937
+ onEndReached = props.onEndReached,
938
+ _props$onEndReachedTh = props.onEndReachedThreshold,
939
+ onEndReachedThreshold = _props$onEndReachedTh === void 0 ? 0.5 : _props$onEndReachedTh,
940
+ contentContainerStyle = props.contentContainerStyle,
941
+ flashListProps = _objectWithoutProperties(props, _excluded);
942
+ var userExtraData = flashListProps.extraData,
943
+ userOnCommitLayoutEffect = flashListProps.onCommitLayoutEffect,
944
+ restFlashListProps = _objectWithoutProperties(flashListProps, _excluded2);
945
+
946
+ // Merge config with defaults
947
+ // Note: estimatedItemSize was removed in FlashList v2
948
+ // Users should configure itemHeight via config.drag.itemHeight
949
+ var dragConfig = (0, _react.useMemo)(function () {
950
+ return _objectSpread(_objectSpread({}, _drag.DEFAULT_DRAG_CONFIG), config === null || config === void 0 ? void 0 : config.drag);
951
+ }, [config === null || config === void 0 ? void 0 : config.drag]);
952
+
953
+ // Ref to FlashList
954
+ var flashListRef = (0, _react.useRef)(null);
955
+
956
+ // Expose methods to parent via ref
957
+ (0, _react.useImperativeHandle)(ref, function () {
958
+ return {
959
+ prepareForLayoutAnimation: function prepareForLayoutAnimation() {
960
+ var _flashListRef$current3;
961
+ (_flashListRef$current3 = flashListRef.current) === null || _flashListRef$current3 === void 0 || _flashListRef$current3.prepareForLayoutAnimationRender();
962
+ onPrepareLayoutAnimation === null || onPrepareLayoutAnimation === void 0 || onPrepareLayoutAnimation();
963
+ },
964
+ scrollToOffset: function scrollToOffset(offset) {
965
+ var _flashListRef$current4;
966
+ var animated = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
967
+ (_flashListRef$current4 = flashListRef.current) === null || _flashListRef$current4 === void 0 || _flashListRef$current4.scrollToOffset({
968
+ offset: offset,
969
+ animated: animated
970
+ });
971
+ },
972
+ scrollToIndex: function scrollToIndex(index) {
973
+ var _flashListRef$current5;
974
+ var animated = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
975
+ (_flashListRef$current5 = flashListRef.current) === null || _flashListRef$current5 === void 0 || _flashListRef$current5.scrollToIndex({
976
+ index: index,
977
+ animated: animated
978
+ });
979
+ }
980
+ };
981
+ });
982
+
983
+ // Keep valid items in ref for reorder callback
984
+ var dataRef = (0, _react.useRef)([]);
985
+ dataRef.current = data;
986
+
987
+ // Force FlashList to re-render visible cells after reorder to avoid
988
+ // 1-frame gaps when internal recycling repositions without a React render.
989
+ var _useState9 = (0, _react.useState)(0),
990
+ _useState0 = _slicedToArray(_useState9, 2),
991
+ layoutVersion = _useState0[0],
992
+ setLayoutVersion = _useState0[1];
993
+ var bumpLayoutVersion = (0, _react.useCallback)(function () {
994
+ setLayoutVersion(function (version) {
995
+ return version + 1;
996
+ });
997
+ }, []);
998
+
999
+ // Handle reorder by index delta - converts to various callback formats
1000
+ // NOTE: No LayoutAnimation needed - layout commit compensation handles visual transitions
1001
+ var handleReorderByDelta = (0, _react.useCallback)(function (itemId, indexDelta) {
1002
+ if (indexDelta === 0) return;
1003
+ var currentItems = dataRef.current;
1004
+ var currentIndex = currentItems.findIndex(function (item) {
1005
+ return item.id === itemId;
1006
+ });
1007
+ if (currentIndex === -1) return;
1008
+ var newIndex = Math.max(0, Math.min(currentItems.length - 1, currentIndex + indexDelta));
1009
+ if (newIndex === currentIndex) return;
1010
+
1011
+ // Call onReorder if provided
1012
+ if (onReorder) {
1013
+ onReorder(itemId, currentIndex, newIndex);
1014
+ }
1015
+
1016
+ // Call onReorderByNeighbors if provided (for fractional indexing)
1017
+ if (onReorderByNeighbors) {
1018
+ var afterItemId = null;
1019
+ var beforeItemId = null;
1020
+ if (indexDelta > 0) {
1021
+ var _currentItems$newInde, _currentItems$newInde2, _currentItems$id, _currentItems;
1022
+ afterItemId = (_currentItems$newInde = (_currentItems$newInde2 = currentItems[newIndex]) === null || _currentItems$newInde2 === void 0 ? void 0 : _currentItems$newInde2.id) !== null && _currentItems$newInde !== void 0 ? _currentItems$newInde : null;
1023
+ beforeItemId = newIndex < currentItems.length - 1 ? (_currentItems$id = (_currentItems = currentItems[newIndex + 1]) === null || _currentItems === void 0 ? void 0 : _currentItems.id) !== null && _currentItems$id !== void 0 ? _currentItems$id : null : null;
1024
+ } else {
1025
+ var _currentItems$id2, _currentItems2, _currentItems$newInde3, _currentItems$newInde4;
1026
+ afterItemId = newIndex > 0 ? (_currentItems$id2 = (_currentItems2 = currentItems[newIndex - 1]) === null || _currentItems2 === void 0 ? void 0 : _currentItems2.id) !== null && _currentItems$id2 !== void 0 ? _currentItems$id2 : null : null;
1027
+ beforeItemId = (_currentItems$newInde3 = (_currentItems$newInde4 = currentItems[newIndex]) === null || _currentItems$newInde4 === void 0 ? void 0 : _currentItems$newInde4.id) !== null && _currentItems$newInde3 !== void 0 ? _currentItems$newInde3 : null;
1028
+ }
1029
+ onReorderByNeighbors(itemId, afterItemId, beforeItemId);
514
1030
  }
515
- const plainFlashListFallback = (<react_native_1.View style={containerStyle}>
516
- <flash_list_1.FlashList ref={flashListRef} data={data} renderItem={({ item, index }) => renderItem({
517
- item,
518
- index,
519
- totalItems: data.length,
520
- animatedStyle: {},
521
- dragHandleProps: null,
522
- isDragging: false,
523
- isDragEnabled: false,
524
- triggerExitAnimation: () => { },
525
- resetExitAnimation: () => { },
526
- })} keyExtractor={keyExtractor} contentContainerStyle={contentContainerStyle} ListFooterComponent={ListFooterComponent ?? undefined} onEndReached={onEndReached} onEndReachedThreshold={onEndReachedThreshold}/>
527
- </react_native_1.View>);
528
- return (<AnimationErrorBoundary fallback={plainFlashListFallback}>
529
- <ListAnimationContext_1.ListAnimationProvider>
530
- <DragStateContext_1.DragStateProvider config={dragConfig}>
531
- <DragContainer>
532
- <InnerFlashList {...restFlashListProps} data={data} totalItemsRef={totalItemsRef} flashListRef={flashListRef} renderItem={renderItem} keyExtractor={keyExtractor} canDrag={canDrag} dragEnabled={dragEnabled} onReorderByDelta={onReorder || onReorderByNeighbors
533
- ? handleReorderByDelta
534
- : undefined} onHapticFeedback={onHapticFeedback} itemHeight={dragConfig.itemHeight} ListFooterComponent={ListFooterComponent} ListEmptyComponent={ListEmptyComponent} onEndReached={onEndReached} onEndReachedThreshold={onEndReachedThreshold} onRefresh={onRefresh} refreshing={refreshing} contentContainerStyle={contentContainerStyle} extraData={combinedExtraData} onCommitLayoutEffect={userOnCommitLayoutEffect}/>
535
- <DragOverlay data={data} totalItemsRef={totalItemsRef} renderItem={renderItem}/>
536
- <ShiftedItemOverlays data={data} totalItemsRef={totalItemsRef} renderItem={renderItem}/>
537
- </DragContainer>
538
- </DragStateContext_1.DragStateProvider>
539
- </ListAnimationContext_1.ListAnimationProvider>
540
- </AnimationErrorBoundary>);
1031
+ bumpLayoutVersion();
1032
+ }, [onReorder, onReorderByNeighbors, bumpLayoutVersion]);
1033
+ var combinedExtraData = (0, _react.useMemo)(function () {
1034
+ return [userExtraData, layoutVersion];
1035
+ }, [userExtraData, layoutVersion]);
1036
+
1037
+ // Use ref for totalItems to avoid renderItem callback recreation
1038
+ var totalItemsRef = (0, _react.useRef)(data.length);
1039
+ totalItemsRef.current = data.length;
1040
+
1041
+ // Early return for empty data
1042
+ if (!data || !Array.isArray(data) || data.length === 0) {
1043
+ var emptyContent = ListEmptyComponent ? /*#__PURE__*/_react["default"].isValidElement(ListEmptyComponent) ? ListEmptyComponent : /*#__PURE__*/_react["default"].createElement(ListEmptyComponent) : null;
1044
+ var footerContent = ListFooterComponent ? /*#__PURE__*/_react["default"].isValidElement(ListFooterComponent) ? ListFooterComponent : /*#__PURE__*/_react["default"].createElement(ListFooterComponent) : null;
1045
+ if (emptyContent || footerContent) {
1046
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1047
+ style: containerStyle,
1048
+ children: [emptyContent, footerContent]
1049
+ });
1050
+ }
1051
+ return null;
1052
+ }
1053
+ var plainFlashListFallback = /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1054
+ style: containerStyle,
1055
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_flashList.FlashList, {
1056
+ ref: flashListRef,
1057
+ data: data,
1058
+ renderItem: function renderItem(_ref17) {
1059
+ var item = _ref17.item,
1060
+ index = _ref17.index;
1061
+ return _renderItem({
1062
+ item: item,
1063
+ index: index,
1064
+ totalItems: data.length,
1065
+ animatedStyle: {},
1066
+ dragHandleProps: null,
1067
+ isDragging: false,
1068
+ isDragEnabled: false,
1069
+ triggerExitAnimation: function triggerExitAnimation() {},
1070
+ resetExitAnimation: function resetExitAnimation() {}
1071
+ });
1072
+ },
1073
+ keyExtractor: keyExtractor,
1074
+ contentContainerStyle: contentContainerStyle,
1075
+ ListFooterComponent: ListFooterComponent !== null && ListFooterComponent !== void 0 ? ListFooterComponent : undefined,
1076
+ onEndReached: onEndReached,
1077
+ onEndReachedThreshold: onEndReachedThreshold
1078
+ })
1079
+ });
1080
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(AnimationErrorBoundary, {
1081
+ fallback: plainFlashListFallback,
1082
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_ListAnimationContext.ListAnimationProvider, {
1083
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_DragStateContext.DragStateProvider, {
1084
+ config: dragConfig,
1085
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(DragContainer, {
1086
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(InnerFlashList, _objectSpread(_objectSpread({}, restFlashListProps), {}, {
1087
+ data: data,
1088
+ totalItemsRef: totalItemsRef,
1089
+ flashListRef: flashListRef,
1090
+ renderItem: _renderItem,
1091
+ keyExtractor: keyExtractor,
1092
+ canDrag: canDrag,
1093
+ dragEnabled: dragEnabled,
1094
+ onReorderByDelta: onReorder || onReorderByNeighbors ? handleReorderByDelta : undefined,
1095
+ onHapticFeedback: onHapticFeedback,
1096
+ itemHeight: dragConfig.itemHeight,
1097
+ ListFooterComponent: ListFooterComponent,
1098
+ ListEmptyComponent: ListEmptyComponent,
1099
+ onEndReached: onEndReached,
1100
+ onEndReachedThreshold: onEndReachedThreshold,
1101
+ onRefresh: onRefresh,
1102
+ refreshing: refreshing,
1103
+ contentContainerStyle: contentContainerStyle,
1104
+ extraData: combinedExtraData,
1105
+ onCommitLayoutEffect: userOnCommitLayoutEffect
1106
+ })), /*#__PURE__*/(0, _jsxRuntime.jsx)(DragOverlay, {
1107
+ data: data,
1108
+ totalItemsRef: totalItemsRef,
1109
+ renderItem: _renderItem
1110
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(ShiftedItemOverlays, {
1111
+ data: data,
1112
+ totalItemsRef: totalItemsRef,
1113
+ renderItem: _renderItem
1114
+ })]
1115
+ })
1116
+ })
1117
+ })
1118
+ });
541
1119
  }
542
- const containerStyle = {
543
- flex: 1,
544
- overflow: 'hidden',
1120
+ var containerStyle = {
1121
+ flex: 1,
1122
+ overflow: 'hidden'
545
1123
  };
1124
+
546
1125
  // Forward ref with generic support
547
- exports.AnimatedFlashList = (0, react_1.forwardRef)(AnimatedFlashListInner);
1126
+ var AnimatedFlashList = exports.AnimatedFlashList = /*#__PURE__*/(0, _react.forwardRef)(AnimatedFlashListInner);