react-native-layout-dnd 0.1.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/README.md +262 -0
- package/lib/module/adapters/coordinates.js +135 -0
- package/lib/module/adapters/coordinates.js.map +1 -0
- package/lib/module/adapters/scroll.js +81 -0
- package/lib/module/adapters/scroll.js.map +1 -0
- package/lib/module/adapters/virtualizedList.js +69 -0
- package/lib/module/adapters/virtualizedList.js.map +1 -0
- package/lib/module/components/DndProvider.js +1144 -0
- package/lib/module/components/DndProvider.js.map +1 -0
- package/lib/module/components/DragHandle.js +65 -0
- package/lib/module/components/DragHandle.js.map +1 -0
- package/lib/module/components/GridController.js +151 -0
- package/lib/module/components/GridController.js.map +1 -0
- package/lib/module/components/GridDragHandle.js +15 -0
- package/lib/module/components/GridDragHandle.js.map +1 -0
- package/lib/module/components/GridZoneView.js +59 -0
- package/lib/module/components/GridZoneView.js.map +1 -0
- package/lib/module/components/SortableGrid.js +305 -0
- package/lib/module/components/SortableGrid.js.map +1 -0
- package/lib/module/components/SortableList.js +714 -0
- package/lib/module/components/SortableList.js.map +1 -0
- package/lib/module/components/dndTypes.js +4 -0
- package/lib/module/components/dndTypes.js.map +1 -0
- package/lib/module/components/gridTypes.js +4 -0
- package/lib/module/components/gridTypes.js.map +1 -0
- package/lib/module/components/handleViewProps.js +13 -0
- package/lib/module/components/handleViewProps.js.map +1 -0
- package/lib/module/contracts.js +4 -0
- package/lib/module/contracts.js.map +1 -0
- package/lib/module/controller/approval.js +98 -0
- package/lib/module/controller/approval.js.map +1 -0
- package/lib/module/controller/coordinatorBase.js +93 -0
- package/lib/module/controller/coordinatorBase.js.map +1 -0
- package/lib/module/controller/dndSession.js +621 -0
- package/lib/module/controller/dndSession.js.map +1 -0
- package/lib/module/controller/dndState.js +103 -0
- package/lib/module/controller/dndState.js.map +1 -0
- package/lib/module/controller/gridAdapter.js +191 -0
- package/lib/module/controller/gridAdapter.js.map +1 -0
- package/lib/module/controller/gridState.js +101 -0
- package/lib/module/controller/gridState.js.map +1 -0
- package/lib/module/controller/locations.js +48 -0
- package/lib/module/controller/locations.js.map +1 -0
- package/lib/module/controller/previewPath.js +73 -0
- package/lib/module/controller/previewPath.js.map +1 -0
- package/lib/module/controller/sessionCore.js +222 -0
- package/lib/module/controller/sessionCore.js.map +1 -0
- package/lib/module/controller/useDndState.js +9 -0
- package/lib/module/controller/useDndState.js.map +1 -0
- package/lib/module/controller/useGridState.js +9 -0
- package/lib/module/controller/useGridState.js.map +1 -0
- package/lib/module/controller/useStoreState.js +15 -0
- package/lib/module/controller/useStoreState.js.map +1 -0
- package/lib/module/defaults.js +7 -0
- package/lib/module/defaults.js.map +1 -0
- package/lib/module/diagnostics.js +16 -0
- package/lib/module/diagnostics.js.map +1 -0
- package/lib/module/engine/gridPlacements.js +54 -0
- package/lib/module/engine/gridPlacements.js.map +1 -0
- package/lib/module/engine/index.js +19 -0
- package/lib/module/engine/index.js.map +1 -0
- package/lib/module/engine/layout.js +36 -0
- package/lib/module/engine/layout.js.map +1 -0
- package/lib/module/engine/layoutMove.js +326 -0
- package/lib/module/engine/layoutMove.js.map +1 -0
- package/lib/module/engine/layoutState.js +344 -0
- package/lib/module/engine/layoutState.js.map +1 -0
- package/lib/module/engine/list.js +172 -0
- package/lib/module/engine/list.js.map +1 -0
- package/lib/module/engine/listIndex.js +125 -0
- package/lib/module/engine/listIndex.js.map +1 -0
- package/lib/module/engine/move.js +216 -0
- package/lib/module/engine/move.js.map +1 -0
- package/lib/module/engine/movementPolicy.js +77 -0
- package/lib/module/engine/movementPolicy.js.map +1 -0
- package/lib/module/engine/orderedTarget.js +71 -0
- package/lib/module/engine/orderedTarget.js.map +1 -0
- package/lib/module/engine/pack.js +30 -0
- package/lib/module/engine/pack.js.map +1 -0
- package/lib/module/engine/rectangles.js +12 -0
- package/lib/module/engine/rectangles.js.map +1 -0
- package/lib/module/engine/sameLayout.js +29 -0
- package/lib/module/engine/sameLayout.js.map +1 -0
- package/lib/module/engine/search.js +159 -0
- package/lib/module/engine/search.js.map +1 -0
- package/lib/module/engine/spatial.js +27 -0
- package/lib/module/engine/spatial.js.map +1 -0
- package/lib/module/engine/spatialAppRotation.js +39 -0
- package/lib/module/engine/spatialAppRotation.js.map +1 -0
- package/lib/module/engine/spatialExchange.js +164 -0
- package/lib/module/engine/spatialExchange.js.map +1 -0
- package/lib/module/engine/spatialInsertion.js +135 -0
- package/lib/module/engine/spatialInsertion.js.map +1 -0
- package/lib/module/engine/spatialPolicy.js +41 -0
- package/lib/module/engine/spatialPolicy.js.map +1 -0
- package/lib/module/engine/spatialRotation.js +48 -0
- package/lib/module/engine/spatialRotation.js.map +1 -0
- package/lib/module/engine/validation.js +205 -0
- package/lib/module/engine/validation.js.map +1 -0
- package/lib/module/gesture/DndGestureGroup.js +268 -0
- package/lib/module/gesture/DndGestureGroup.js.map +1 -0
- package/lib/module/gesture/DndGestureHost.js +32 -0
- package/lib/module/gesture/DndGestureHost.js.map +1 -0
- package/lib/module/gesture/gestureConfig.js +81 -0
- package/lib/module/gesture/gestureConfig.js.map +1 -0
- package/lib/module/gesture/gestureGrouping.js +122 -0
- package/lib/module/gesture/gestureGrouping.js.map +1 -0
- package/lib/module/gesture/gestureHost.js +106 -0
- package/lib/module/gesture/gestureHost.js.map +1 -0
- package/lib/module/index.js +23 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/pointer/dndTarget.js +101 -0
- package/lib/module/pointer/dndTarget.js.map +1 -0
- package/lib/module/pointer/geometry.js +363 -0
- package/lib/module/pointer/geometry.js.map +1 -0
- package/lib/module/pointer/layoutTarget.js +307 -0
- package/lib/module/pointer/layoutTarget.js.map +1 -0
- package/lib/module/pointer/listCellMotion.js +99 -0
- package/lib/module/pointer/listCellMotion.js.map +1 -0
- package/lib/module/pointer/listTarget.js +42 -0
- package/lib/module/pointer/listTarget.js.map +1 -0
- package/lib/module/pointer/motion.js +37 -0
- package/lib/module/pointer/motion.js.map +1 -0
- package/lib/module/runtime/dndPresentation.js +81 -0
- package/lib/module/runtime/dndPresentation.js.map +1 -0
- package/lib/module/runtime/dndRenderStore.js +129 -0
- package/lib/module/runtime/dndRenderStore.js.map +1 -0
- package/lib/module/runtime/dndRuntime.js +135 -0
- package/lib/module/runtime/dndRuntime.js.map +1 -0
- package/lib/module/runtime/dndSettlement.js +77 -0
- package/lib/module/runtime/dndSettlement.js.map +1 -0
- package/lib/module/runtime/gridRuntime.js +37 -0
- package/lib/module/runtime/gridRuntime.js.map +1 -0
- package/lib/module/types.js +4 -0
- package/lib/module/types.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/adapters/coordinates.d.ts +9 -0
- package/lib/typescript/src/adapters/coordinates.d.ts.map +1 -0
- package/lib/typescript/src/adapters/scroll.d.ts +49 -0
- package/lib/typescript/src/adapters/scroll.d.ts.map +1 -0
- package/lib/typescript/src/adapters/virtualizedList.d.ts +22 -0
- package/lib/typescript/src/adapters/virtualizedList.d.ts.map +1 -0
- package/lib/typescript/src/components/DndProvider.d.ts +4 -0
- package/lib/typescript/src/components/DndProvider.d.ts.map +1 -0
- package/lib/typescript/src/components/DragHandle.d.ts +4 -0
- package/lib/typescript/src/components/DragHandle.d.ts.map +1 -0
- package/lib/typescript/src/components/GridController.d.ts +21 -0
- package/lib/typescript/src/components/GridController.d.ts.map +1 -0
- package/lib/typescript/src/components/GridDragHandle.d.ts +8 -0
- package/lib/typescript/src/components/GridDragHandle.d.ts.map +1 -0
- package/lib/typescript/src/components/GridZoneView.d.ts +8 -0
- package/lib/typescript/src/components/GridZoneView.d.ts.map +1 -0
- package/lib/typescript/src/components/SortableGrid.d.ts +4 -0
- package/lib/typescript/src/components/SortableGrid.d.ts.map +1 -0
- package/lib/typescript/src/components/SortableList.d.ts +4 -0
- package/lib/typescript/src/components/SortableList.d.ts.map +1 -0
- package/lib/typescript/src/components/dndTypes.d.ts +94 -0
- package/lib/typescript/src/components/dndTypes.d.ts.map +1 -0
- package/lib/typescript/src/components/gridTypes.d.ts +84 -0
- package/lib/typescript/src/components/gridTypes.d.ts.map +1 -0
- package/lib/typescript/src/components/handleViewProps.d.ts +6 -0
- package/lib/typescript/src/components/handleViewProps.d.ts.map +1 -0
- package/lib/typescript/src/contracts.d.ts +145 -0
- package/lib/typescript/src/contracts.d.ts.map +1 -0
- package/lib/typescript/src/controller/approval.d.ts +66 -0
- package/lib/typescript/src/controller/approval.d.ts.map +1 -0
- package/lib/typescript/src/controller/coordinatorBase.d.ts +37 -0
- package/lib/typescript/src/controller/coordinatorBase.d.ts.map +1 -0
- package/lib/typescript/src/controller/dndSession.d.ts +152 -0
- package/lib/typescript/src/controller/dndSession.d.ts.map +1 -0
- package/lib/typescript/src/controller/dndState.d.ts +27 -0
- package/lib/typescript/src/controller/dndState.d.ts.map +1 -0
- package/lib/typescript/src/controller/gridAdapter.d.ts +44 -0
- package/lib/typescript/src/controller/gridAdapter.d.ts.map +1 -0
- package/lib/typescript/src/controller/gridState.d.ts +18 -0
- package/lib/typescript/src/controller/gridState.d.ts.map +1 -0
- package/lib/typescript/src/controller/locations.d.ts +9 -0
- package/lib/typescript/src/controller/locations.d.ts.map +1 -0
- package/lib/typescript/src/controller/previewPath.d.ts +49 -0
- package/lib/typescript/src/controller/previewPath.d.ts.map +1 -0
- package/lib/typescript/src/controller/sessionCore.d.ts +106 -0
- package/lib/typescript/src/controller/sessionCore.d.ts.map +1 -0
- package/lib/typescript/src/controller/useDndState.d.ts +8 -0
- package/lib/typescript/src/controller/useDndState.d.ts.map +1 -0
- package/lib/typescript/src/controller/useGridState.d.ts +8 -0
- package/lib/typescript/src/controller/useGridState.d.ts.map +1 -0
- package/lib/typescript/src/controller/useStoreState.d.ts +16 -0
- package/lib/typescript/src/controller/useStoreState.d.ts.map +1 -0
- package/lib/typescript/src/defaults.d.ts +5 -0
- package/lib/typescript/src/defaults.d.ts.map +1 -0
- package/lib/typescript/src/diagnostics.d.ts +9 -0
- package/lib/typescript/src/diagnostics.d.ts.map +1 -0
- package/lib/typescript/src/engine/gridPlacements.d.ts +22 -0
- package/lib/typescript/src/engine/gridPlacements.d.ts.map +1 -0
- package/lib/typescript/src/engine/index.d.ts +28 -0
- package/lib/typescript/src/engine/index.d.ts.map +1 -0
- package/lib/typescript/src/engine/layout.d.ts +4 -0
- package/lib/typescript/src/engine/layout.d.ts.map +1 -0
- package/lib/typescript/src/engine/layoutMove.d.ts +46 -0
- package/lib/typescript/src/engine/layoutMove.d.ts.map +1 -0
- package/lib/typescript/src/engine/layoutState.d.ts +22 -0
- package/lib/typescript/src/engine/layoutState.d.ts.map +1 -0
- package/lib/typescript/src/engine/list.d.ts +68 -0
- package/lib/typescript/src/engine/list.d.ts.map +1 -0
- package/lib/typescript/src/engine/listIndex.d.ts +32 -0
- package/lib/typescript/src/engine/listIndex.d.ts.map +1 -0
- package/lib/typescript/src/engine/move.d.ts +10 -0
- package/lib/typescript/src/engine/move.d.ts.map +1 -0
- package/lib/typescript/src/engine/movementPolicy.d.ts +34 -0
- package/lib/typescript/src/engine/movementPolicy.d.ts.map +1 -0
- package/lib/typescript/src/engine/orderedTarget.d.ts +32 -0
- package/lib/typescript/src/engine/orderedTarget.d.ts.map +1 -0
- package/lib/typescript/src/engine/pack.d.ts +3 -0
- package/lib/typescript/src/engine/pack.d.ts.map +1 -0
- package/lib/typescript/src/engine/rectangles.d.ts +9 -0
- package/lib/typescript/src/engine/rectangles.d.ts.map +1 -0
- package/lib/typescript/src/engine/sameLayout.d.ts +4 -0
- package/lib/typescript/src/engine/sameLayout.d.ts.map +1 -0
- package/lib/typescript/src/engine/search.d.ts +28 -0
- package/lib/typescript/src/engine/search.d.ts.map +1 -0
- package/lib/typescript/src/engine/spatial.d.ts +16 -0
- package/lib/typescript/src/engine/spatial.d.ts.map +1 -0
- package/lib/typescript/src/engine/spatialAppRotation.d.ts +4 -0
- package/lib/typescript/src/engine/spatialAppRotation.d.ts.map +1 -0
- package/lib/typescript/src/engine/spatialExchange.d.ts +5 -0
- package/lib/typescript/src/engine/spatialExchange.d.ts.map +1 -0
- package/lib/typescript/src/engine/spatialInsertion.d.ts +5 -0
- package/lib/typescript/src/engine/spatialInsertion.d.ts.map +1 -0
- package/lib/typescript/src/engine/spatialPolicy.d.ts +9 -0
- package/lib/typescript/src/engine/spatialPolicy.d.ts.map +1 -0
- package/lib/typescript/src/engine/spatialRotation.d.ts +8 -0
- package/lib/typescript/src/engine/spatialRotation.d.ts.map +1 -0
- package/lib/typescript/src/engine/validation.d.ts +9 -0
- package/lib/typescript/src/engine/validation.d.ts.map +1 -0
- package/lib/typescript/src/gesture/DndGestureGroup.d.ts +33 -0
- package/lib/typescript/src/gesture/DndGestureGroup.d.ts.map +1 -0
- package/lib/typescript/src/gesture/DndGestureHost.d.ts +12 -0
- package/lib/typescript/src/gesture/DndGestureHost.d.ts.map +1 -0
- package/lib/typescript/src/gesture/gestureConfig.d.ts +15 -0
- package/lib/typescript/src/gesture/gestureConfig.d.ts.map +1 -0
- package/lib/typescript/src/gesture/gestureGrouping.d.ts +66 -0
- package/lib/typescript/src/gesture/gestureGrouping.d.ts.map +1 -0
- package/lib/typescript/src/gesture/gestureHost.d.ts +23 -0
- package/lib/typescript/src/gesture/gestureHost.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +38 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/pointer/dndTarget.d.ts +14 -0
- package/lib/typescript/src/pointer/dndTarget.d.ts.map +1 -0
- package/lib/typescript/src/pointer/geometry.d.ts +28 -0
- package/lib/typescript/src/pointer/geometry.d.ts.map +1 -0
- package/lib/typescript/src/pointer/layoutTarget.d.ts +43 -0
- package/lib/typescript/src/pointer/layoutTarget.d.ts.map +1 -0
- package/lib/typescript/src/pointer/listCellMotion.d.ts +43 -0
- package/lib/typescript/src/pointer/listCellMotion.d.ts.map +1 -0
- package/lib/typescript/src/pointer/listTarget.d.ts +7 -0
- package/lib/typescript/src/pointer/listTarget.d.ts.map +1 -0
- package/lib/typescript/src/pointer/motion.d.ts +20 -0
- package/lib/typescript/src/pointer/motion.d.ts.map +1 -0
- package/lib/typescript/src/runtime/dndPresentation.d.ts +37 -0
- package/lib/typescript/src/runtime/dndPresentation.d.ts.map +1 -0
- package/lib/typescript/src/runtime/dndRenderStore.d.ts +46 -0
- package/lib/typescript/src/runtime/dndRenderStore.d.ts.map +1 -0
- package/lib/typescript/src/runtime/dndRuntime.d.ts +138 -0
- package/lib/typescript/src/runtime/dndRuntime.d.ts.map +1 -0
- package/lib/typescript/src/runtime/dndSettlement.d.ts +18 -0
- package/lib/typescript/src/runtime/dndSettlement.d.ts.map +1 -0
- package/lib/typescript/src/runtime/gridRuntime.d.ts +54 -0
- package/lib/typescript/src/runtime/gridRuntime.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +180 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/package.json +136 -0
- package/src/adapters/coordinates.ts +181 -0
- package/src/adapters/scroll.ts +147 -0
- package/src/adapters/virtualizedList.ts +112 -0
- package/src/components/DndProvider.tsx +1836 -0
- package/src/components/DragHandle.tsx +89 -0
- package/src/components/GridController.tsx +213 -0
- package/src/components/GridDragHandle.tsx +11 -0
- package/src/components/GridZoneView.tsx +57 -0
- package/src/components/SortableGrid.tsx +449 -0
- package/src/components/SortableList.tsx +1028 -0
- package/src/components/dndTypes.ts +102 -0
- package/src/components/gridTypes.ts +90 -0
- package/src/components/handleViewProps.ts +35 -0
- package/src/contracts.ts +149 -0
- package/src/controller/approval.ts +179 -0
- package/src/controller/coordinatorBase.ts +118 -0
- package/src/controller/dndSession.ts +1058 -0
- package/src/controller/dndState.ts +198 -0
- package/src/controller/gridAdapter.ts +268 -0
- package/src/controller/gridState.ts +187 -0
- package/src/controller/locations.ts +67 -0
- package/src/controller/previewPath.ts +90 -0
- package/src/controller/sessionCore.ts +390 -0
- package/src/controller/useDndState.ts +26 -0
- package/src/controller/useGridState.ts +22 -0
- package/src/controller/useStoreState.ts +27 -0
- package/src/defaults.ts +4 -0
- package/src/diagnostics.ts +23 -0
- package/src/engine/gridPlacements.ts +85 -0
- package/src/engine/index.ts +99 -0
- package/src/engine/layout.ts +29 -0
- package/src/engine/layoutMove.ts +401 -0
- package/src/engine/layoutState.ts +490 -0
- package/src/engine/list.ts +307 -0
- package/src/engine/listIndex.ts +174 -0
- package/src/engine/move.ts +274 -0
- package/src/engine/movementPolicy.ts +151 -0
- package/src/engine/orderedTarget.ts +131 -0
- package/src/engine/pack.ts +24 -0
- package/src/engine/rectangles.ts +18 -0
- package/src/engine/sameLayout.ts +50 -0
- package/src/engine/search.ts +174 -0
- package/src/engine/spatial.ts +36 -0
- package/src/engine/spatialAppRotation.ts +53 -0
- package/src/engine/spatialExchange.ts +212 -0
- package/src/engine/spatialInsertion.ts +157 -0
- package/src/engine/spatialPolicy.ts +48 -0
- package/src/engine/spatialRotation.ts +56 -0
- package/src/engine/validation.ts +358 -0
- package/src/gesture/DndGestureGroup.tsx +357 -0
- package/src/gesture/DndGestureHost.tsx +50 -0
- package/src/gesture/gestureConfig.ts +123 -0
- package/src/gesture/gestureGrouping.ts +210 -0
- package/src/gesture/gestureHost.tsx +163 -0
- package/src/index.ts +162 -0
- package/src/pointer/dndTarget.ts +213 -0
- package/src/pointer/geometry.ts +584 -0
- package/src/pointer/layoutTarget.ts +515 -0
- package/src/pointer/listCellMotion.ts +174 -0
- package/src/pointer/listTarget.ts +56 -0
- package/src/pointer/motion.ts +66 -0
- package/src/runtime/dndPresentation.ts +113 -0
- package/src/runtime/dndRenderStore.ts +246 -0
- package/src/runtime/dndRuntime.ts +311 -0
- package/src/runtime/dndSettlement.ts +117 -0
- package/src/runtime/gridRuntime.ts +73 -0
- package/src/types.ts +189 -0
|
@@ -0,0 +1,1028 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createContext,
|
|
3
|
+
memo,
|
|
4
|
+
useCallback,
|
|
5
|
+
useContext,
|
|
6
|
+
useEffect,
|
|
7
|
+
useLayoutEffect,
|
|
8
|
+
useMemo,
|
|
9
|
+
useRef,
|
|
10
|
+
useState,
|
|
11
|
+
} from 'react';
|
|
12
|
+
import {
|
|
13
|
+
type FlatList,
|
|
14
|
+
type HostInstance,
|
|
15
|
+
type LayoutChangeEvent,
|
|
16
|
+
type ScrollView,
|
|
17
|
+
StyleSheet,
|
|
18
|
+
View,
|
|
19
|
+
} from 'react-native';
|
|
20
|
+
import Animated, {
|
|
21
|
+
type AnimatedRef,
|
|
22
|
+
type SharedValue,
|
|
23
|
+
makeMutable,
|
|
24
|
+
useAnimatedRef,
|
|
25
|
+
useAnimatedScrollHandler,
|
|
26
|
+
useAnimatedStyle,
|
|
27
|
+
useDerivedValue,
|
|
28
|
+
useSharedValue,
|
|
29
|
+
} from 'react-native-reanimated';
|
|
30
|
+
import { scheduleOnRN, scheduleOnUI } from 'react-native-worklets';
|
|
31
|
+
|
|
32
|
+
import { validateAutoScrollOptions } from '../adapters/scroll';
|
|
33
|
+
import { resolveListVirtualization } from '../adapters/virtualizedList';
|
|
34
|
+
import type { DndItem, ListLayoutZone } from '../contracts';
|
|
35
|
+
import {
|
|
36
|
+
computeListLayout,
|
|
37
|
+
ListSizeCache,
|
|
38
|
+
type ListLayoutEntry,
|
|
39
|
+
type ListLayoutResult,
|
|
40
|
+
} from '../engine/list';
|
|
41
|
+
import {
|
|
42
|
+
DndItemContext,
|
|
43
|
+
useDndRuntime,
|
|
44
|
+
useDndZoneSnapshot,
|
|
45
|
+
useDndItemSnapshot,
|
|
46
|
+
type DndRuntime,
|
|
47
|
+
} from '../runtime/dndRuntime';
|
|
48
|
+
import {
|
|
49
|
+
createZonePresentation,
|
|
50
|
+
type DndZonePresentation,
|
|
51
|
+
} from '../runtime/dndPresentation';
|
|
52
|
+
import type { RetainSlotsRule } from '../runtime/dndRenderStore';
|
|
53
|
+
import type { ListItemRenderArgs, SortableListProps } from './dndTypes';
|
|
54
|
+
import { useListCellMotion } from '../pointer/listCellMotion';
|
|
55
|
+
|
|
56
|
+
type ListCellProps<T> = {
|
|
57
|
+
item: DndItem<T>;
|
|
58
|
+
index: number;
|
|
59
|
+
zoneId: string;
|
|
60
|
+
committedRevision?: SharedValue<number>;
|
|
61
|
+
start: number;
|
|
62
|
+
size: number;
|
|
63
|
+
orientation: 'vertical' | 'horizontal';
|
|
64
|
+
crossSize: number;
|
|
65
|
+
fixed: boolean;
|
|
66
|
+
measurementIdentity: object;
|
|
67
|
+
renderItem(args: ListItemRenderArgs<T>): React.ReactNode;
|
|
68
|
+
hiddenItemId: SharedValue<string | null>;
|
|
69
|
+
animation: DndRuntime['animation'];
|
|
70
|
+
presentation: DndRuntime['presentation'];
|
|
71
|
+
isDragging: boolean;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
type ListMeasurementState = {
|
|
75
|
+
revision: number;
|
|
76
|
+
onSize(itemId: string, size: number): void;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const ListMeasurementContext = createContext<ListMeasurementState | null>(null);
|
|
80
|
+
|
|
81
|
+
function ListCellContent<T>({
|
|
82
|
+
item,
|
|
83
|
+
index,
|
|
84
|
+
zoneId,
|
|
85
|
+
committedRevision,
|
|
86
|
+
start,
|
|
87
|
+
size,
|
|
88
|
+
orientation,
|
|
89
|
+
crossSize,
|
|
90
|
+
fixed,
|
|
91
|
+
measurementIdentity,
|
|
92
|
+
slotStart,
|
|
93
|
+
expectedStart,
|
|
94
|
+
renderItem,
|
|
95
|
+
hiddenItemId,
|
|
96
|
+
animation,
|
|
97
|
+
presentation,
|
|
98
|
+
isDragging,
|
|
99
|
+
}: ListCellProps<T> & {
|
|
100
|
+
/**
|
|
101
|
+
* Flow start of the enclosing native slot in list content coordinates. It
|
|
102
|
+
* is applied as a plain style in the React commit that positions the slot,
|
|
103
|
+
* so the slot move and its compensation reach the screen in one mount
|
|
104
|
+
* transaction. A measured correction would lag that commit by a frame and
|
|
105
|
+
* show the cell at its new slot before its content start has moved.
|
|
106
|
+
*/
|
|
107
|
+
slotStart?: number;
|
|
108
|
+
/** UI copy of the slot's flow start for the motion fallback target. */
|
|
109
|
+
expectedStart?: SharedValue<number>;
|
|
110
|
+
}) {
|
|
111
|
+
const measurement = useContext(ListMeasurementContext);
|
|
112
|
+
if (!measurement)
|
|
113
|
+
throw new Error('ListCell requires its measurement context.');
|
|
114
|
+
const { revision, onSize } = measurement;
|
|
115
|
+
const ref = useAnimatedRef();
|
|
116
|
+
const mounted = useRef(false);
|
|
117
|
+
useLayoutEffect(() => {
|
|
118
|
+
mounted.current = true;
|
|
119
|
+
return () => {
|
|
120
|
+
mounted.current = false;
|
|
121
|
+
};
|
|
122
|
+
}, []);
|
|
123
|
+
const itemId = item.id;
|
|
124
|
+
const runtime = useDndRuntime();
|
|
125
|
+
const publicSlot = useDndItemSnapshot(runtime, zoneId, itemId);
|
|
126
|
+
const itemContext = useMemo(
|
|
127
|
+
() => ({ itemId, zoneId, ref }),
|
|
128
|
+
[itemId, zoneId, ref],
|
|
129
|
+
);
|
|
130
|
+
const publicIndex = publicSlot?.index ?? index;
|
|
131
|
+
const content = useMemo(
|
|
132
|
+
() => renderItem({ item, index: publicIndex, zoneId, isDragging }),
|
|
133
|
+
[item, publicIndex, zoneId, isDragging, renderItem],
|
|
134
|
+
);
|
|
135
|
+
const vertical = orientation === 'vertical';
|
|
136
|
+
const revisionSource = committedRevision ?? revision;
|
|
137
|
+
// Mount uses the Provider's JS view of the scheduled UI geometry. Reading the
|
|
138
|
+
// shared presentation from JS would block on the UI runtime and copy every
|
|
139
|
+
// rect for each new cell; the UI mapper adopts actual UI geometry first.
|
|
140
|
+
const { animatedStart, targetSize } = useListCellMotion({
|
|
141
|
+
zoneId,
|
|
142
|
+
itemId,
|
|
143
|
+
vertical,
|
|
144
|
+
fixed,
|
|
145
|
+
start: expectedStart ?? start,
|
|
146
|
+
size,
|
|
147
|
+
presentation,
|
|
148
|
+
revision: revisionSource,
|
|
149
|
+
hiddenItemId,
|
|
150
|
+
animation,
|
|
151
|
+
initial: {
|
|
152
|
+
presentation: runtime.presentationSnapshot?.(),
|
|
153
|
+
revision:
|
|
154
|
+
typeof revisionSource === 'number'
|
|
155
|
+
? revisionSource
|
|
156
|
+
: (runtime.snapshot.value?.revision ?? 0),
|
|
157
|
+
start,
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
// Natural-size cells never consume a target dimension; only authoritative
|
|
161
|
+
// sizes read the validated size, so measurements do not restart this mapper.
|
|
162
|
+
// The transform is the content start itself; the slot offset below cancels
|
|
163
|
+
// the native flow position, so no per-frame slot measurement is involved.
|
|
164
|
+
const animated = useAnimatedStyle(() => ({
|
|
165
|
+
opacity: hiddenItemId.get() === itemId ? 0 : 1,
|
|
166
|
+
...(fixed
|
|
167
|
+
? vertical
|
|
168
|
+
? { height: targetSize.get() }
|
|
169
|
+
: { width: targetSize.get() }
|
|
170
|
+
: {}),
|
|
171
|
+
transform: [
|
|
172
|
+
{ translateX: vertical ? 0 : animatedStart.get() },
|
|
173
|
+
{ translateY: vertical ? animatedStart.get() : 0 },
|
|
174
|
+
],
|
|
175
|
+
}));
|
|
176
|
+
const measured = useCallback(
|
|
177
|
+
(width: number, height: number) => {
|
|
178
|
+
if (mounted.current && !fixed) onSize(itemId, vertical ? height : width);
|
|
179
|
+
},
|
|
180
|
+
[fixed, itemId, onSize, vertical],
|
|
181
|
+
);
|
|
182
|
+
const measurementRequest = useRef<{
|
|
183
|
+
identity: object;
|
|
184
|
+
itemId: string;
|
|
185
|
+
fixed: boolean;
|
|
186
|
+
pending: boolean;
|
|
187
|
+
} | null>(null);
|
|
188
|
+
useEffect(() => {
|
|
189
|
+
const previous = measurementRequest.current;
|
|
190
|
+
const request = {
|
|
191
|
+
identity: measurementIdentity,
|
|
192
|
+
itemId,
|
|
193
|
+
fixed,
|
|
194
|
+
pending:
|
|
195
|
+
!fixed &&
|
|
196
|
+
previous !== null &&
|
|
197
|
+
(previous.identity !== measurementIdentity ||
|
|
198
|
+
previous.itemId !== itemId ||
|
|
199
|
+
previous.fixed ||
|
|
200
|
+
previous.pending),
|
|
201
|
+
};
|
|
202
|
+
measurementRequest.current = request;
|
|
203
|
+
// A new native cell reports its initial size through onLayout. Asking for
|
|
204
|
+
// the same measurement here doubles native callbacks while windowing.
|
|
205
|
+
// An existing cell needs a fallback only when its cache context changes,
|
|
206
|
+
// since unchanged native dimensions may not produce another onLayout.
|
|
207
|
+
if (!request.pending) return;
|
|
208
|
+
let current = true;
|
|
209
|
+
ref.current?.measure((_x, _y, width, height) => {
|
|
210
|
+
if (current) {
|
|
211
|
+
request.pending = false;
|
|
212
|
+
measured(width, height);
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
return () => {
|
|
216
|
+
current = false;
|
|
217
|
+
};
|
|
218
|
+
}, [fixed, itemId, measured, measurementIdentity, ref]);
|
|
219
|
+
return (
|
|
220
|
+
<DndItemContext value={itemContext}>
|
|
221
|
+
<Animated.View
|
|
222
|
+
ref={ref}
|
|
223
|
+
collapsable={false}
|
|
224
|
+
style={[
|
|
225
|
+
styles.cell,
|
|
226
|
+
vertical
|
|
227
|
+
? {
|
|
228
|
+
width: crossSize,
|
|
229
|
+
...(slotStart !== undefined ? { top: negate(slotStart) } : {}),
|
|
230
|
+
...(fixed ? { height: size } : {}),
|
|
231
|
+
}
|
|
232
|
+
: {
|
|
233
|
+
height: crossSize,
|
|
234
|
+
...(slotStart !== undefined ? { left: negate(slotStart) } : {}),
|
|
235
|
+
...(fixed ? { width: size } : {}),
|
|
236
|
+
},
|
|
237
|
+
animated,
|
|
238
|
+
]}
|
|
239
|
+
onLayout={event =>
|
|
240
|
+
measured(
|
|
241
|
+
event.nativeEvent.layout.width,
|
|
242
|
+
event.nativeEvent.layout.height,
|
|
243
|
+
)
|
|
244
|
+
}
|
|
245
|
+
>
|
|
246
|
+
{content}
|
|
247
|
+
</Animated.View>
|
|
248
|
+
</DndItemContext>
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Pointer/session updates must not rerender every mounted card. Pass only the
|
|
253
|
+
// cell's visible state and primitive geometry; entries are rebuilt together
|
|
254
|
+
// when an item is measured or moved, including entries that did not change.
|
|
255
|
+
const ListCell = memo(ListCellContent) as typeof ListCellContent;
|
|
256
|
+
|
|
257
|
+
type ExpectedStartUpdate = { shared: SharedValue<number>; start: number };
|
|
258
|
+
/** Queue a slot's new flow start for the UI runtime; `null` drops it. */
|
|
259
|
+
type PublishExpectedStart = (
|
|
260
|
+
shared: SharedValue<number>,
|
|
261
|
+
start: number | null,
|
|
262
|
+
) => void;
|
|
263
|
+
|
|
264
|
+
function applyExpectedStarts(updates: ExpectedStartUpdate[]) {
|
|
265
|
+
'worklet';
|
|
266
|
+
for (const update of updates) update.shared.set(update.start);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function negate(value: number) {
|
|
270
|
+
return value === 0 ? 0 : -value;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function VirtualListCellContent<T>({
|
|
274
|
+
publishExpectedStart,
|
|
275
|
+
slotSize,
|
|
276
|
+
...cellProps
|
|
277
|
+
}: ListCellProps<T> & {
|
|
278
|
+
publishExpectedStart: PublishExpectedStart;
|
|
279
|
+
slotSize: number;
|
|
280
|
+
}) {
|
|
281
|
+
const style = useMemo(
|
|
282
|
+
() =>
|
|
283
|
+
cellProps.orientation === 'vertical'
|
|
284
|
+
? { width: cellProps.crossSize, height: slotSize }
|
|
285
|
+
: [
|
|
286
|
+
styles.horizontalSlot,
|
|
287
|
+
{ height: cellProps.crossSize, width: slotSize },
|
|
288
|
+
],
|
|
289
|
+
[cellProps.orientation, cellProps.crossSize, slotSize],
|
|
290
|
+
);
|
|
291
|
+
// The flow start is only ever assigned plain numbers. A plain mutable skips
|
|
292
|
+
// the per-value cancelAnimation UI task that useSharedValue schedules on
|
|
293
|
+
// unmount. It feeds the cell's motion fallback target; positioning itself
|
|
294
|
+
// uses the React-committed slot offset, so nothing waits for a native measure.
|
|
295
|
+
const [expectedStart] = useState(() => makeMutable(cellProps.start));
|
|
296
|
+
const published = useRef(cellProps.start);
|
|
297
|
+
useLayoutEffect(() => {
|
|
298
|
+
if (published.current === cellProps.start) return;
|
|
299
|
+
published.current = cellProps.start;
|
|
300
|
+
publishExpectedStart(expectedStart, cellProps.start);
|
|
301
|
+
}, [cellProps.start, expectedStart, publishExpectedStart]);
|
|
302
|
+
useLayoutEffect(
|
|
303
|
+
() => () => publishExpectedStart(expectedStart, null),
|
|
304
|
+
[expectedStart, publishExpectedStart],
|
|
305
|
+
);
|
|
306
|
+
return (
|
|
307
|
+
<View collapsable={false} style={style}>
|
|
308
|
+
<ListCell
|
|
309
|
+
{...cellProps}
|
|
310
|
+
slotStart={cellProps.start}
|
|
311
|
+
expectedStart={expectedStart}
|
|
312
|
+
/>
|
|
313
|
+
</View>
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const VirtualListCell = memo(
|
|
318
|
+
VirtualListCellContent,
|
|
319
|
+
) as typeof VirtualListCellContent;
|
|
320
|
+
|
|
321
|
+
type VirtualListRenderState = {
|
|
322
|
+
entriesById: ReadonlyMap<string, ListLayoutEntry>;
|
|
323
|
+
data: ReadonlyMap<string, DndItem<unknown>>;
|
|
324
|
+
activeItemId: string | null;
|
|
325
|
+
slotSize(entry: ListLayoutEntry): number;
|
|
326
|
+
cellProps: Omit<
|
|
327
|
+
ListCellProps<unknown>,
|
|
328
|
+
'item' | 'index' | 'start' | 'size' | 'isDragging'
|
|
329
|
+
> & {
|
|
330
|
+
publishExpectedStart: PublishExpectedStart;
|
|
331
|
+
};
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
const VirtualListContext = createContext<VirtualListRenderState | null>(null);
|
|
335
|
+
|
|
336
|
+
function VirtualItemBridge({ itemId }: { itemId: string }) {
|
|
337
|
+
const state = useContext(VirtualListContext);
|
|
338
|
+
const entry = state?.entriesById.get(itemId);
|
|
339
|
+
const item = state?.data.get(itemId);
|
|
340
|
+
if (!state || !entry || !item) return null;
|
|
341
|
+
return (
|
|
342
|
+
<VirtualListCell
|
|
343
|
+
{...state.cellProps}
|
|
344
|
+
slotSize={state.slotSize(entry)}
|
|
345
|
+
item={item}
|
|
346
|
+
index={entry.index}
|
|
347
|
+
start={entry.start}
|
|
348
|
+
size={entry.size}
|
|
349
|
+
isDragging={state.activeItemId === itemId}
|
|
350
|
+
/>
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Context updates reach the bridge even when FlatList's native cell wrapper
|
|
355
|
+
// skips rendering. Geometry and getItemLayout use the same React snapshot.
|
|
356
|
+
const renderVirtualItem = ({ item }: { item: string }) => (
|
|
357
|
+
<VirtualItemBridge itemId={item} />
|
|
358
|
+
);
|
|
359
|
+
// Our pinned RN 0.87 FlatList supports this in its Flow implementation, but
|
|
360
|
+
// omits it from FlatListProps.d.ts. Its default renderer recreates a callback
|
|
361
|
+
// on every metrics update; strictMode memoizes that internal callback too.
|
|
362
|
+
const FLAT_LIST_RENDERER_OPTIONS = { strictMode: true };
|
|
363
|
+
|
|
364
|
+
const EMPTY_ENTRIES: ListLayoutEntry[] = [];
|
|
365
|
+
const EMPTY_IDS: string[] = [];
|
|
366
|
+
const itemKey = (itemId: string) => itemId;
|
|
367
|
+
type PreparedListDisplay = {
|
|
368
|
+
zone: ListLayoutZone;
|
|
369
|
+
revision: number;
|
|
370
|
+
measurementVersion: number;
|
|
371
|
+
layout: ListLayoutResult;
|
|
372
|
+
presentation: DndZonePresentation | null;
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
type RetainSlotsInputs = {
|
|
376
|
+
layout: ListLayoutResult | null;
|
|
377
|
+
gap: number;
|
|
378
|
+
windowSize: number;
|
|
379
|
+
viewportLength: number;
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* FlatList mounts cells by slot order, so it can keep committed slots during a
|
|
384
|
+
* same-list preview only when its render window already holds every cell the
|
|
385
|
+
* preview can expose. Slots between the source and the target are displaced
|
|
386
|
+
* by at most the dragged slot (item plus gap), and VirtualizedList renders
|
|
387
|
+
* (windowSize - 1) / 2 viewport lengths beyond each viewport edge. A larger
|
|
388
|
+
* displacement falls back to candidate-order slots for that drag. Items of
|
|
389
|
+
* other lists change this list's membership, which the store resolves itself.
|
|
390
|
+
*/
|
|
391
|
+
function retainsCommittedSlots(
|
|
392
|
+
inputs: RetainSlotsInputs,
|
|
393
|
+
itemId: string,
|
|
394
|
+
): boolean {
|
|
395
|
+
if (!inputs.layout?.valid) return false;
|
|
396
|
+
const entry = inputs.layout.entries.find(
|
|
397
|
+
candidate => candidate.itemId === itemId,
|
|
398
|
+
);
|
|
399
|
+
if (!entry) return true;
|
|
400
|
+
return (
|
|
401
|
+
inputs.viewportLength > 0 &&
|
|
402
|
+
entry.size + inputs.gap <=
|
|
403
|
+
((inputs.windowSize - 1) / 2) * inputs.viewportLength
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/** Shared measured layout for ScrollView and FlatList adapters. */
|
|
408
|
+
export function SortableList<T>({
|
|
409
|
+
zoneId,
|
|
410
|
+
renderItem,
|
|
411
|
+
estimatedItemSize = 48,
|
|
412
|
+
itemSize,
|
|
413
|
+
gap = 0,
|
|
414
|
+
paddingStart = 0,
|
|
415
|
+
paddingEnd = 0,
|
|
416
|
+
autoScroll,
|
|
417
|
+
scrollEnabled = true,
|
|
418
|
+
showsScrollIndicator = true,
|
|
419
|
+
onScrollOffsetChange,
|
|
420
|
+
virtualization = false,
|
|
421
|
+
onLayout,
|
|
422
|
+
style,
|
|
423
|
+
...viewProps
|
|
424
|
+
}: SortableListProps<T>) {
|
|
425
|
+
const runtime = useDndRuntime();
|
|
426
|
+
const { registerZone, unregisterZone, reportIssues } = runtime;
|
|
427
|
+
const windowing = resolveListVirtualization(virtualization);
|
|
428
|
+
const virtualized = windowing.valid && windowing.options !== null;
|
|
429
|
+
// Native slots keep their committed order through a same-list preview and
|
|
430
|
+
// the shared presentation moves the cells, so a candidate change renders
|
|
431
|
+
// nothing here. An index-dependent size recipe must update its native slot
|
|
432
|
+
// lengths too. FlatList keeps committed slots only while its render window
|
|
433
|
+
// covers the preview's displacement; the rule answers once per drag session.
|
|
434
|
+
const retainInputs = useRef<RetainSlotsInputs>({
|
|
435
|
+
layout: null,
|
|
436
|
+
gap: 0,
|
|
437
|
+
windowSize: 0,
|
|
438
|
+
viewportLength: 0,
|
|
439
|
+
});
|
|
440
|
+
const retainSlots = useCallback<RetainSlotsRule>(
|
|
441
|
+
itemId => retainsCommittedSlots(retainInputs.current, itemId),
|
|
442
|
+
[],
|
|
443
|
+
);
|
|
444
|
+
const snapshot = useDndZoneSnapshot(
|
|
445
|
+
runtime,
|
|
446
|
+
zoneId,
|
|
447
|
+
typeof itemSize === 'function' ? false : virtualized ? retainSlots : true,
|
|
448
|
+
);
|
|
449
|
+
const identity = useRef({}).current;
|
|
450
|
+
const [viewportSize, setViewportSize] = useState({ width: 0, height: 0 });
|
|
451
|
+
const [measurementVersion, setMeasurementVersion] = useState(0);
|
|
452
|
+
const zone = snapshot.zone;
|
|
453
|
+
const displayZone = snapshot.display;
|
|
454
|
+
const orientation = zone?.kind === 'list' ? zone.orientation : 'vertical';
|
|
455
|
+
const vertical = orientation === 'vertical';
|
|
456
|
+
const crossSize = vertical ? viewportSize.width : viewportSize.height;
|
|
457
|
+
const items = snapshot.items;
|
|
458
|
+
const revision = snapshot.revision;
|
|
459
|
+
const displayRevision = snapshot.displayRevision;
|
|
460
|
+
const cache = useMemo(
|
|
461
|
+
() => new ListSizeCache(),
|
|
462
|
+
// A new geometry/data context must not reuse old native measurements.
|
|
463
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
464
|
+
[crossSize, orientation, items],
|
|
465
|
+
);
|
|
466
|
+
const measurements = useMemo(
|
|
467
|
+
() => ({
|
|
468
|
+
cache,
|
|
469
|
+
version: 0,
|
|
470
|
+
committedVersion: 0,
|
|
471
|
+
frame: null as number | null,
|
|
472
|
+
active: false,
|
|
473
|
+
}),
|
|
474
|
+
[cache],
|
|
475
|
+
);
|
|
476
|
+
const measurementContext = useRef<{
|
|
477
|
+
cache: ListSizeCache;
|
|
478
|
+
revision: number;
|
|
479
|
+
} | null>(null);
|
|
480
|
+
const scheduleMeasurementRender = useCallback(() => {
|
|
481
|
+
if (
|
|
482
|
+
!measurements.active ||
|
|
483
|
+
measurements.frame !== null ||
|
|
484
|
+
measurements.version <= measurements.committedVersion
|
|
485
|
+
)
|
|
486
|
+
return;
|
|
487
|
+
const frame = requestAnimationFrame(() => {
|
|
488
|
+
// A cancelled native frame can already be queued for RN delivery.
|
|
489
|
+
if (measurements.frame !== frame) return;
|
|
490
|
+
measurements.frame = null;
|
|
491
|
+
if (
|
|
492
|
+
!measurements.active ||
|
|
493
|
+
measurementContext.current?.cache !== measurements.cache ||
|
|
494
|
+
measurements.version <= measurements.committedVersion
|
|
495
|
+
)
|
|
496
|
+
return;
|
|
497
|
+
setMeasurementVersion(current => current + 1);
|
|
498
|
+
});
|
|
499
|
+
measurements.frame = frame;
|
|
500
|
+
}, [measurements]);
|
|
501
|
+
useLayoutEffect(() => {
|
|
502
|
+
measurements.active = true;
|
|
503
|
+
scheduleMeasurementRender();
|
|
504
|
+
return () => {
|
|
505
|
+
measurements.active = false;
|
|
506
|
+
if (measurements.frame !== null) cancelAnimationFrame(measurements.frame);
|
|
507
|
+
measurements.frame = null;
|
|
508
|
+
};
|
|
509
|
+
}, [measurements, scheduleMeasurementRender]);
|
|
510
|
+
const renderedMeasurementVersion = useMemo(
|
|
511
|
+
() => measurements.version,
|
|
512
|
+
// An unrelated render may reuse memoized layout; only a measurement signal
|
|
513
|
+
// guarantees that the layout consumed the newest cached dimensions.
|
|
514
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
515
|
+
[measurements, measurementVersion],
|
|
516
|
+
);
|
|
517
|
+
useLayoutEffect(() => {
|
|
518
|
+
measurements.committedVersion = renderedMeasurementVersion;
|
|
519
|
+
// A callback arriving between render and commit already reserved the next
|
|
520
|
+
// frame. Its newer version must not be acknowledged by this older render.
|
|
521
|
+
}, [measurements, renderedMeasurementVersion]);
|
|
522
|
+
const scrollRef = useAnimatedRef<ScrollView>();
|
|
523
|
+
const offset = useSharedValue(0);
|
|
524
|
+
const { motion, animation, presentation, committedRevision } = runtime;
|
|
525
|
+
// Virtual cells position their content with a React-committed slot offset
|
|
526
|
+
// plus the animated content start. The UI runtime only needs each slot's
|
|
527
|
+
// flow start as the motion fallback target; changed starts of one commit
|
|
528
|
+
// travel together in one UI task, without RN shared-value reads.
|
|
529
|
+
const expectedStarts = useMemo(
|
|
530
|
+
() => ({
|
|
531
|
+
pending: new Map<SharedValue<number>, number>(),
|
|
532
|
+
queued: false,
|
|
533
|
+
active: false,
|
|
534
|
+
}),
|
|
535
|
+
// Native viewports and their cells are replaced when these change.
|
|
536
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
537
|
+
[orientation, virtualized, zoneId],
|
|
538
|
+
);
|
|
539
|
+
const flushExpectedStarts = useCallback(() => {
|
|
540
|
+
expectedStarts.queued = false;
|
|
541
|
+
if (!expectedStarts.active || expectedStarts.pending.size === 0) return;
|
|
542
|
+
const updates = Array.from(
|
|
543
|
+
expectedStarts.pending,
|
|
544
|
+
([shared, start]): ExpectedStartUpdate => ({ shared, start }),
|
|
545
|
+
);
|
|
546
|
+
expectedStarts.pending.clear();
|
|
547
|
+
scheduleOnUI(applyExpectedStarts, updates);
|
|
548
|
+
}, [expectedStarts]);
|
|
549
|
+
const publishExpectedStart = useCallback<PublishExpectedStart>(
|
|
550
|
+
(shared, start) => {
|
|
551
|
+
if (start === null) {
|
|
552
|
+
expectedStarts.pending.delete(shared);
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
expectedStarts.pending.set(shared, start);
|
|
556
|
+
if (expectedStarts.queued) return;
|
|
557
|
+
expectedStarts.queued = true;
|
|
558
|
+
(
|
|
559
|
+
globalThis as typeof globalThis & {
|
|
560
|
+
queueMicrotask(callback: () => void): void;
|
|
561
|
+
}
|
|
562
|
+
).queueMicrotask(flushExpectedStarts);
|
|
563
|
+
},
|
|
564
|
+
[expectedStarts, flushExpectedStarts],
|
|
565
|
+
);
|
|
566
|
+
useLayoutEffect(() => {
|
|
567
|
+
expectedStarts.active = true;
|
|
568
|
+
return () => {
|
|
569
|
+
expectedStarts.active = false;
|
|
570
|
+
expectedStarts.pending.clear();
|
|
571
|
+
};
|
|
572
|
+
}, [expectedStarts]);
|
|
573
|
+
const prepareLayout = useMemo(() => {
|
|
574
|
+
let previous: PreparedListDisplay | undefined;
|
|
575
|
+
let entries = new Map<string, ListLayoutEntry>();
|
|
576
|
+
return (candidate: ListLayoutZone, candidateRevision: number) => {
|
|
577
|
+
if (candidate.orientation !== orientation || crossSize <= 0) return null;
|
|
578
|
+
if (
|
|
579
|
+
previous?.zone === candidate &&
|
|
580
|
+
previous.revision === candidateRevision &&
|
|
581
|
+
previous.measurementVersion === measurements.version
|
|
582
|
+
)
|
|
583
|
+
return previous;
|
|
584
|
+
const layout = computeListLayout({
|
|
585
|
+
itemIds: candidate.itemIds,
|
|
586
|
+
orientation,
|
|
587
|
+
crossSize,
|
|
588
|
+
estimatedItemSize,
|
|
589
|
+
itemSize,
|
|
590
|
+
gap,
|
|
591
|
+
paddingStart,
|
|
592
|
+
paddingEnd,
|
|
593
|
+
revision: candidateRevision,
|
|
594
|
+
cache,
|
|
595
|
+
});
|
|
596
|
+
if (layout.valid) {
|
|
597
|
+
const nextEntries = new Map<string, ListLayoutEntry>();
|
|
598
|
+
layout.entries = layout.entries.map(entry => {
|
|
599
|
+
const old = entries.get(entry.itemId);
|
|
600
|
+
const sameRect =
|
|
601
|
+
old &&
|
|
602
|
+
old.rect.x === entry.rect.x &&
|
|
603
|
+
old.rect.y === entry.rect.y &&
|
|
604
|
+
old.rect.width === entry.rect.width &&
|
|
605
|
+
old.rect.height === entry.rect.height;
|
|
606
|
+
// Rects contain only coordinates. Preserve their immutable identity
|
|
607
|
+
// across revision/source changes so UI need not receive them again;
|
|
608
|
+
// the new entry still carries its current index and revision.
|
|
609
|
+
if (sameRect) entry.rect = old.rect;
|
|
610
|
+
const same =
|
|
611
|
+
old &&
|
|
612
|
+
old.index === entry.index &&
|
|
613
|
+
old.start === entry.start &&
|
|
614
|
+
old.size === entry.size &&
|
|
615
|
+
old.source === entry.source &&
|
|
616
|
+
old.revision === entry.revision &&
|
|
617
|
+
sameRect;
|
|
618
|
+
const retained = same ? old : entry;
|
|
619
|
+
nextEntries.set(entry.itemId, retained);
|
|
620
|
+
return retained;
|
|
621
|
+
});
|
|
622
|
+
entries = nextEntries;
|
|
623
|
+
}
|
|
624
|
+
previous = {
|
|
625
|
+
zone: candidate,
|
|
626
|
+
revision: candidateRevision,
|
|
627
|
+
measurementVersion: measurements.version,
|
|
628
|
+
layout,
|
|
629
|
+
// Candidates of one item set share the slot map; only coordinates cross
|
|
630
|
+
// to UI again.
|
|
631
|
+
presentation: layout.valid
|
|
632
|
+
? createZonePresentation(
|
|
633
|
+
layout.entries.map(entry => [entry.itemId, entry.rect] as const),
|
|
634
|
+
previous?.presentation,
|
|
635
|
+
)
|
|
636
|
+
: null,
|
|
637
|
+
};
|
|
638
|
+
return previous;
|
|
639
|
+
};
|
|
640
|
+
}, [
|
|
641
|
+
cache,
|
|
642
|
+
crossSize,
|
|
643
|
+
estimatedItemSize,
|
|
644
|
+
gap,
|
|
645
|
+
itemSize,
|
|
646
|
+
measurements,
|
|
647
|
+
orientation,
|
|
648
|
+
paddingEnd,
|
|
649
|
+
paddingStart,
|
|
650
|
+
]);
|
|
651
|
+
const prepareDisplay = useCallback(
|
|
652
|
+
(candidate: ListLayoutZone, candidateRevision: number) =>
|
|
653
|
+
prepareLayout(candidate, candidateRevision)?.presentation ?? null,
|
|
654
|
+
[prepareLayout],
|
|
655
|
+
);
|
|
656
|
+
const base = useMemo(
|
|
657
|
+
() =>
|
|
658
|
+
zone?.kind === 'list'
|
|
659
|
+
? (prepareLayout(zone, revision)?.layout ?? null)
|
|
660
|
+
: null,
|
|
661
|
+
// Cache writes are mutable; the version explicitly invalidates this result.
|
|
662
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
663
|
+
[zone, revision, prepareLayout, measurementVersion],
|
|
664
|
+
);
|
|
665
|
+
const viewportLength = vertical ? viewportSize.height : viewportSize.width;
|
|
666
|
+
const windowSize = windowing.valid ? (windowing.options?.windowSize ?? 0) : 0;
|
|
667
|
+
useLayoutEffect(() => {
|
|
668
|
+
// Read by the retain rule when a drag starts. The committed layout does
|
|
669
|
+
// not change between idle and the first preview of a session.
|
|
670
|
+
retainInputs.current = { layout: base, gap, windowSize, viewportLength };
|
|
671
|
+
}, [base, gap, viewportLength, windowSize]);
|
|
672
|
+
const display = useMemo(
|
|
673
|
+
() =>
|
|
674
|
+
displayZone === zone
|
|
675
|
+
? base
|
|
676
|
+
: displayZone?.kind === 'list'
|
|
677
|
+
? (prepareLayout(displayZone, displayRevision)?.layout ?? null)
|
|
678
|
+
: null,
|
|
679
|
+
// Candidate rects must also be recomputed after an accepted measurement.
|
|
680
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
681
|
+
[
|
|
682
|
+
displayZone,
|
|
683
|
+
zone,
|
|
684
|
+
base,
|
|
685
|
+
displayRevision,
|
|
686
|
+
prepareLayout,
|
|
687
|
+
measurementVersion,
|
|
688
|
+
],
|
|
689
|
+
);
|
|
690
|
+
const validScroll =
|
|
691
|
+
validateAutoScrollOptions(autoScroll).valid &&
|
|
692
|
+
typeof scrollEnabled === 'boolean';
|
|
693
|
+
useLayoutEffect(() => {
|
|
694
|
+
measurementContext.current = { cache, revision };
|
|
695
|
+
return () => {
|
|
696
|
+
measurementContext.current = null;
|
|
697
|
+
};
|
|
698
|
+
}, [cache, revision]);
|
|
699
|
+
useLayoutEffect(() => {
|
|
700
|
+
// Switching adapter/direction remounts the native viewport.
|
|
701
|
+
offset.set(0);
|
|
702
|
+
return () => unregisterZone(zoneId, identity);
|
|
703
|
+
}, [identity, offset, orientation, unregisterZone, virtualized, zoneId]);
|
|
704
|
+
useLayoutEffect(() => {
|
|
705
|
+
reportIssues(
|
|
706
|
+
`list:${zoneId}`,
|
|
707
|
+
zone?.kind !== 'list'
|
|
708
|
+
? [
|
|
709
|
+
{
|
|
710
|
+
code: 'invalid-configuration',
|
|
711
|
+
zoneId,
|
|
712
|
+
message: 'SortableList requires an existing list zone.',
|
|
713
|
+
},
|
|
714
|
+
]
|
|
715
|
+
: !windowing.valid
|
|
716
|
+
? windowing.issues
|
|
717
|
+
: !validScroll
|
|
718
|
+
? [
|
|
719
|
+
{
|
|
720
|
+
code: 'invalid-configuration',
|
|
721
|
+
zoneId,
|
|
722
|
+
message: 'List scrolling options are invalid.',
|
|
723
|
+
},
|
|
724
|
+
]
|
|
725
|
+
: base && !base.valid
|
|
726
|
+
? base.issues
|
|
727
|
+
: [],
|
|
728
|
+
);
|
|
729
|
+
});
|
|
730
|
+
useLayoutEffect(() => {
|
|
731
|
+
if (!base?.valid || !display?.valid || !validScroll || !windowing.valid) {
|
|
732
|
+
unregisterZone(zoneId, identity);
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
registerZone(
|
|
736
|
+
{
|
|
737
|
+
kind: 'list',
|
|
738
|
+
zoneId,
|
|
739
|
+
orientation,
|
|
740
|
+
revision,
|
|
741
|
+
ref: scrollRef as unknown as AnimatedRef<HostInstance>,
|
|
742
|
+
offset,
|
|
743
|
+
contentSize: display.totalSize,
|
|
744
|
+
autoScroll: {
|
|
745
|
+
...autoScroll,
|
|
746
|
+
enabled: scrollEnabled && autoScroll?.enabled !== false,
|
|
747
|
+
},
|
|
748
|
+
layout: base,
|
|
749
|
+
dataIdentity: items,
|
|
750
|
+
prepareDisplay,
|
|
751
|
+
renderItem: args => renderItem(args as ListItemRenderArgs<T>),
|
|
752
|
+
},
|
|
753
|
+
identity,
|
|
754
|
+
);
|
|
755
|
+
}, [
|
|
756
|
+
registerZone,
|
|
757
|
+
unregisterZone,
|
|
758
|
+
zoneId,
|
|
759
|
+
orientation,
|
|
760
|
+
revision,
|
|
761
|
+
scrollRef,
|
|
762
|
+
offset,
|
|
763
|
+
display,
|
|
764
|
+
base,
|
|
765
|
+
autoScroll,
|
|
766
|
+
validScroll,
|
|
767
|
+
scrollEnabled,
|
|
768
|
+
renderItem,
|
|
769
|
+
identity,
|
|
770
|
+
windowing.valid,
|
|
771
|
+
virtualized,
|
|
772
|
+
prepareDisplay,
|
|
773
|
+
items,
|
|
774
|
+
]);
|
|
775
|
+
useLayoutEffect(
|
|
776
|
+
() => () => {
|
|
777
|
+
unregisterZone(zoneId, identity);
|
|
778
|
+
reportIssues(`list:${zoneId}`, []);
|
|
779
|
+
},
|
|
780
|
+
[zoneId, identity, unregisterZone, reportIssues],
|
|
781
|
+
);
|
|
782
|
+
const onSize = useCallback(
|
|
783
|
+
(itemId: string, size: number) => {
|
|
784
|
+
if (
|
|
785
|
+
measurementContext.current?.cache !== cache ||
|
|
786
|
+
measurementContext.current.revision !== revision
|
|
787
|
+
)
|
|
788
|
+
return;
|
|
789
|
+
const previous = cache.get(itemId, { orientation, crossSize });
|
|
790
|
+
if (previous !== undefined && Math.abs(previous - size) < 0.001) return;
|
|
791
|
+
if (
|
|
792
|
+
cache.set(itemId, size, { orientation, crossSize }, revision) &&
|
|
793
|
+
previous !== size
|
|
794
|
+
) {
|
|
795
|
+
// Invalidate before React renders, since the Provider may ask for a
|
|
796
|
+
// validated presentation while this native measurement is still batched.
|
|
797
|
+
measurements.version++;
|
|
798
|
+
scheduleMeasurementRender();
|
|
799
|
+
}
|
|
800
|
+
},
|
|
801
|
+
[
|
|
802
|
+
cache,
|
|
803
|
+
orientation,
|
|
804
|
+
crossSize,
|
|
805
|
+
revision,
|
|
806
|
+
measurements,
|
|
807
|
+
scheduleMeasurementRender,
|
|
808
|
+
],
|
|
809
|
+
);
|
|
810
|
+
const cellMeasurements = useMemo(
|
|
811
|
+
() => ({ revision, onSize }),
|
|
812
|
+
[revision, onSize],
|
|
813
|
+
);
|
|
814
|
+
const scrollHandler = useAnimatedScrollHandler({
|
|
815
|
+
onScroll: event => {
|
|
816
|
+
const nextOffset = vertical
|
|
817
|
+
? event.contentOffset.y
|
|
818
|
+
: event.contentOffset.x;
|
|
819
|
+
offset.set(nextOffset);
|
|
820
|
+
if (onScrollOffsetChange)
|
|
821
|
+
scheduleOnRN(onScrollOffsetChange, {
|
|
822
|
+
x: event.contentOffset.x,
|
|
823
|
+
y: event.contentOffset.y,
|
|
824
|
+
});
|
|
825
|
+
},
|
|
826
|
+
});
|
|
827
|
+
const handleLayout = useCallback(
|
|
828
|
+
(event: LayoutChangeEvent) => {
|
|
829
|
+
const { width, height } = event.nativeEvent.layout;
|
|
830
|
+
setViewportSize(current =>
|
|
831
|
+
current.width === width && current.height === height
|
|
832
|
+
? current
|
|
833
|
+
: { width, height },
|
|
834
|
+
);
|
|
835
|
+
onLayout?.(event);
|
|
836
|
+
},
|
|
837
|
+
[onLayout],
|
|
838
|
+
);
|
|
839
|
+
const data = useMemo(
|
|
840
|
+
() => new Map(items?.map(item => [item.id, item]) ?? []),
|
|
841
|
+
[items],
|
|
842
|
+
);
|
|
843
|
+
const contentSize = display?.valid ? display.totalSize : 0;
|
|
844
|
+
const visibleEntries = display?.valid ? display.entries : EMPTY_ENTRIES;
|
|
845
|
+
const visibleIds =
|
|
846
|
+
display?.valid && displayZone?.kind === 'list'
|
|
847
|
+
? displayZone.itemIds
|
|
848
|
+
: EMPTY_IDS;
|
|
849
|
+
const entriesById = useMemo(
|
|
850
|
+
() => new Map(visibleEntries.map(entry => [entry.itemId, entry])),
|
|
851
|
+
[visibleEntries],
|
|
852
|
+
);
|
|
853
|
+
const entryCount = visibleEntries.length;
|
|
854
|
+
const slotSize = useCallback(
|
|
855
|
+
(entry: ListLayoutEntry) =>
|
|
856
|
+
entry.size + (entry.index < entryCount - 1 ? gap : 0),
|
|
857
|
+
[entryCount, gap],
|
|
858
|
+
);
|
|
859
|
+
const getItemLayout = useCallback(
|
|
860
|
+
(_itemIds: ArrayLike<string> | null | undefined, index: number) => {
|
|
861
|
+
const entry = visibleEntries[index];
|
|
862
|
+
return { index, offset: entry.start, length: slotSize(entry) };
|
|
863
|
+
},
|
|
864
|
+
[slotSize, visibleEntries],
|
|
865
|
+
);
|
|
866
|
+
// Cells only need the active source's visibility, not the pointer and all
|
|
867
|
+
// measured viewports copied into motion on every frame.
|
|
868
|
+
const hiddenItemId = useDerivedValue(() => {
|
|
869
|
+
const state = motion.get();
|
|
870
|
+
return state.visible ? state.itemId : null;
|
|
871
|
+
});
|
|
872
|
+
const activeItemId = snapshot.activeItemId;
|
|
873
|
+
const fixed = itemSize !== undefined;
|
|
874
|
+
const virtualRenderState = useMemo<VirtualListRenderState>(
|
|
875
|
+
() => ({
|
|
876
|
+
entriesById,
|
|
877
|
+
data,
|
|
878
|
+
activeItemId,
|
|
879
|
+
slotSize,
|
|
880
|
+
cellProps: {
|
|
881
|
+
publishExpectedStart,
|
|
882
|
+
zoneId,
|
|
883
|
+
committedRevision,
|
|
884
|
+
orientation,
|
|
885
|
+
crossSize,
|
|
886
|
+
fixed,
|
|
887
|
+
measurementIdentity: cache,
|
|
888
|
+
renderItem,
|
|
889
|
+
hiddenItemId,
|
|
890
|
+
animation,
|
|
891
|
+
presentation,
|
|
892
|
+
},
|
|
893
|
+
}),
|
|
894
|
+
[
|
|
895
|
+
activeItemId,
|
|
896
|
+
animation,
|
|
897
|
+
cache,
|
|
898
|
+
committedRevision,
|
|
899
|
+
crossSize,
|
|
900
|
+
data,
|
|
901
|
+
entriesById,
|
|
902
|
+
fixed,
|
|
903
|
+
hiddenItemId,
|
|
904
|
+
orientation,
|
|
905
|
+
renderItem,
|
|
906
|
+
slotSize,
|
|
907
|
+
zoneId,
|
|
908
|
+
presentation,
|
|
909
|
+
publishExpectedStart,
|
|
910
|
+
],
|
|
911
|
+
);
|
|
912
|
+
const contentContainerStyle = useMemo(
|
|
913
|
+
() =>
|
|
914
|
+
vertical
|
|
915
|
+
? { paddingTop: paddingStart, paddingBottom: paddingEnd }
|
|
916
|
+
: { paddingLeft: paddingStart, paddingRight: paddingEnd },
|
|
917
|
+
[paddingEnd, paddingStart, vertical],
|
|
918
|
+
);
|
|
919
|
+
if (virtualized && windowing.valid && windowing.options) {
|
|
920
|
+
// These are actual native flow slots, including the trailing inter-item gap.
|
|
921
|
+
// The natural-size child is absolute so an estimate cannot constrain its
|
|
922
|
+
// measurement. Cache updates change both native slots and getItemLayout.
|
|
923
|
+
return (
|
|
924
|
+
<ListMeasurementContext value={cellMeasurements}>
|
|
925
|
+
<VirtualListContext value={virtualRenderState}>
|
|
926
|
+
<Animated.FlatList<string>
|
|
927
|
+
{...viewProps}
|
|
928
|
+
{...windowing.options}
|
|
929
|
+
{...FLAT_LIST_RENDERER_OPTIONS}
|
|
930
|
+
key={`${zoneId}:${orientation}:flat`}
|
|
931
|
+
ref={scrollRef as unknown as AnimatedRef<FlatList<string>>}
|
|
932
|
+
data={visibleIds}
|
|
933
|
+
keyExtractor={itemKey}
|
|
934
|
+
horizontal={!vertical}
|
|
935
|
+
style={style}
|
|
936
|
+
contentContainerStyle={contentContainerStyle}
|
|
937
|
+
onLayout={handleLayout}
|
|
938
|
+
onScroll={scrollHandler}
|
|
939
|
+
scrollEventThrottle={16}
|
|
940
|
+
scrollEnabled={scrollEnabled && snapshot.scrollEnabled}
|
|
941
|
+
removeClippedSubviews={false}
|
|
942
|
+
showsVerticalScrollIndicator={showsScrollIndicator}
|
|
943
|
+
showsHorizontalScrollIndicator={showsScrollIndicator}
|
|
944
|
+
contentInsetAdjustmentBehavior="never"
|
|
945
|
+
getItemLayout={getItemLayout}
|
|
946
|
+
renderItem={renderVirtualItem}
|
|
947
|
+
/>
|
|
948
|
+
</VirtualListContext>
|
|
949
|
+
</ListMeasurementContext>
|
|
950
|
+
);
|
|
951
|
+
}
|
|
952
|
+
const entries = new Map(
|
|
953
|
+
display?.valid ? display.entries.map(entry => [entry.itemId, entry]) : [],
|
|
954
|
+
);
|
|
955
|
+
// The ScrollView adapter retains source cells through cross-list previews.
|
|
956
|
+
// FlatList may unmount them; the provider owns the gesture and overlay.
|
|
957
|
+
const renderIds = new Set([
|
|
958
|
+
...(zone?.kind === 'list' ? zone.itemIds : []),
|
|
959
|
+
...(displayZone?.kind === 'list' ? displayZone.itemIds : []),
|
|
960
|
+
]);
|
|
961
|
+
return (
|
|
962
|
+
<ListMeasurementContext value={cellMeasurements}>
|
|
963
|
+
<Animated.ScrollView
|
|
964
|
+
{...viewProps}
|
|
965
|
+
key={`${zoneId}:${orientation}:scroll`}
|
|
966
|
+
ref={scrollRef}
|
|
967
|
+
horizontal={!vertical}
|
|
968
|
+
style={style}
|
|
969
|
+
onLayout={handleLayout}
|
|
970
|
+
onScroll={scrollHandler}
|
|
971
|
+
scrollEventThrottle={16}
|
|
972
|
+
scrollEnabled={scrollEnabled && snapshot.scrollEnabled}
|
|
973
|
+
removeClippedSubviews={false}
|
|
974
|
+
showsVerticalScrollIndicator={showsScrollIndicator}
|
|
975
|
+
showsHorizontalScrollIndicator={showsScrollIndicator}
|
|
976
|
+
contentInsetAdjustmentBehavior="never"
|
|
977
|
+
>
|
|
978
|
+
<View
|
|
979
|
+
style={
|
|
980
|
+
vertical
|
|
981
|
+
? { width: crossSize, height: contentSize }
|
|
982
|
+
: [
|
|
983
|
+
styles.horizontalSlot,
|
|
984
|
+
{ height: crossSize, width: contentSize },
|
|
985
|
+
]
|
|
986
|
+
}
|
|
987
|
+
>
|
|
988
|
+
{base?.valid &&
|
|
989
|
+
display?.valid &&
|
|
990
|
+
Array.from(renderIds).map(itemId => {
|
|
991
|
+
const item = data.get(itemId);
|
|
992
|
+
const entry =
|
|
993
|
+
entries.get(itemId) ??
|
|
994
|
+
base.entries.find(candidate => candidate.itemId === itemId);
|
|
995
|
+
if (!item || !entry) return null;
|
|
996
|
+
return (
|
|
997
|
+
<ListCell
|
|
998
|
+
key={itemId}
|
|
999
|
+
item={item as DndItem<T>}
|
|
1000
|
+
index={entry.index}
|
|
1001
|
+
zoneId={zoneId}
|
|
1002
|
+
committedRevision={committedRevision}
|
|
1003
|
+
start={entry.start}
|
|
1004
|
+
size={entry.size}
|
|
1005
|
+
orientation={orientation}
|
|
1006
|
+
crossSize={crossSize}
|
|
1007
|
+
fixed={fixed}
|
|
1008
|
+
measurementIdentity={cache}
|
|
1009
|
+
renderItem={renderItem}
|
|
1010
|
+
hiddenItemId={hiddenItemId}
|
|
1011
|
+
animation={animation}
|
|
1012
|
+
presentation={presentation}
|
|
1013
|
+
isDragging={activeItemId === item.id}
|
|
1014
|
+
/>
|
|
1015
|
+
);
|
|
1016
|
+
})}
|
|
1017
|
+
</View>
|
|
1018
|
+
</Animated.ScrollView>
|
|
1019
|
+
</ListMeasurementContext>
|
|
1020
|
+
);
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
const styles = StyleSheet.create({
|
|
1024
|
+
cell: { position: 'absolute', left: 0, top: 0 },
|
|
1025
|
+
// Yoga otherwise fits an auto-width absolute child to a column parent's
|
|
1026
|
+
// estimated width. A row leaves the measured main axis unconstrained.
|
|
1027
|
+
horizontalSlot: { flexDirection: 'row' },
|
|
1028
|
+
});
|