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,714 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { createContext, memo, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState, createElement as _createElement } from 'react';
|
|
4
|
+
import { StyleSheet, View } from 'react-native';
|
|
5
|
+
import Animated, { makeMutable, useAnimatedRef, useAnimatedScrollHandler, useAnimatedStyle, useDerivedValue, useSharedValue } from 'react-native-reanimated';
|
|
6
|
+
import { scheduleOnRN, scheduleOnUI } from 'react-native-worklets';
|
|
7
|
+
import { validateAutoScrollOptions } from "../adapters/scroll.js";
|
|
8
|
+
import { resolveListVirtualization } from "../adapters/virtualizedList.js";
|
|
9
|
+
import { computeListLayout, ListSizeCache } from "../engine/list.js";
|
|
10
|
+
import { DndItemContext, useDndRuntime, useDndZoneSnapshot, useDndItemSnapshot } from "../runtime/dndRuntime.js";
|
|
11
|
+
import { createZonePresentation } from "../runtime/dndPresentation.js";
|
|
12
|
+
import { useListCellMotion } from "../pointer/listCellMotion.js";
|
|
13
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
14
|
+
const ListMeasurementContext = /*#__PURE__*/createContext(null);
|
|
15
|
+
function ListCellContent({
|
|
16
|
+
item,
|
|
17
|
+
index,
|
|
18
|
+
zoneId,
|
|
19
|
+
committedRevision,
|
|
20
|
+
start,
|
|
21
|
+
size,
|
|
22
|
+
orientation,
|
|
23
|
+
crossSize,
|
|
24
|
+
fixed,
|
|
25
|
+
measurementIdentity,
|
|
26
|
+
slotStart,
|
|
27
|
+
expectedStart,
|
|
28
|
+
renderItem,
|
|
29
|
+
hiddenItemId,
|
|
30
|
+
animation,
|
|
31
|
+
presentation,
|
|
32
|
+
isDragging
|
|
33
|
+
}) {
|
|
34
|
+
const measurement = useContext(ListMeasurementContext);
|
|
35
|
+
if (!measurement) throw new Error('ListCell requires its measurement context.');
|
|
36
|
+
const {
|
|
37
|
+
revision,
|
|
38
|
+
onSize
|
|
39
|
+
} = measurement;
|
|
40
|
+
const ref = useAnimatedRef();
|
|
41
|
+
const mounted = useRef(false);
|
|
42
|
+
useLayoutEffect(() => {
|
|
43
|
+
mounted.current = true;
|
|
44
|
+
return () => {
|
|
45
|
+
mounted.current = false;
|
|
46
|
+
};
|
|
47
|
+
}, []);
|
|
48
|
+
const itemId = item.id;
|
|
49
|
+
const runtime = useDndRuntime();
|
|
50
|
+
const publicSlot = useDndItemSnapshot(runtime, zoneId, itemId);
|
|
51
|
+
const itemContext = useMemo(() => ({
|
|
52
|
+
itemId,
|
|
53
|
+
zoneId,
|
|
54
|
+
ref
|
|
55
|
+
}), [itemId, zoneId, ref]);
|
|
56
|
+
const publicIndex = publicSlot?.index ?? index;
|
|
57
|
+
const content = useMemo(() => renderItem({
|
|
58
|
+
item,
|
|
59
|
+
index: publicIndex,
|
|
60
|
+
zoneId,
|
|
61
|
+
isDragging
|
|
62
|
+
}), [item, publicIndex, zoneId, isDragging, renderItem]);
|
|
63
|
+
const vertical = orientation === 'vertical';
|
|
64
|
+
const revisionSource = committedRevision ?? revision;
|
|
65
|
+
// Mount uses the Provider's JS view of the scheduled UI geometry. Reading the
|
|
66
|
+
// shared presentation from JS would block on the UI runtime and copy every
|
|
67
|
+
// rect for each new cell; the UI mapper adopts actual UI geometry first.
|
|
68
|
+
const {
|
|
69
|
+
animatedStart,
|
|
70
|
+
targetSize
|
|
71
|
+
} = useListCellMotion({
|
|
72
|
+
zoneId,
|
|
73
|
+
itemId,
|
|
74
|
+
vertical,
|
|
75
|
+
fixed,
|
|
76
|
+
start: expectedStart ?? start,
|
|
77
|
+
size,
|
|
78
|
+
presentation,
|
|
79
|
+
revision: revisionSource,
|
|
80
|
+
hiddenItemId,
|
|
81
|
+
animation,
|
|
82
|
+
initial: {
|
|
83
|
+
presentation: runtime.presentationSnapshot?.(),
|
|
84
|
+
revision: typeof revisionSource === 'number' ? revisionSource : runtime.snapshot.value?.revision ?? 0,
|
|
85
|
+
start
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
// Natural-size cells never consume a target dimension; only authoritative
|
|
89
|
+
// sizes read the validated size, so measurements do not restart this mapper.
|
|
90
|
+
// The transform is the content start itself; the slot offset below cancels
|
|
91
|
+
// the native flow position, so no per-frame slot measurement is involved.
|
|
92
|
+
const animated = useAnimatedStyle(() => ({
|
|
93
|
+
opacity: hiddenItemId.get() === itemId ? 0 : 1,
|
|
94
|
+
...(fixed ? vertical ? {
|
|
95
|
+
height: targetSize.get()
|
|
96
|
+
} : {
|
|
97
|
+
width: targetSize.get()
|
|
98
|
+
} : {}),
|
|
99
|
+
transform: [{
|
|
100
|
+
translateX: vertical ? 0 : animatedStart.get()
|
|
101
|
+
}, {
|
|
102
|
+
translateY: vertical ? animatedStart.get() : 0
|
|
103
|
+
}]
|
|
104
|
+
}));
|
|
105
|
+
const measured = useCallback((width, height) => {
|
|
106
|
+
if (mounted.current && !fixed) onSize(itemId, vertical ? height : width);
|
|
107
|
+
}, [fixed, itemId, onSize, vertical]);
|
|
108
|
+
const measurementRequest = useRef(null);
|
|
109
|
+
useEffect(() => {
|
|
110
|
+
const previous = measurementRequest.current;
|
|
111
|
+
const request = {
|
|
112
|
+
identity: measurementIdentity,
|
|
113
|
+
itemId,
|
|
114
|
+
fixed,
|
|
115
|
+
pending: !fixed && previous !== null && (previous.identity !== measurementIdentity || previous.itemId !== itemId || previous.fixed || previous.pending)
|
|
116
|
+
};
|
|
117
|
+
measurementRequest.current = request;
|
|
118
|
+
// A new native cell reports its initial size through onLayout. Asking for
|
|
119
|
+
// the same measurement here doubles native callbacks while windowing.
|
|
120
|
+
// An existing cell needs a fallback only when its cache context changes,
|
|
121
|
+
// since unchanged native dimensions may not produce another onLayout.
|
|
122
|
+
if (!request.pending) return;
|
|
123
|
+
let current = true;
|
|
124
|
+
ref.current?.measure((_x, _y, width, height) => {
|
|
125
|
+
if (current) {
|
|
126
|
+
request.pending = false;
|
|
127
|
+
measured(width, height);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
return () => {
|
|
131
|
+
current = false;
|
|
132
|
+
};
|
|
133
|
+
}, [fixed, itemId, measured, measurementIdentity, ref]);
|
|
134
|
+
return /*#__PURE__*/_jsx(DndItemContext, {
|
|
135
|
+
value: itemContext,
|
|
136
|
+
children: /*#__PURE__*/_jsx(Animated.View, {
|
|
137
|
+
ref: ref,
|
|
138
|
+
collapsable: false,
|
|
139
|
+
style: [styles.cell, vertical ? {
|
|
140
|
+
width: crossSize,
|
|
141
|
+
...(slotStart !== undefined ? {
|
|
142
|
+
top: negate(slotStart)
|
|
143
|
+
} : {}),
|
|
144
|
+
...(fixed ? {
|
|
145
|
+
height: size
|
|
146
|
+
} : {})
|
|
147
|
+
} : {
|
|
148
|
+
height: crossSize,
|
|
149
|
+
...(slotStart !== undefined ? {
|
|
150
|
+
left: negate(slotStart)
|
|
151
|
+
} : {}),
|
|
152
|
+
...(fixed ? {
|
|
153
|
+
width: size
|
|
154
|
+
} : {})
|
|
155
|
+
}, animated],
|
|
156
|
+
onLayout: event => measured(event.nativeEvent.layout.width, event.nativeEvent.layout.height),
|
|
157
|
+
children: content
|
|
158
|
+
})
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Pointer/session updates must not rerender every mounted card. Pass only the
|
|
163
|
+
// cell's visible state and primitive geometry; entries are rebuilt together
|
|
164
|
+
// when an item is measured or moved, including entries that did not change.
|
|
165
|
+
const ListCell = /*#__PURE__*/memo(ListCellContent);
|
|
166
|
+
|
|
167
|
+
/** Queue a slot's new flow start for the UI runtime; `null` drops it. */
|
|
168
|
+
|
|
169
|
+
function applyExpectedStarts(updates) {
|
|
170
|
+
'worklet';
|
|
171
|
+
|
|
172
|
+
for (const update of updates) update.shared.set(update.start);
|
|
173
|
+
}
|
|
174
|
+
function negate(value) {
|
|
175
|
+
return value === 0 ? 0 : -value;
|
|
176
|
+
}
|
|
177
|
+
function VirtualListCellContent({
|
|
178
|
+
publishExpectedStart,
|
|
179
|
+
slotSize,
|
|
180
|
+
...cellProps
|
|
181
|
+
}) {
|
|
182
|
+
const style = useMemo(() => cellProps.orientation === 'vertical' ? {
|
|
183
|
+
width: cellProps.crossSize,
|
|
184
|
+
height: slotSize
|
|
185
|
+
} : [styles.horizontalSlot, {
|
|
186
|
+
height: cellProps.crossSize,
|
|
187
|
+
width: slotSize
|
|
188
|
+
}], [cellProps.orientation, cellProps.crossSize, slotSize]);
|
|
189
|
+
// The flow start is only ever assigned plain numbers. A plain mutable skips
|
|
190
|
+
// the per-value cancelAnimation UI task that useSharedValue schedules on
|
|
191
|
+
// unmount. It feeds the cell's motion fallback target; positioning itself
|
|
192
|
+
// uses the React-committed slot offset, so nothing waits for a native measure.
|
|
193
|
+
const [expectedStart] = useState(() => makeMutable(cellProps.start));
|
|
194
|
+
const published = useRef(cellProps.start);
|
|
195
|
+
useLayoutEffect(() => {
|
|
196
|
+
if (published.current === cellProps.start) return;
|
|
197
|
+
published.current = cellProps.start;
|
|
198
|
+
publishExpectedStart(expectedStart, cellProps.start);
|
|
199
|
+
}, [cellProps.start, expectedStart, publishExpectedStart]);
|
|
200
|
+
useLayoutEffect(() => () => publishExpectedStart(expectedStart, null), [expectedStart, publishExpectedStart]);
|
|
201
|
+
return /*#__PURE__*/_jsx(View, {
|
|
202
|
+
collapsable: false,
|
|
203
|
+
style: style,
|
|
204
|
+
children: /*#__PURE__*/_jsx(ListCell, {
|
|
205
|
+
...cellProps,
|
|
206
|
+
slotStart: cellProps.start,
|
|
207
|
+
expectedStart: expectedStart
|
|
208
|
+
})
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
const VirtualListCell = /*#__PURE__*/memo(VirtualListCellContent);
|
|
212
|
+
const VirtualListContext = /*#__PURE__*/createContext(null);
|
|
213
|
+
function VirtualItemBridge({
|
|
214
|
+
itemId
|
|
215
|
+
}) {
|
|
216
|
+
const state = useContext(VirtualListContext);
|
|
217
|
+
const entry = state?.entriesById.get(itemId);
|
|
218
|
+
const item = state?.data.get(itemId);
|
|
219
|
+
if (!state || !entry || !item) return null;
|
|
220
|
+
return /*#__PURE__*/_jsx(VirtualListCell, {
|
|
221
|
+
...state.cellProps,
|
|
222
|
+
slotSize: state.slotSize(entry),
|
|
223
|
+
item: item,
|
|
224
|
+
index: entry.index,
|
|
225
|
+
start: entry.start,
|
|
226
|
+
size: entry.size,
|
|
227
|
+
isDragging: state.activeItemId === itemId
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Context updates reach the bridge even when FlatList's native cell wrapper
|
|
232
|
+
// skips rendering. Geometry and getItemLayout use the same React snapshot.
|
|
233
|
+
const renderVirtualItem = ({
|
|
234
|
+
item
|
|
235
|
+
}) => /*#__PURE__*/_jsx(VirtualItemBridge, {
|
|
236
|
+
itemId: item
|
|
237
|
+
});
|
|
238
|
+
// Our pinned RN 0.87 FlatList supports this in its Flow implementation, but
|
|
239
|
+
// omits it from FlatListProps.d.ts. Its default renderer recreates a callback
|
|
240
|
+
// on every metrics update; strictMode memoizes that internal callback too.
|
|
241
|
+
const FLAT_LIST_RENDERER_OPTIONS = {
|
|
242
|
+
strictMode: true
|
|
243
|
+
};
|
|
244
|
+
const EMPTY_ENTRIES = [];
|
|
245
|
+
const EMPTY_IDS = [];
|
|
246
|
+
const itemKey = itemId => itemId;
|
|
247
|
+
/**
|
|
248
|
+
* FlatList mounts cells by slot order, so it can keep committed slots during a
|
|
249
|
+
* same-list preview only when its render window already holds every cell the
|
|
250
|
+
* preview can expose. Slots between the source and the target are displaced
|
|
251
|
+
* by at most the dragged slot (item plus gap), and VirtualizedList renders
|
|
252
|
+
* (windowSize - 1) / 2 viewport lengths beyond each viewport edge. A larger
|
|
253
|
+
* displacement falls back to candidate-order slots for that drag. Items of
|
|
254
|
+
* other lists change this list's membership, which the store resolves itself.
|
|
255
|
+
*/
|
|
256
|
+
function retainsCommittedSlots(inputs, itemId) {
|
|
257
|
+
if (!inputs.layout?.valid) return false;
|
|
258
|
+
const entry = inputs.layout.entries.find(candidate => candidate.itemId === itemId);
|
|
259
|
+
if (!entry) return true;
|
|
260
|
+
return inputs.viewportLength > 0 && entry.size + inputs.gap <= (inputs.windowSize - 1) / 2 * inputs.viewportLength;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/** Shared measured layout for ScrollView and FlatList adapters. */
|
|
264
|
+
export function SortableList({
|
|
265
|
+
zoneId,
|
|
266
|
+
renderItem,
|
|
267
|
+
estimatedItemSize = 48,
|
|
268
|
+
itemSize,
|
|
269
|
+
gap = 0,
|
|
270
|
+
paddingStart = 0,
|
|
271
|
+
paddingEnd = 0,
|
|
272
|
+
autoScroll,
|
|
273
|
+
scrollEnabled = true,
|
|
274
|
+
showsScrollIndicator = true,
|
|
275
|
+
onScrollOffsetChange,
|
|
276
|
+
virtualization = false,
|
|
277
|
+
onLayout,
|
|
278
|
+
style,
|
|
279
|
+
...viewProps
|
|
280
|
+
}) {
|
|
281
|
+
const runtime = useDndRuntime();
|
|
282
|
+
const {
|
|
283
|
+
registerZone,
|
|
284
|
+
unregisterZone,
|
|
285
|
+
reportIssues
|
|
286
|
+
} = runtime;
|
|
287
|
+
const windowing = resolveListVirtualization(virtualization);
|
|
288
|
+
const virtualized = windowing.valid && windowing.options !== null;
|
|
289
|
+
// Native slots keep their committed order through a same-list preview and
|
|
290
|
+
// the shared presentation moves the cells, so a candidate change renders
|
|
291
|
+
// nothing here. An index-dependent size recipe must update its native slot
|
|
292
|
+
// lengths too. FlatList keeps committed slots only while its render window
|
|
293
|
+
// covers the preview's displacement; the rule answers once per drag session.
|
|
294
|
+
const retainInputs = useRef({
|
|
295
|
+
layout: null,
|
|
296
|
+
gap: 0,
|
|
297
|
+
windowSize: 0,
|
|
298
|
+
viewportLength: 0
|
|
299
|
+
});
|
|
300
|
+
const retainSlots = useCallback(itemId => retainsCommittedSlots(retainInputs.current, itemId), []);
|
|
301
|
+
const snapshot = useDndZoneSnapshot(runtime, zoneId, typeof itemSize === 'function' ? false : virtualized ? retainSlots : true);
|
|
302
|
+
const identity = useRef({}).current;
|
|
303
|
+
const [viewportSize, setViewportSize] = useState({
|
|
304
|
+
width: 0,
|
|
305
|
+
height: 0
|
|
306
|
+
});
|
|
307
|
+
const [measurementVersion, setMeasurementVersion] = useState(0);
|
|
308
|
+
const zone = snapshot.zone;
|
|
309
|
+
const displayZone = snapshot.display;
|
|
310
|
+
const orientation = zone?.kind === 'list' ? zone.orientation : 'vertical';
|
|
311
|
+
const vertical = orientation === 'vertical';
|
|
312
|
+
const crossSize = vertical ? viewportSize.width : viewportSize.height;
|
|
313
|
+
const items = snapshot.items;
|
|
314
|
+
const revision = snapshot.revision;
|
|
315
|
+
const displayRevision = snapshot.displayRevision;
|
|
316
|
+
const cache = useMemo(() => new ListSizeCache(),
|
|
317
|
+
// A new geometry/data context must not reuse old native measurements.
|
|
318
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
319
|
+
[crossSize, orientation, items]);
|
|
320
|
+
const measurements = useMemo(() => ({
|
|
321
|
+
cache,
|
|
322
|
+
version: 0,
|
|
323
|
+
committedVersion: 0,
|
|
324
|
+
frame: null,
|
|
325
|
+
active: false
|
|
326
|
+
}), [cache]);
|
|
327
|
+
const measurementContext = useRef(null);
|
|
328
|
+
const scheduleMeasurementRender = useCallback(() => {
|
|
329
|
+
if (!measurements.active || measurements.frame !== null || measurements.version <= measurements.committedVersion) return;
|
|
330
|
+
const frame = requestAnimationFrame(() => {
|
|
331
|
+
// A cancelled native frame can already be queued for RN delivery.
|
|
332
|
+
if (measurements.frame !== frame) return;
|
|
333
|
+
measurements.frame = null;
|
|
334
|
+
if (!measurements.active || measurementContext.current?.cache !== measurements.cache || measurements.version <= measurements.committedVersion) return;
|
|
335
|
+
setMeasurementVersion(current => current + 1);
|
|
336
|
+
});
|
|
337
|
+
measurements.frame = frame;
|
|
338
|
+
}, [measurements]);
|
|
339
|
+
useLayoutEffect(() => {
|
|
340
|
+
measurements.active = true;
|
|
341
|
+
scheduleMeasurementRender();
|
|
342
|
+
return () => {
|
|
343
|
+
measurements.active = false;
|
|
344
|
+
if (measurements.frame !== null) cancelAnimationFrame(measurements.frame);
|
|
345
|
+
measurements.frame = null;
|
|
346
|
+
};
|
|
347
|
+
}, [measurements, scheduleMeasurementRender]);
|
|
348
|
+
const renderedMeasurementVersion = useMemo(() => measurements.version,
|
|
349
|
+
// An unrelated render may reuse memoized layout; only a measurement signal
|
|
350
|
+
// guarantees that the layout consumed the newest cached dimensions.
|
|
351
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
352
|
+
[measurements, measurementVersion]);
|
|
353
|
+
useLayoutEffect(() => {
|
|
354
|
+
measurements.committedVersion = renderedMeasurementVersion;
|
|
355
|
+
// A callback arriving between render and commit already reserved the next
|
|
356
|
+
// frame. Its newer version must not be acknowledged by this older render.
|
|
357
|
+
}, [measurements, renderedMeasurementVersion]);
|
|
358
|
+
const scrollRef = useAnimatedRef();
|
|
359
|
+
const offset = useSharedValue(0);
|
|
360
|
+
const {
|
|
361
|
+
motion,
|
|
362
|
+
animation,
|
|
363
|
+
presentation,
|
|
364
|
+
committedRevision
|
|
365
|
+
} = runtime;
|
|
366
|
+
// Virtual cells position their content with a React-committed slot offset
|
|
367
|
+
// plus the animated content start. The UI runtime only needs each slot's
|
|
368
|
+
// flow start as the motion fallback target; changed starts of one commit
|
|
369
|
+
// travel together in one UI task, without RN shared-value reads.
|
|
370
|
+
const expectedStarts = useMemo(() => ({
|
|
371
|
+
pending: new Map(),
|
|
372
|
+
queued: false,
|
|
373
|
+
active: false
|
|
374
|
+
}),
|
|
375
|
+
// Native viewports and their cells are replaced when these change.
|
|
376
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
377
|
+
[orientation, virtualized, zoneId]);
|
|
378
|
+
const flushExpectedStarts = useCallback(() => {
|
|
379
|
+
expectedStarts.queued = false;
|
|
380
|
+
if (!expectedStarts.active || expectedStarts.pending.size === 0) return;
|
|
381
|
+
const updates = Array.from(expectedStarts.pending, ([shared, start]) => ({
|
|
382
|
+
shared,
|
|
383
|
+
start
|
|
384
|
+
}));
|
|
385
|
+
expectedStarts.pending.clear();
|
|
386
|
+
scheduleOnUI(applyExpectedStarts, updates);
|
|
387
|
+
}, [expectedStarts]);
|
|
388
|
+
const publishExpectedStart = useCallback((shared, start) => {
|
|
389
|
+
if (start === null) {
|
|
390
|
+
expectedStarts.pending.delete(shared);
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
expectedStarts.pending.set(shared, start);
|
|
394
|
+
if (expectedStarts.queued) return;
|
|
395
|
+
expectedStarts.queued = true;
|
|
396
|
+
globalThis.queueMicrotask(flushExpectedStarts);
|
|
397
|
+
}, [expectedStarts, flushExpectedStarts]);
|
|
398
|
+
useLayoutEffect(() => {
|
|
399
|
+
expectedStarts.active = true;
|
|
400
|
+
return () => {
|
|
401
|
+
expectedStarts.active = false;
|
|
402
|
+
expectedStarts.pending.clear();
|
|
403
|
+
};
|
|
404
|
+
}, [expectedStarts]);
|
|
405
|
+
const prepareLayout = useMemo(() => {
|
|
406
|
+
let previous;
|
|
407
|
+
let entries = new Map();
|
|
408
|
+
return (candidate, candidateRevision) => {
|
|
409
|
+
if (candidate.orientation !== orientation || crossSize <= 0) return null;
|
|
410
|
+
if (previous?.zone === candidate && previous.revision === candidateRevision && previous.measurementVersion === measurements.version) return previous;
|
|
411
|
+
const layout = computeListLayout({
|
|
412
|
+
itemIds: candidate.itemIds,
|
|
413
|
+
orientation,
|
|
414
|
+
crossSize,
|
|
415
|
+
estimatedItemSize,
|
|
416
|
+
itemSize,
|
|
417
|
+
gap,
|
|
418
|
+
paddingStart,
|
|
419
|
+
paddingEnd,
|
|
420
|
+
revision: candidateRevision,
|
|
421
|
+
cache
|
|
422
|
+
});
|
|
423
|
+
if (layout.valid) {
|
|
424
|
+
const nextEntries = new Map();
|
|
425
|
+
layout.entries = layout.entries.map(entry => {
|
|
426
|
+
const old = entries.get(entry.itemId);
|
|
427
|
+
const sameRect = old && old.rect.x === entry.rect.x && old.rect.y === entry.rect.y && old.rect.width === entry.rect.width && old.rect.height === entry.rect.height;
|
|
428
|
+
// Rects contain only coordinates. Preserve their immutable identity
|
|
429
|
+
// across revision/source changes so UI need not receive them again;
|
|
430
|
+
// the new entry still carries its current index and revision.
|
|
431
|
+
if (sameRect) entry.rect = old.rect;
|
|
432
|
+
const same = old && old.index === entry.index && old.start === entry.start && old.size === entry.size && old.source === entry.source && old.revision === entry.revision && sameRect;
|
|
433
|
+
const retained = same ? old : entry;
|
|
434
|
+
nextEntries.set(entry.itemId, retained);
|
|
435
|
+
return retained;
|
|
436
|
+
});
|
|
437
|
+
entries = nextEntries;
|
|
438
|
+
}
|
|
439
|
+
previous = {
|
|
440
|
+
zone: candidate,
|
|
441
|
+
revision: candidateRevision,
|
|
442
|
+
measurementVersion: measurements.version,
|
|
443
|
+
layout,
|
|
444
|
+
// Candidates of one item set share the slot map; only coordinates cross
|
|
445
|
+
// to UI again.
|
|
446
|
+
presentation: layout.valid ? createZonePresentation(layout.entries.map(entry => [entry.itemId, entry.rect]), previous?.presentation) : null
|
|
447
|
+
};
|
|
448
|
+
return previous;
|
|
449
|
+
};
|
|
450
|
+
}, [cache, crossSize, estimatedItemSize, gap, itemSize, measurements, orientation, paddingEnd, paddingStart]);
|
|
451
|
+
const prepareDisplay = useCallback((candidate, candidateRevision) => prepareLayout(candidate, candidateRevision)?.presentation ?? null, [prepareLayout]);
|
|
452
|
+
const base = useMemo(() => zone?.kind === 'list' ? prepareLayout(zone, revision)?.layout ?? null : null,
|
|
453
|
+
// Cache writes are mutable; the version explicitly invalidates this result.
|
|
454
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
455
|
+
[zone, revision, prepareLayout, measurementVersion]);
|
|
456
|
+
const viewportLength = vertical ? viewportSize.height : viewportSize.width;
|
|
457
|
+
const windowSize = windowing.valid ? windowing.options?.windowSize ?? 0 : 0;
|
|
458
|
+
useLayoutEffect(() => {
|
|
459
|
+
// Read by the retain rule when a drag starts. The committed layout does
|
|
460
|
+
// not change between idle and the first preview of a session.
|
|
461
|
+
retainInputs.current = {
|
|
462
|
+
layout: base,
|
|
463
|
+
gap,
|
|
464
|
+
windowSize,
|
|
465
|
+
viewportLength
|
|
466
|
+
};
|
|
467
|
+
}, [base, gap, viewportLength, windowSize]);
|
|
468
|
+
const display = useMemo(() => displayZone === zone ? base : displayZone?.kind === 'list' ? prepareLayout(displayZone, displayRevision)?.layout ?? null : null,
|
|
469
|
+
// Candidate rects must also be recomputed after an accepted measurement.
|
|
470
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
471
|
+
[displayZone, zone, base, displayRevision, prepareLayout, measurementVersion]);
|
|
472
|
+
const validScroll = validateAutoScrollOptions(autoScroll).valid && typeof scrollEnabled === 'boolean';
|
|
473
|
+
useLayoutEffect(() => {
|
|
474
|
+
measurementContext.current = {
|
|
475
|
+
cache,
|
|
476
|
+
revision
|
|
477
|
+
};
|
|
478
|
+
return () => {
|
|
479
|
+
measurementContext.current = null;
|
|
480
|
+
};
|
|
481
|
+
}, [cache, revision]);
|
|
482
|
+
useLayoutEffect(() => {
|
|
483
|
+
// Switching adapter/direction remounts the native viewport.
|
|
484
|
+
offset.set(0);
|
|
485
|
+
return () => unregisterZone(zoneId, identity);
|
|
486
|
+
}, [identity, offset, orientation, unregisterZone, virtualized, zoneId]);
|
|
487
|
+
useLayoutEffect(() => {
|
|
488
|
+
reportIssues(`list:${zoneId}`, zone?.kind !== 'list' ? [{
|
|
489
|
+
code: 'invalid-configuration',
|
|
490
|
+
zoneId,
|
|
491
|
+
message: 'SortableList requires an existing list zone.'
|
|
492
|
+
}] : !windowing.valid ? windowing.issues : !validScroll ? [{
|
|
493
|
+
code: 'invalid-configuration',
|
|
494
|
+
zoneId,
|
|
495
|
+
message: 'List scrolling options are invalid.'
|
|
496
|
+
}] : base && !base.valid ? base.issues : []);
|
|
497
|
+
});
|
|
498
|
+
useLayoutEffect(() => {
|
|
499
|
+
if (!base?.valid || !display?.valid || !validScroll || !windowing.valid) {
|
|
500
|
+
unregisterZone(zoneId, identity);
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
registerZone({
|
|
504
|
+
kind: 'list',
|
|
505
|
+
zoneId,
|
|
506
|
+
orientation,
|
|
507
|
+
revision,
|
|
508
|
+
ref: scrollRef,
|
|
509
|
+
offset,
|
|
510
|
+
contentSize: display.totalSize,
|
|
511
|
+
autoScroll: {
|
|
512
|
+
...autoScroll,
|
|
513
|
+
enabled: scrollEnabled && autoScroll?.enabled !== false
|
|
514
|
+
},
|
|
515
|
+
layout: base,
|
|
516
|
+
dataIdentity: items,
|
|
517
|
+
prepareDisplay,
|
|
518
|
+
renderItem: args => renderItem(args)
|
|
519
|
+
}, identity);
|
|
520
|
+
}, [registerZone, unregisterZone, zoneId, orientation, revision, scrollRef, offset, display, base, autoScroll, validScroll, scrollEnabled, renderItem, identity, windowing.valid, virtualized, prepareDisplay, items]);
|
|
521
|
+
useLayoutEffect(() => () => {
|
|
522
|
+
unregisterZone(zoneId, identity);
|
|
523
|
+
reportIssues(`list:${zoneId}`, []);
|
|
524
|
+
}, [zoneId, identity, unregisterZone, reportIssues]);
|
|
525
|
+
const onSize = useCallback((itemId, size) => {
|
|
526
|
+
if (measurementContext.current?.cache !== cache || measurementContext.current.revision !== revision) return;
|
|
527
|
+
const previous = cache.get(itemId, {
|
|
528
|
+
orientation,
|
|
529
|
+
crossSize
|
|
530
|
+
});
|
|
531
|
+
if (previous !== undefined && Math.abs(previous - size) < 0.001) return;
|
|
532
|
+
if (cache.set(itemId, size, {
|
|
533
|
+
orientation,
|
|
534
|
+
crossSize
|
|
535
|
+
}, revision) && previous !== size) {
|
|
536
|
+
// Invalidate before React renders, since the Provider may ask for a
|
|
537
|
+
// validated presentation while this native measurement is still batched.
|
|
538
|
+
measurements.version++;
|
|
539
|
+
scheduleMeasurementRender();
|
|
540
|
+
}
|
|
541
|
+
}, [cache, orientation, crossSize, revision, measurements, scheduleMeasurementRender]);
|
|
542
|
+
const cellMeasurements = useMemo(() => ({
|
|
543
|
+
revision,
|
|
544
|
+
onSize
|
|
545
|
+
}), [revision, onSize]);
|
|
546
|
+
const scrollHandler = useAnimatedScrollHandler({
|
|
547
|
+
onScroll: event => {
|
|
548
|
+
const nextOffset = vertical ? event.contentOffset.y : event.contentOffset.x;
|
|
549
|
+
offset.set(nextOffset);
|
|
550
|
+
if (onScrollOffsetChange) scheduleOnRN(onScrollOffsetChange, {
|
|
551
|
+
x: event.contentOffset.x,
|
|
552
|
+
y: event.contentOffset.y
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
const handleLayout = useCallback(event => {
|
|
557
|
+
const {
|
|
558
|
+
width,
|
|
559
|
+
height
|
|
560
|
+
} = event.nativeEvent.layout;
|
|
561
|
+
setViewportSize(current => current.width === width && current.height === height ? current : {
|
|
562
|
+
width,
|
|
563
|
+
height
|
|
564
|
+
});
|
|
565
|
+
onLayout?.(event);
|
|
566
|
+
}, [onLayout]);
|
|
567
|
+
const data = useMemo(() => new Map(items?.map(item => [item.id, item]) ?? []), [items]);
|
|
568
|
+
const contentSize = display?.valid ? display.totalSize : 0;
|
|
569
|
+
const visibleEntries = display?.valid ? display.entries : EMPTY_ENTRIES;
|
|
570
|
+
const visibleIds = display?.valid && displayZone?.kind === 'list' ? displayZone.itemIds : EMPTY_IDS;
|
|
571
|
+
const entriesById = useMemo(() => new Map(visibleEntries.map(entry => [entry.itemId, entry])), [visibleEntries]);
|
|
572
|
+
const entryCount = visibleEntries.length;
|
|
573
|
+
const slotSize = useCallback(entry => entry.size + (entry.index < entryCount - 1 ? gap : 0), [entryCount, gap]);
|
|
574
|
+
const getItemLayout = useCallback((_itemIds, index) => {
|
|
575
|
+
const entry = visibleEntries[index];
|
|
576
|
+
return {
|
|
577
|
+
index,
|
|
578
|
+
offset: entry.start,
|
|
579
|
+
length: slotSize(entry)
|
|
580
|
+
};
|
|
581
|
+
}, [slotSize, visibleEntries]);
|
|
582
|
+
// Cells only need the active source's visibility, not the pointer and all
|
|
583
|
+
// measured viewports copied into motion on every frame.
|
|
584
|
+
const hiddenItemId = useDerivedValue(() => {
|
|
585
|
+
const state = motion.get();
|
|
586
|
+
return state.visible ? state.itemId : null;
|
|
587
|
+
});
|
|
588
|
+
const activeItemId = snapshot.activeItemId;
|
|
589
|
+
const fixed = itemSize !== undefined;
|
|
590
|
+
const virtualRenderState = useMemo(() => ({
|
|
591
|
+
entriesById,
|
|
592
|
+
data,
|
|
593
|
+
activeItemId,
|
|
594
|
+
slotSize,
|
|
595
|
+
cellProps: {
|
|
596
|
+
publishExpectedStart,
|
|
597
|
+
zoneId,
|
|
598
|
+
committedRevision,
|
|
599
|
+
orientation,
|
|
600
|
+
crossSize,
|
|
601
|
+
fixed,
|
|
602
|
+
measurementIdentity: cache,
|
|
603
|
+
renderItem,
|
|
604
|
+
hiddenItemId,
|
|
605
|
+
animation,
|
|
606
|
+
presentation
|
|
607
|
+
}
|
|
608
|
+
}), [activeItemId, animation, cache, committedRevision, crossSize, data, entriesById, fixed, hiddenItemId, orientation, renderItem, slotSize, zoneId, presentation, publishExpectedStart]);
|
|
609
|
+
const contentContainerStyle = useMemo(() => vertical ? {
|
|
610
|
+
paddingTop: paddingStart,
|
|
611
|
+
paddingBottom: paddingEnd
|
|
612
|
+
} : {
|
|
613
|
+
paddingLeft: paddingStart,
|
|
614
|
+
paddingRight: paddingEnd
|
|
615
|
+
}, [paddingEnd, paddingStart, vertical]);
|
|
616
|
+
if (virtualized && windowing.valid && windowing.options) {
|
|
617
|
+
// These are actual native flow slots, including the trailing inter-item gap.
|
|
618
|
+
// The natural-size child is absolute so an estimate cannot constrain its
|
|
619
|
+
// measurement. Cache updates change both native slots and getItemLayout.
|
|
620
|
+
return /*#__PURE__*/_jsx(ListMeasurementContext, {
|
|
621
|
+
value: cellMeasurements,
|
|
622
|
+
children: /*#__PURE__*/_jsx(VirtualListContext, {
|
|
623
|
+
value: virtualRenderState,
|
|
624
|
+
children: /*#__PURE__*/_createElement(Animated.FlatList, {
|
|
625
|
+
...viewProps,
|
|
626
|
+
...windowing.options,
|
|
627
|
+
...FLAT_LIST_RENDERER_OPTIONS,
|
|
628
|
+
key: `${zoneId}:${orientation}:flat`,
|
|
629
|
+
ref: scrollRef,
|
|
630
|
+
data: visibleIds,
|
|
631
|
+
keyExtractor: itemKey,
|
|
632
|
+
horizontal: !vertical,
|
|
633
|
+
style: style,
|
|
634
|
+
contentContainerStyle: contentContainerStyle,
|
|
635
|
+
onLayout: handleLayout,
|
|
636
|
+
onScroll: scrollHandler,
|
|
637
|
+
scrollEventThrottle: 16,
|
|
638
|
+
scrollEnabled: scrollEnabled && snapshot.scrollEnabled,
|
|
639
|
+
removeClippedSubviews: false,
|
|
640
|
+
showsVerticalScrollIndicator: showsScrollIndicator,
|
|
641
|
+
showsHorizontalScrollIndicator: showsScrollIndicator,
|
|
642
|
+
contentInsetAdjustmentBehavior: "never",
|
|
643
|
+
getItemLayout: getItemLayout,
|
|
644
|
+
renderItem: renderVirtualItem
|
|
645
|
+
})
|
|
646
|
+
})
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
const entries = new Map(display?.valid ? display.entries.map(entry => [entry.itemId, entry]) : []);
|
|
650
|
+
// The ScrollView adapter retains source cells through cross-list previews.
|
|
651
|
+
// FlatList may unmount them; the provider owns the gesture and overlay.
|
|
652
|
+
const renderIds = new Set([...(zone?.kind === 'list' ? zone.itemIds : []), ...(displayZone?.kind === 'list' ? displayZone.itemIds : [])]);
|
|
653
|
+
return /*#__PURE__*/_jsx(ListMeasurementContext, {
|
|
654
|
+
value: cellMeasurements,
|
|
655
|
+
children: /*#__PURE__*/_createElement(Animated.ScrollView, {
|
|
656
|
+
...viewProps,
|
|
657
|
+
key: `${zoneId}:${orientation}:scroll`,
|
|
658
|
+
ref: scrollRef,
|
|
659
|
+
horizontal: !vertical,
|
|
660
|
+
style: style,
|
|
661
|
+
onLayout: handleLayout,
|
|
662
|
+
onScroll: scrollHandler,
|
|
663
|
+
scrollEventThrottle: 16,
|
|
664
|
+
scrollEnabled: scrollEnabled && snapshot.scrollEnabled,
|
|
665
|
+
removeClippedSubviews: false,
|
|
666
|
+
showsVerticalScrollIndicator: showsScrollIndicator,
|
|
667
|
+
showsHorizontalScrollIndicator: showsScrollIndicator,
|
|
668
|
+
contentInsetAdjustmentBehavior: "never"
|
|
669
|
+
}, /*#__PURE__*/_jsx(View, {
|
|
670
|
+
style: vertical ? {
|
|
671
|
+
width: crossSize,
|
|
672
|
+
height: contentSize
|
|
673
|
+
} : [styles.horizontalSlot, {
|
|
674
|
+
height: crossSize,
|
|
675
|
+
width: contentSize
|
|
676
|
+
}],
|
|
677
|
+
children: base?.valid && display?.valid && Array.from(renderIds).map(itemId => {
|
|
678
|
+
const item = data.get(itemId);
|
|
679
|
+
const entry = entries.get(itemId) ?? base.entries.find(candidate => candidate.itemId === itemId);
|
|
680
|
+
if (!item || !entry) return null;
|
|
681
|
+
return /*#__PURE__*/_jsx(ListCell, {
|
|
682
|
+
item: item,
|
|
683
|
+
index: entry.index,
|
|
684
|
+
zoneId: zoneId,
|
|
685
|
+
committedRevision: committedRevision,
|
|
686
|
+
start: entry.start,
|
|
687
|
+
size: entry.size,
|
|
688
|
+
orientation: orientation,
|
|
689
|
+
crossSize: crossSize,
|
|
690
|
+
fixed: fixed,
|
|
691
|
+
measurementIdentity: cache,
|
|
692
|
+
renderItem: renderItem,
|
|
693
|
+
hiddenItemId: hiddenItemId,
|
|
694
|
+
animation: animation,
|
|
695
|
+
presentation: presentation,
|
|
696
|
+
isDragging: activeItemId === item.id
|
|
697
|
+
}, itemId);
|
|
698
|
+
})
|
|
699
|
+
}))
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
const styles = StyleSheet.create({
|
|
703
|
+
cell: {
|
|
704
|
+
position: 'absolute',
|
|
705
|
+
left: 0,
|
|
706
|
+
top: 0
|
|
707
|
+
},
|
|
708
|
+
// Yoga otherwise fits an auto-width absolute child to a column parent's
|
|
709
|
+
// estimated width. A row leaves the measured main axis unconstrained.
|
|
710
|
+
horizontalSlot: {
|
|
711
|
+
flexDirection: 'row'
|
|
712
|
+
}
|
|
713
|
+
});
|
|
714
|
+
//# sourceMappingURL=SortableList.js.map
|