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.
Files changed (340) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +262 -0
  3. package/lib/module/adapters/coordinates.js +135 -0
  4. package/lib/module/adapters/coordinates.js.map +1 -0
  5. package/lib/module/adapters/scroll.js +81 -0
  6. package/lib/module/adapters/scroll.js.map +1 -0
  7. package/lib/module/adapters/virtualizedList.js +69 -0
  8. package/lib/module/adapters/virtualizedList.js.map +1 -0
  9. package/lib/module/components/DndProvider.js +1144 -0
  10. package/lib/module/components/DndProvider.js.map +1 -0
  11. package/lib/module/components/DragHandle.js +65 -0
  12. package/lib/module/components/DragHandle.js.map +1 -0
  13. package/lib/module/components/GridController.js +151 -0
  14. package/lib/module/components/GridController.js.map +1 -0
  15. package/lib/module/components/GridDragHandle.js +15 -0
  16. package/lib/module/components/GridDragHandle.js.map +1 -0
  17. package/lib/module/components/GridZoneView.js +59 -0
  18. package/lib/module/components/GridZoneView.js.map +1 -0
  19. package/lib/module/components/SortableGrid.js +305 -0
  20. package/lib/module/components/SortableGrid.js.map +1 -0
  21. package/lib/module/components/SortableList.js +714 -0
  22. package/lib/module/components/SortableList.js.map +1 -0
  23. package/lib/module/components/dndTypes.js +4 -0
  24. package/lib/module/components/dndTypes.js.map +1 -0
  25. package/lib/module/components/gridTypes.js +4 -0
  26. package/lib/module/components/gridTypes.js.map +1 -0
  27. package/lib/module/components/handleViewProps.js +13 -0
  28. package/lib/module/components/handleViewProps.js.map +1 -0
  29. package/lib/module/contracts.js +4 -0
  30. package/lib/module/contracts.js.map +1 -0
  31. package/lib/module/controller/approval.js +98 -0
  32. package/lib/module/controller/approval.js.map +1 -0
  33. package/lib/module/controller/coordinatorBase.js +93 -0
  34. package/lib/module/controller/coordinatorBase.js.map +1 -0
  35. package/lib/module/controller/dndSession.js +621 -0
  36. package/lib/module/controller/dndSession.js.map +1 -0
  37. package/lib/module/controller/dndState.js +103 -0
  38. package/lib/module/controller/dndState.js.map +1 -0
  39. package/lib/module/controller/gridAdapter.js +191 -0
  40. package/lib/module/controller/gridAdapter.js.map +1 -0
  41. package/lib/module/controller/gridState.js +101 -0
  42. package/lib/module/controller/gridState.js.map +1 -0
  43. package/lib/module/controller/locations.js +48 -0
  44. package/lib/module/controller/locations.js.map +1 -0
  45. package/lib/module/controller/previewPath.js +73 -0
  46. package/lib/module/controller/previewPath.js.map +1 -0
  47. package/lib/module/controller/sessionCore.js +222 -0
  48. package/lib/module/controller/sessionCore.js.map +1 -0
  49. package/lib/module/controller/useDndState.js +9 -0
  50. package/lib/module/controller/useDndState.js.map +1 -0
  51. package/lib/module/controller/useGridState.js +9 -0
  52. package/lib/module/controller/useGridState.js.map +1 -0
  53. package/lib/module/controller/useStoreState.js +15 -0
  54. package/lib/module/controller/useStoreState.js.map +1 -0
  55. package/lib/module/defaults.js +7 -0
  56. package/lib/module/defaults.js.map +1 -0
  57. package/lib/module/diagnostics.js +16 -0
  58. package/lib/module/diagnostics.js.map +1 -0
  59. package/lib/module/engine/gridPlacements.js +54 -0
  60. package/lib/module/engine/gridPlacements.js.map +1 -0
  61. package/lib/module/engine/index.js +19 -0
  62. package/lib/module/engine/index.js.map +1 -0
  63. package/lib/module/engine/layout.js +36 -0
  64. package/lib/module/engine/layout.js.map +1 -0
  65. package/lib/module/engine/layoutMove.js +326 -0
  66. package/lib/module/engine/layoutMove.js.map +1 -0
  67. package/lib/module/engine/layoutState.js +344 -0
  68. package/lib/module/engine/layoutState.js.map +1 -0
  69. package/lib/module/engine/list.js +172 -0
  70. package/lib/module/engine/list.js.map +1 -0
  71. package/lib/module/engine/listIndex.js +125 -0
  72. package/lib/module/engine/listIndex.js.map +1 -0
  73. package/lib/module/engine/move.js +216 -0
  74. package/lib/module/engine/move.js.map +1 -0
  75. package/lib/module/engine/movementPolicy.js +77 -0
  76. package/lib/module/engine/movementPolicy.js.map +1 -0
  77. package/lib/module/engine/orderedTarget.js +71 -0
  78. package/lib/module/engine/orderedTarget.js.map +1 -0
  79. package/lib/module/engine/pack.js +30 -0
  80. package/lib/module/engine/pack.js.map +1 -0
  81. package/lib/module/engine/rectangles.js +12 -0
  82. package/lib/module/engine/rectangles.js.map +1 -0
  83. package/lib/module/engine/sameLayout.js +29 -0
  84. package/lib/module/engine/sameLayout.js.map +1 -0
  85. package/lib/module/engine/search.js +159 -0
  86. package/lib/module/engine/search.js.map +1 -0
  87. package/lib/module/engine/spatial.js +27 -0
  88. package/lib/module/engine/spatial.js.map +1 -0
  89. package/lib/module/engine/spatialAppRotation.js +39 -0
  90. package/lib/module/engine/spatialAppRotation.js.map +1 -0
  91. package/lib/module/engine/spatialExchange.js +164 -0
  92. package/lib/module/engine/spatialExchange.js.map +1 -0
  93. package/lib/module/engine/spatialInsertion.js +135 -0
  94. package/lib/module/engine/spatialInsertion.js.map +1 -0
  95. package/lib/module/engine/spatialPolicy.js +41 -0
  96. package/lib/module/engine/spatialPolicy.js.map +1 -0
  97. package/lib/module/engine/spatialRotation.js +48 -0
  98. package/lib/module/engine/spatialRotation.js.map +1 -0
  99. package/lib/module/engine/validation.js +205 -0
  100. package/lib/module/engine/validation.js.map +1 -0
  101. package/lib/module/gesture/DndGestureGroup.js +268 -0
  102. package/lib/module/gesture/DndGestureGroup.js.map +1 -0
  103. package/lib/module/gesture/DndGestureHost.js +32 -0
  104. package/lib/module/gesture/DndGestureHost.js.map +1 -0
  105. package/lib/module/gesture/gestureConfig.js +81 -0
  106. package/lib/module/gesture/gestureConfig.js.map +1 -0
  107. package/lib/module/gesture/gestureGrouping.js +122 -0
  108. package/lib/module/gesture/gestureGrouping.js.map +1 -0
  109. package/lib/module/gesture/gestureHost.js +106 -0
  110. package/lib/module/gesture/gestureHost.js.map +1 -0
  111. package/lib/module/index.js +23 -0
  112. package/lib/module/index.js.map +1 -0
  113. package/lib/module/package.json +1 -0
  114. package/lib/module/pointer/dndTarget.js +101 -0
  115. package/lib/module/pointer/dndTarget.js.map +1 -0
  116. package/lib/module/pointer/geometry.js +363 -0
  117. package/lib/module/pointer/geometry.js.map +1 -0
  118. package/lib/module/pointer/layoutTarget.js +307 -0
  119. package/lib/module/pointer/layoutTarget.js.map +1 -0
  120. package/lib/module/pointer/listCellMotion.js +99 -0
  121. package/lib/module/pointer/listCellMotion.js.map +1 -0
  122. package/lib/module/pointer/listTarget.js +42 -0
  123. package/lib/module/pointer/listTarget.js.map +1 -0
  124. package/lib/module/pointer/motion.js +37 -0
  125. package/lib/module/pointer/motion.js.map +1 -0
  126. package/lib/module/runtime/dndPresentation.js +81 -0
  127. package/lib/module/runtime/dndPresentation.js.map +1 -0
  128. package/lib/module/runtime/dndRenderStore.js +129 -0
  129. package/lib/module/runtime/dndRenderStore.js.map +1 -0
  130. package/lib/module/runtime/dndRuntime.js +135 -0
  131. package/lib/module/runtime/dndRuntime.js.map +1 -0
  132. package/lib/module/runtime/dndSettlement.js +77 -0
  133. package/lib/module/runtime/dndSettlement.js.map +1 -0
  134. package/lib/module/runtime/gridRuntime.js +37 -0
  135. package/lib/module/runtime/gridRuntime.js.map +1 -0
  136. package/lib/module/types.js +4 -0
  137. package/lib/module/types.js.map +1 -0
  138. package/lib/typescript/package.json +1 -0
  139. package/lib/typescript/src/adapters/coordinates.d.ts +9 -0
  140. package/lib/typescript/src/adapters/coordinates.d.ts.map +1 -0
  141. package/lib/typescript/src/adapters/scroll.d.ts +49 -0
  142. package/lib/typescript/src/adapters/scroll.d.ts.map +1 -0
  143. package/lib/typescript/src/adapters/virtualizedList.d.ts +22 -0
  144. package/lib/typescript/src/adapters/virtualizedList.d.ts.map +1 -0
  145. package/lib/typescript/src/components/DndProvider.d.ts +4 -0
  146. package/lib/typescript/src/components/DndProvider.d.ts.map +1 -0
  147. package/lib/typescript/src/components/DragHandle.d.ts +4 -0
  148. package/lib/typescript/src/components/DragHandle.d.ts.map +1 -0
  149. package/lib/typescript/src/components/GridController.d.ts +21 -0
  150. package/lib/typescript/src/components/GridController.d.ts.map +1 -0
  151. package/lib/typescript/src/components/GridDragHandle.d.ts +8 -0
  152. package/lib/typescript/src/components/GridDragHandle.d.ts.map +1 -0
  153. package/lib/typescript/src/components/GridZoneView.d.ts +8 -0
  154. package/lib/typescript/src/components/GridZoneView.d.ts.map +1 -0
  155. package/lib/typescript/src/components/SortableGrid.d.ts +4 -0
  156. package/lib/typescript/src/components/SortableGrid.d.ts.map +1 -0
  157. package/lib/typescript/src/components/SortableList.d.ts +4 -0
  158. package/lib/typescript/src/components/SortableList.d.ts.map +1 -0
  159. package/lib/typescript/src/components/dndTypes.d.ts +94 -0
  160. package/lib/typescript/src/components/dndTypes.d.ts.map +1 -0
  161. package/lib/typescript/src/components/gridTypes.d.ts +84 -0
  162. package/lib/typescript/src/components/gridTypes.d.ts.map +1 -0
  163. package/lib/typescript/src/components/handleViewProps.d.ts +6 -0
  164. package/lib/typescript/src/components/handleViewProps.d.ts.map +1 -0
  165. package/lib/typescript/src/contracts.d.ts +145 -0
  166. package/lib/typescript/src/contracts.d.ts.map +1 -0
  167. package/lib/typescript/src/controller/approval.d.ts +66 -0
  168. package/lib/typescript/src/controller/approval.d.ts.map +1 -0
  169. package/lib/typescript/src/controller/coordinatorBase.d.ts +37 -0
  170. package/lib/typescript/src/controller/coordinatorBase.d.ts.map +1 -0
  171. package/lib/typescript/src/controller/dndSession.d.ts +152 -0
  172. package/lib/typescript/src/controller/dndSession.d.ts.map +1 -0
  173. package/lib/typescript/src/controller/dndState.d.ts +27 -0
  174. package/lib/typescript/src/controller/dndState.d.ts.map +1 -0
  175. package/lib/typescript/src/controller/gridAdapter.d.ts +44 -0
  176. package/lib/typescript/src/controller/gridAdapter.d.ts.map +1 -0
  177. package/lib/typescript/src/controller/gridState.d.ts +18 -0
  178. package/lib/typescript/src/controller/gridState.d.ts.map +1 -0
  179. package/lib/typescript/src/controller/locations.d.ts +9 -0
  180. package/lib/typescript/src/controller/locations.d.ts.map +1 -0
  181. package/lib/typescript/src/controller/previewPath.d.ts +49 -0
  182. package/lib/typescript/src/controller/previewPath.d.ts.map +1 -0
  183. package/lib/typescript/src/controller/sessionCore.d.ts +106 -0
  184. package/lib/typescript/src/controller/sessionCore.d.ts.map +1 -0
  185. package/lib/typescript/src/controller/useDndState.d.ts +8 -0
  186. package/lib/typescript/src/controller/useDndState.d.ts.map +1 -0
  187. package/lib/typescript/src/controller/useGridState.d.ts +8 -0
  188. package/lib/typescript/src/controller/useGridState.d.ts.map +1 -0
  189. package/lib/typescript/src/controller/useStoreState.d.ts +16 -0
  190. package/lib/typescript/src/controller/useStoreState.d.ts.map +1 -0
  191. package/lib/typescript/src/defaults.d.ts +5 -0
  192. package/lib/typescript/src/defaults.d.ts.map +1 -0
  193. package/lib/typescript/src/diagnostics.d.ts +9 -0
  194. package/lib/typescript/src/diagnostics.d.ts.map +1 -0
  195. package/lib/typescript/src/engine/gridPlacements.d.ts +22 -0
  196. package/lib/typescript/src/engine/gridPlacements.d.ts.map +1 -0
  197. package/lib/typescript/src/engine/index.d.ts +28 -0
  198. package/lib/typescript/src/engine/index.d.ts.map +1 -0
  199. package/lib/typescript/src/engine/layout.d.ts +4 -0
  200. package/lib/typescript/src/engine/layout.d.ts.map +1 -0
  201. package/lib/typescript/src/engine/layoutMove.d.ts +46 -0
  202. package/lib/typescript/src/engine/layoutMove.d.ts.map +1 -0
  203. package/lib/typescript/src/engine/layoutState.d.ts +22 -0
  204. package/lib/typescript/src/engine/layoutState.d.ts.map +1 -0
  205. package/lib/typescript/src/engine/list.d.ts +68 -0
  206. package/lib/typescript/src/engine/list.d.ts.map +1 -0
  207. package/lib/typescript/src/engine/listIndex.d.ts +32 -0
  208. package/lib/typescript/src/engine/listIndex.d.ts.map +1 -0
  209. package/lib/typescript/src/engine/move.d.ts +10 -0
  210. package/lib/typescript/src/engine/move.d.ts.map +1 -0
  211. package/lib/typescript/src/engine/movementPolicy.d.ts +34 -0
  212. package/lib/typescript/src/engine/movementPolicy.d.ts.map +1 -0
  213. package/lib/typescript/src/engine/orderedTarget.d.ts +32 -0
  214. package/lib/typescript/src/engine/orderedTarget.d.ts.map +1 -0
  215. package/lib/typescript/src/engine/pack.d.ts +3 -0
  216. package/lib/typescript/src/engine/pack.d.ts.map +1 -0
  217. package/lib/typescript/src/engine/rectangles.d.ts +9 -0
  218. package/lib/typescript/src/engine/rectangles.d.ts.map +1 -0
  219. package/lib/typescript/src/engine/sameLayout.d.ts +4 -0
  220. package/lib/typescript/src/engine/sameLayout.d.ts.map +1 -0
  221. package/lib/typescript/src/engine/search.d.ts +28 -0
  222. package/lib/typescript/src/engine/search.d.ts.map +1 -0
  223. package/lib/typescript/src/engine/spatial.d.ts +16 -0
  224. package/lib/typescript/src/engine/spatial.d.ts.map +1 -0
  225. package/lib/typescript/src/engine/spatialAppRotation.d.ts +4 -0
  226. package/lib/typescript/src/engine/spatialAppRotation.d.ts.map +1 -0
  227. package/lib/typescript/src/engine/spatialExchange.d.ts +5 -0
  228. package/lib/typescript/src/engine/spatialExchange.d.ts.map +1 -0
  229. package/lib/typescript/src/engine/spatialInsertion.d.ts +5 -0
  230. package/lib/typescript/src/engine/spatialInsertion.d.ts.map +1 -0
  231. package/lib/typescript/src/engine/spatialPolicy.d.ts +9 -0
  232. package/lib/typescript/src/engine/spatialPolicy.d.ts.map +1 -0
  233. package/lib/typescript/src/engine/spatialRotation.d.ts +8 -0
  234. package/lib/typescript/src/engine/spatialRotation.d.ts.map +1 -0
  235. package/lib/typescript/src/engine/validation.d.ts +9 -0
  236. package/lib/typescript/src/engine/validation.d.ts.map +1 -0
  237. package/lib/typescript/src/gesture/DndGestureGroup.d.ts +33 -0
  238. package/lib/typescript/src/gesture/DndGestureGroup.d.ts.map +1 -0
  239. package/lib/typescript/src/gesture/DndGestureHost.d.ts +12 -0
  240. package/lib/typescript/src/gesture/DndGestureHost.d.ts.map +1 -0
  241. package/lib/typescript/src/gesture/gestureConfig.d.ts +15 -0
  242. package/lib/typescript/src/gesture/gestureConfig.d.ts.map +1 -0
  243. package/lib/typescript/src/gesture/gestureGrouping.d.ts +66 -0
  244. package/lib/typescript/src/gesture/gestureGrouping.d.ts.map +1 -0
  245. package/lib/typescript/src/gesture/gestureHost.d.ts +23 -0
  246. package/lib/typescript/src/gesture/gestureHost.d.ts.map +1 -0
  247. package/lib/typescript/src/index.d.ts +38 -0
  248. package/lib/typescript/src/index.d.ts.map +1 -0
  249. package/lib/typescript/src/pointer/dndTarget.d.ts +14 -0
  250. package/lib/typescript/src/pointer/dndTarget.d.ts.map +1 -0
  251. package/lib/typescript/src/pointer/geometry.d.ts +28 -0
  252. package/lib/typescript/src/pointer/geometry.d.ts.map +1 -0
  253. package/lib/typescript/src/pointer/layoutTarget.d.ts +43 -0
  254. package/lib/typescript/src/pointer/layoutTarget.d.ts.map +1 -0
  255. package/lib/typescript/src/pointer/listCellMotion.d.ts +43 -0
  256. package/lib/typescript/src/pointer/listCellMotion.d.ts.map +1 -0
  257. package/lib/typescript/src/pointer/listTarget.d.ts +7 -0
  258. package/lib/typescript/src/pointer/listTarget.d.ts.map +1 -0
  259. package/lib/typescript/src/pointer/motion.d.ts +20 -0
  260. package/lib/typescript/src/pointer/motion.d.ts.map +1 -0
  261. package/lib/typescript/src/runtime/dndPresentation.d.ts +37 -0
  262. package/lib/typescript/src/runtime/dndPresentation.d.ts.map +1 -0
  263. package/lib/typescript/src/runtime/dndRenderStore.d.ts +46 -0
  264. package/lib/typescript/src/runtime/dndRenderStore.d.ts.map +1 -0
  265. package/lib/typescript/src/runtime/dndRuntime.d.ts +138 -0
  266. package/lib/typescript/src/runtime/dndRuntime.d.ts.map +1 -0
  267. package/lib/typescript/src/runtime/dndSettlement.d.ts +18 -0
  268. package/lib/typescript/src/runtime/dndSettlement.d.ts.map +1 -0
  269. package/lib/typescript/src/runtime/gridRuntime.d.ts +54 -0
  270. package/lib/typescript/src/runtime/gridRuntime.d.ts.map +1 -0
  271. package/lib/typescript/src/types.d.ts +180 -0
  272. package/lib/typescript/src/types.d.ts.map +1 -0
  273. package/package.json +136 -0
  274. package/src/adapters/coordinates.ts +181 -0
  275. package/src/adapters/scroll.ts +147 -0
  276. package/src/adapters/virtualizedList.ts +112 -0
  277. package/src/components/DndProvider.tsx +1836 -0
  278. package/src/components/DragHandle.tsx +89 -0
  279. package/src/components/GridController.tsx +213 -0
  280. package/src/components/GridDragHandle.tsx +11 -0
  281. package/src/components/GridZoneView.tsx +57 -0
  282. package/src/components/SortableGrid.tsx +449 -0
  283. package/src/components/SortableList.tsx +1028 -0
  284. package/src/components/dndTypes.ts +102 -0
  285. package/src/components/gridTypes.ts +90 -0
  286. package/src/components/handleViewProps.ts +35 -0
  287. package/src/contracts.ts +149 -0
  288. package/src/controller/approval.ts +179 -0
  289. package/src/controller/coordinatorBase.ts +118 -0
  290. package/src/controller/dndSession.ts +1058 -0
  291. package/src/controller/dndState.ts +198 -0
  292. package/src/controller/gridAdapter.ts +268 -0
  293. package/src/controller/gridState.ts +187 -0
  294. package/src/controller/locations.ts +67 -0
  295. package/src/controller/previewPath.ts +90 -0
  296. package/src/controller/sessionCore.ts +390 -0
  297. package/src/controller/useDndState.ts +26 -0
  298. package/src/controller/useGridState.ts +22 -0
  299. package/src/controller/useStoreState.ts +27 -0
  300. package/src/defaults.ts +4 -0
  301. package/src/diagnostics.ts +23 -0
  302. package/src/engine/gridPlacements.ts +85 -0
  303. package/src/engine/index.ts +99 -0
  304. package/src/engine/layout.ts +29 -0
  305. package/src/engine/layoutMove.ts +401 -0
  306. package/src/engine/layoutState.ts +490 -0
  307. package/src/engine/list.ts +307 -0
  308. package/src/engine/listIndex.ts +174 -0
  309. package/src/engine/move.ts +274 -0
  310. package/src/engine/movementPolicy.ts +151 -0
  311. package/src/engine/orderedTarget.ts +131 -0
  312. package/src/engine/pack.ts +24 -0
  313. package/src/engine/rectangles.ts +18 -0
  314. package/src/engine/sameLayout.ts +50 -0
  315. package/src/engine/search.ts +174 -0
  316. package/src/engine/spatial.ts +36 -0
  317. package/src/engine/spatialAppRotation.ts +53 -0
  318. package/src/engine/spatialExchange.ts +212 -0
  319. package/src/engine/spatialInsertion.ts +157 -0
  320. package/src/engine/spatialPolicy.ts +48 -0
  321. package/src/engine/spatialRotation.ts +56 -0
  322. package/src/engine/validation.ts +358 -0
  323. package/src/gesture/DndGestureGroup.tsx +357 -0
  324. package/src/gesture/DndGestureHost.tsx +50 -0
  325. package/src/gesture/gestureConfig.ts +123 -0
  326. package/src/gesture/gestureGrouping.ts +210 -0
  327. package/src/gesture/gestureHost.tsx +163 -0
  328. package/src/index.ts +162 -0
  329. package/src/pointer/dndTarget.ts +213 -0
  330. package/src/pointer/geometry.ts +584 -0
  331. package/src/pointer/layoutTarget.ts +515 -0
  332. package/src/pointer/listCellMotion.ts +174 -0
  333. package/src/pointer/listTarget.ts +56 -0
  334. package/src/pointer/motion.ts +66 -0
  335. package/src/runtime/dndPresentation.ts +113 -0
  336. package/src/runtime/dndRenderStore.ts +246 -0
  337. package/src/runtime/dndRuntime.ts +311 -0
  338. package/src/runtime/dndSettlement.ts +117 -0
  339. package/src/runtime/gridRuntime.ts +73 -0
  340. package/src/types.ts +189 -0
@@ -0,0 +1,56 @@
1
+ import { screenToContent } from '../adapters/coordinates';
2
+ import type { ListLocation } from '../contracts';
3
+ import { computeListIndex } from '../engine/list';
4
+ import { findListTargetIndex } from '../engine/listIndex';
5
+ import type { DndMotion, ListRegistration } from '../runtime/dndRuntime';
6
+ import { rectContains } from './geometry';
7
+
8
+ /** Target from committed order; candidate reflow must not move its own thresholds. */
9
+ export function listTarget(
10
+ state: DndMotion,
11
+ registrations: ReadonlyMap<
12
+ string,
13
+ ListRegistration & { layoutVersion: number }
14
+ >,
15
+ ): ListLocation | null {
16
+ if (!rectContains(state.root, state.pointer.x, state.pointer.y)) return null;
17
+ for (const measured of state.zones) {
18
+ const registration = registrations.get(measured.zoneId);
19
+ if (
20
+ !registration ||
21
+ registration.revision !== state.revision ||
22
+ registration.layout.revision !== state.revision ||
23
+ registration.layoutVersion !== measured.layoutVersion ||
24
+ !rectContains(measured.viewport.rect, state.pointer.x, state.pointer.y)
25
+ )
26
+ continue;
27
+ const vertical = registration.orientation === 'vertical';
28
+ const center = {
29
+ x: state.pointer.x - state.grip.x + state.width / 2,
30
+ y: state.pointer.y - state.grip.y + state.height / 2,
31
+ };
32
+ const content = screenToContent(
33
+ center,
34
+ {
35
+ viewport: measured.viewport,
36
+ scrollOffset: vertical
37
+ ? { x: 0, y: measured.offset }
38
+ : { x: measured.offset, y: 0 },
39
+ },
40
+ { revision: state.revision },
41
+ );
42
+ if (!content.valid) return null;
43
+ const mainAxisCenter = vertical ? content.value.y : content.value.x;
44
+ const index = registration.targetIndex
45
+ ? findListTargetIndex(
46
+ registration.targetIndex,
47
+ state.itemId,
48
+ mainAxisCenter,
49
+ )
50
+ : computeListIndex(registration.layout, state.itemId, mainAxisCenter);
51
+ return index === null
52
+ ? null
53
+ : { kind: 'list', zoneId: measured.zoneId, index };
54
+ }
55
+ return null;
56
+ }
@@ -0,0 +1,66 @@
1
+ import type { ValidationIssue } from '../types';
2
+
3
+ export type GridMotionConfig = {
4
+ /** Duration in milliseconds for item movement and preview settling. Defaults to 180. */
5
+ durationMs?: number;
6
+ /** Respect the device accessibility setting by default. */
7
+ reduceMotion?: 'system' | 'always' | 'never';
8
+ };
9
+
10
+ type ResolvedGridMotionConfig = Required<GridMotionConfig>;
11
+
12
+ export const DEFAULT_GRID_MOTION: Readonly<ResolvedGridMotionConfig> =
13
+ Object.freeze({ durationMs: 180, reduceMotion: 'system' });
14
+
15
+ type GridMotionResult =
16
+ | { valid: true; motion: ResolvedGridMotionConfig }
17
+ | { valid: false; issues: ValidationIssue[] };
18
+
19
+ /** Validate before sharing animation settings with the UI runtime. */
20
+ export function resolveGridMotion(value: unknown): GridMotionResult {
21
+ const fail = (message: string): GridMotionResult => ({
22
+ valid: false,
23
+ issues: [{ code: 'invalid-configuration', message }],
24
+ });
25
+ if (value === undefined)
26
+ return { valid: true, motion: { ...DEFAULT_GRID_MOTION } };
27
+ if (!value || typeof value !== 'object' || Array.isArray(value))
28
+ return fail('motion must be an object with durationMs and reduceMotion.');
29
+ const config = value as Record<string, unknown>;
30
+ if (
31
+ Object.keys(config).some(
32
+ key => key !== 'durationMs' && key !== 'reduceMotion',
33
+ )
34
+ )
35
+ return fail('motion supports only durationMs and reduceMotion.');
36
+
37
+ const durationMs =
38
+ config.durationMs === undefined
39
+ ? DEFAULT_GRID_MOTION.durationMs
40
+ : config.durationMs;
41
+ if (
42
+ typeof durationMs !== 'number' ||
43
+ !Number.isFinite(durationMs) ||
44
+ durationMs < 0
45
+ )
46
+ return fail('motion.durationMs must be a finite non-negative number.');
47
+
48
+ const reduceMotion =
49
+ config.reduceMotion === undefined
50
+ ? DEFAULT_GRID_MOTION.reduceMotion
51
+ : config.reduceMotion;
52
+ if (
53
+ reduceMotion !== 'system' &&
54
+ reduceMotion !== 'always' &&
55
+ reduceMotion !== 'never'
56
+ )
57
+ return fail('motion.reduceMotion must be system, always or never.');
58
+
59
+ return {
60
+ valid: true,
61
+ motion: {
62
+ durationMs: reduceMotion === 'always' ? 0 : durationMs,
63
+ reduceMotion,
64
+ },
65
+ };
66
+ }
@@ -0,0 +1,113 @@
1
+ import type { LayoutLocation, LayoutRect } from '../contracts';
2
+
3
+ /**
4
+ * Only validated numeric geometry crosses into the UI runtime. Coordinates
5
+ * form one typed array so a zone table crosses runtimes as a single buffer;
6
+ * the slot map is reused while a zone keeps the same items, so repeated
7
+ * candidates serialize only the coordinates.
8
+ */
9
+ export type DndZonePresentation = {
10
+ /** Prefixed item ID → slot; four coordinates per slot in `rects`. */
11
+ slots: Readonly<Record<string, number>>;
12
+ /** x, y, width and height of each slot, in zone content coordinates. */
13
+ rects: Float64Array;
14
+ };
15
+
16
+ export type DndPresentation = {
17
+ token: number;
18
+ revision: number;
19
+ zones: Record<string, DndZonePresentation>;
20
+ /** Last policy-approved preview, usable for motion before JS acknowledges release. */
21
+ releaseTarget?: {
22
+ target: LayoutLocation;
23
+ policyRevision: number;
24
+ /** Final pointer already validated by JS; controlled state may still be pending. */
25
+ releasedSeq?: number;
26
+ };
27
+ };
28
+
29
+ export const EMPTY_DND_PRESENTATION: DndPresentation = {
30
+ token: 0,
31
+ revision: 0,
32
+ zones: {},
33
+ };
34
+
35
+ export function presentationRect(
36
+ presentation: DndPresentation | undefined,
37
+ zoneId: string,
38
+ itemId: string,
39
+ ): LayoutRect | undefined {
40
+ 'worklet';
41
+ const zone = presentation?.zones[`#${zoneId}`];
42
+ if (zone === undefined) return undefined;
43
+ const slot = zone.slots[`#${itemId}`];
44
+ if (slot === undefined) return undefined;
45
+ const offset = slot * 4;
46
+ return {
47
+ x: zone.rects[offset],
48
+ y: zone.rects[offset + 1],
49
+ width: zone.rects[offset + 2],
50
+ height: zone.rects[offset + 3],
51
+ };
52
+ }
53
+
54
+ /**
55
+ * Build a zone table from item rects. The previous table's slot map is kept
56
+ * when it lists exactly these items, so unchanged maps keep their identity.
57
+ */
58
+ export function createZonePresentation(
59
+ entries: ReadonlyArray<readonly [itemId: string, rect: LayoutRect]>,
60
+ previous?: DndZonePresentation | null,
61
+ ): DndZonePresentation {
62
+ let slots = previous?.slots;
63
+ let count = slots ? Object.keys(slots).length : 0;
64
+ if (
65
+ !slots ||
66
+ count !== entries.length ||
67
+ entries.some(([itemId]) => slots![`#${itemId}`] === undefined)
68
+ ) {
69
+ const next: Record<string, number> = {};
70
+ count = 0;
71
+ for (const [itemId] of entries) {
72
+ const key = `#${itemId}`;
73
+ if (next[key] === undefined) next[key] = count++;
74
+ }
75
+ slots = next;
76
+ }
77
+ const rects = new Float64Array(count * 4);
78
+ for (const [itemId, rect] of entries) {
79
+ const offset = slots[`#${itemId}`] * 4;
80
+ rects[offset] = rect.x;
81
+ rects[offset + 1] = rect.y;
82
+ rects[offset + 2] = rect.width;
83
+ rects[offset + 3] = rect.height;
84
+ }
85
+ return { slots, rects };
86
+ }
87
+
88
+ /** Build a zone table from a `#itemId` keyed rect record. */
89
+ export function zonePresentationFromRects(
90
+ rects: Readonly<Record<string, LayoutRect>>,
91
+ ): DndZonePresentation {
92
+ return createZonePresentation(
93
+ Object.entries(rects).map(([key, rect]) => [key.slice(1), rect] as const),
94
+ );
95
+ }
96
+
97
+ /** Read a zone table back as a `#itemId` keyed rect record. */
98
+ export function zonePresentationRects(
99
+ zone: DndZonePresentation | undefined,
100
+ ): Record<string, LayoutRect> {
101
+ const result: Record<string, LayoutRect> = {};
102
+ if (!zone) return result;
103
+ for (const [key, slot] of Object.entries(zone.slots)) {
104
+ const offset = slot * 4;
105
+ result[key] = {
106
+ x: zone.rects[offset],
107
+ y: zone.rects[offset + 1],
108
+ width: zone.rects[offset + 2],
109
+ height: zone.rects[offset + 3],
110
+ };
111
+ }
112
+ return result;
113
+ }
@@ -0,0 +1,246 @@
1
+ import type { DndItem, LayoutZone } from '../contracts';
2
+ import type { DndSessionSnapshot } from '../controller/dndSession';
3
+
4
+ type DndSnapshotSource<T = unknown> = {
5
+ getSnapshot(): DndSessionSnapshot<T>;
6
+ subscribe(listener: () => void): () => void;
7
+ };
8
+
9
+ export type DndZoneSnapshot = {
10
+ zone: LayoutZone | undefined;
11
+ display: LayoutZone | undefined;
12
+ items: readonly DndItem<unknown>[] | undefined;
13
+ revision: number;
14
+ displayRevision: number;
15
+ activeItemId: string | null;
16
+ scrollEnabled: boolean;
17
+ };
18
+
19
+ type DndItemSnapshot = {
20
+ index: number;
21
+ row?: number;
22
+ col?: number;
23
+ };
24
+
25
+ /**
26
+ * Answers once per drag session whether a zone keeps its native slots in
27
+ * committed order while `activeItemId` is dragged. Adapters whose mounted set
28
+ * depends on slot order (windowed lists) answer from their render window.
29
+ */
30
+ export type RetainSlotsRule = (activeItemId: string) => boolean;
31
+
32
+ /** Native cells keep their slots during a preview. Shared geometry moves them. */
33
+ function retainNativeSlots(
34
+ previous: LayoutZone | undefined,
35
+ next: LayoutZone | undefined,
36
+ ): LayoutZone | undefined {
37
+ if (!previous || !next || previous.kind !== next.kind) return next;
38
+ if (previous === next) return previous;
39
+ if (previous.kind === 'list' && next.kind === 'list') {
40
+ if (
41
+ previous.orientation !== next.orientation ||
42
+ previous.itemIds.length !== next.itemIds.length
43
+ )
44
+ return next;
45
+ const ids = new Set(previous.itemIds);
46
+ return next.itemIds.every(id => ids.has(id)) ? previous : next;
47
+ }
48
+ if (previous.kind === 'grid' && next.kind === 'grid') {
49
+ if (
50
+ previous.rows !== next.rows ||
51
+ previous.columns !== next.columns ||
52
+ previous.placements.length !== next.placements.length
53
+ )
54
+ return next;
55
+ const placements = new Map(
56
+ previous.placements.map(placement => [placement.itemId, placement]),
57
+ );
58
+ return next.placements.every(placement => {
59
+ const old = placements.get(placement.itemId);
60
+ return (
61
+ old &&
62
+ old.span.rows === placement.span.rows &&
63
+ old.span.cols === placement.span.cols &&
64
+ old.placement === placement.placement
65
+ );
66
+ })
67
+ ? previous
68
+ : next;
69
+ }
70
+ return next;
71
+ }
72
+
73
+ /** Separate React subscriptions from frame-by-frame approved presentation. */
74
+ export class DndRenderStore<T = unknown> {
75
+ private provider: DndSessionSnapshot<T> | undefined;
76
+ private zones = new Map<string, DndZoneSnapshot>();
77
+ private itemIndices = new WeakMap<LayoutZone, Map<string, DndItemSnapshot>>();
78
+ private items = new Map<string, Map<string, DndItemSnapshot>>();
79
+ private retainDecisions = new Map<
80
+ string,
81
+ { sessionId: string | null; itemId: string; retain: boolean }
82
+ >();
83
+
84
+ constructor(private readonly source: DndSnapshotSource<T>) {}
85
+
86
+ getSnapshot = (): DndSessionSnapshot<T> => this.source.getSnapshot();
87
+
88
+ getProviderSnapshot = (): DndSessionSnapshot<T> => {
89
+ const next = this.source.getSnapshot();
90
+ const previous = this.provider;
91
+ if (
92
+ previous &&
93
+ previous.value === next.value &&
94
+ previous.phase === next.phase &&
95
+ previous.sessionId === next.sessionId &&
96
+ previous.itemId === next.itemId &&
97
+ previous.sourceZoneId === next.sourceZoneId &&
98
+ previous.targetZoneId === next.targetZoneId &&
99
+ previous.validity === next.validity &&
100
+ previous.disabled === next.disabled
101
+ )
102
+ return previous;
103
+ return (this.provider = next);
104
+ };
105
+
106
+ getZoneSnapshot = (
107
+ zoneId: string,
108
+ preserveSlots: boolean | RetainSlotsRule = true,
109
+ ): DndZoneSnapshot => {
110
+ const current = this.source.getSnapshot();
111
+ const zone = current.value?.zones.find(entry => entry.id === zoneId);
112
+ const items = current.value?.items;
113
+ const revision = current.value?.revision ?? 0;
114
+ const key = `${preserveSlots === false ? 'ordered' : 'stable'}:${zoneId}`;
115
+ const previous = this.zones.get(key);
116
+ const candidate = current.displayValue?.zones.find(
117
+ entry => entry.id === zoneId,
118
+ );
119
+ const display =
120
+ current.phase !== 'idle' && this.retainSlots(key, current, preserveSlots)
121
+ ? retainNativeSlots(
122
+ previous &&
123
+ previous.zone === zone &&
124
+ previous.items === items &&
125
+ previous.revision === revision
126
+ ? previous.display
127
+ : zone,
128
+ candidate,
129
+ )
130
+ : candidate;
131
+ // The revision of the value the display comes from: candidate geometry is
132
+ // prepared under the candidate's revision, exactly as the provider asks
133
+ // the adapter to prepare it for the UI runtime.
134
+ const displayRevision =
135
+ display === zone
136
+ ? revision
137
+ : display === candidate
138
+ ? (current.displayValue?.revision ?? revision)
139
+ : (previous?.displayRevision ?? revision);
140
+ const next: DndZoneSnapshot = {
141
+ zone,
142
+ display,
143
+ items,
144
+ revision,
145
+ displayRevision,
146
+ activeItemId: current.itemId,
147
+ scrollEnabled: current.phase === 'idle',
148
+ };
149
+ if (
150
+ previous &&
151
+ (Object.keys(next) as Array<keyof DndZoneSnapshot>).every(
152
+ field => previous[field] === next[field],
153
+ )
154
+ )
155
+ return previous;
156
+ this.zones.set(key, next);
157
+ return next;
158
+ };
159
+
160
+ /** A rule is consulted once per drag session so its answer cannot flip mid-drag. */
161
+ private retainSlots(
162
+ key: string,
163
+ current: DndSessionSnapshot<T>,
164
+ preserveSlots: boolean | RetainSlotsRule,
165
+ ): boolean {
166
+ if (typeof preserveSlots !== 'function') return preserveSlots;
167
+ if (current.itemId === null) return true;
168
+ const decision = this.retainDecisions.get(key);
169
+ if (
170
+ decision &&
171
+ decision.sessionId === current.sessionId &&
172
+ decision.itemId === current.itemId
173
+ )
174
+ return decision.retain;
175
+ const retain = preserveSlots(current.itemId);
176
+ this.retainDecisions.set(key, {
177
+ sessionId: current.sessionId,
178
+ itemId: current.itemId,
179
+ retain,
180
+ });
181
+ return retain;
182
+ }
183
+
184
+ getItemSnapshot = (
185
+ zoneId: string,
186
+ itemId: string,
187
+ ): DndItemSnapshot | undefined => {
188
+ const zone = this.source
189
+ .getSnapshot()
190
+ .displayValue?.zones.find(entry => entry.id === zoneId);
191
+ let indices = zone && this.itemIndices.get(zone);
192
+ if (zone && !indices) {
193
+ const prepared = new Map<string, DndItemSnapshot>();
194
+ if (zone.kind === 'list')
195
+ zone.itemIds.forEach((id, index) => prepared.set(id, { index }));
196
+ else
197
+ zone.placements.forEach((entry, index) =>
198
+ prepared.set(entry.itemId, {
199
+ index,
200
+ row: entry.position.row,
201
+ col: entry.position.col,
202
+ }),
203
+ );
204
+ // Many mounted cells read the same approved display in one notification.
205
+ // Build its ID index once instead of scanning the zone for every cell.
206
+ this.itemIndices.set(zone, prepared);
207
+ indices = prepared;
208
+ }
209
+ const next = indices?.get(itemId);
210
+ let selected = this.items.get(zoneId);
211
+ if (!next) {
212
+ selected?.delete(itemId);
213
+ return undefined;
214
+ }
215
+ const previous = selected?.get(itemId);
216
+ if (
217
+ previous &&
218
+ previous.index === next.index &&
219
+ previous.row === next.row &&
220
+ previous.col === next.col
221
+ )
222
+ return previous;
223
+ if (!selected) {
224
+ selected = new Map();
225
+ this.items.set(zoneId, selected);
226
+ }
227
+ selected.set(itemId, next);
228
+ return next;
229
+ };
230
+
231
+ subscribeSelection<Selection>(
232
+ get: () => Selection,
233
+ listener: () => void,
234
+ ): () => void {
235
+ let previous = get();
236
+ return this.source.subscribe(() => {
237
+ const next = get();
238
+ if (Object.is(previous, next)) return;
239
+ previous = next;
240
+ listener();
241
+ });
242
+ }
243
+
244
+ subscribeProvider = (listener: () => void): (() => void) =>
245
+ this.subscribeSelection(this.getProviderSnapshot, listener);
246
+ }