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,85 @@
1
+ import type { GridPlacement } from '../contracts';
2
+ import type { CellPosition, CellSpan } from '../types';
3
+
4
+ export function samePlacement(
5
+ first: GridPlacement,
6
+ second: GridPlacement,
7
+ ): boolean {
8
+ return (
9
+ first.itemId === second.itemId &&
10
+ first.position.row === second.position.row &&
11
+ first.position.col === second.position.col &&
12
+ first.span.rows === second.span.rows &&
13
+ first.span.cols === second.span.cols &&
14
+ first.placement === second.placement
15
+ );
16
+ }
17
+
18
+ /** Slot grid of an ordered zone: reading-order cells of one item span. */
19
+ export type OrderedSlots = { perRow: number; rows: number };
20
+
21
+ export function orderedSlots(
22
+ zone: { rows: number; columns: number },
23
+ itemSpan: CellSpan,
24
+ ): OrderedSlots {
25
+ return {
26
+ perRow: Math.floor(zone.columns / itemSpan.cols),
27
+ rows: Math.floor(zone.rows / itemSpan.rows),
28
+ };
29
+ }
30
+
31
+ /** The cell an ordered slot starts at; the caller guarantees `perRow > 0`. */
32
+ export function slotPosition(
33
+ index: number,
34
+ itemSpan: CellSpan,
35
+ perRow: number,
36
+ ): CellPosition {
37
+ return {
38
+ row: Math.floor(index / perRow) * itemSpan.rows,
39
+ col: (index % perRow) * itemSpan.cols,
40
+ };
41
+ }
42
+
43
+ /** The reading-order slot a position starts, or null when it is not a slot origin. */
44
+ export function slotIndex(
45
+ position: CellPosition,
46
+ itemSpan: CellSpan,
47
+ perRow: number,
48
+ ): number | null {
49
+ if (
50
+ perRow <= 0 ||
51
+ position.row < 0 ||
52
+ position.col < 0 ||
53
+ position.row % itemSpan.rows !== 0 ||
54
+ position.col % itemSpan.cols !== 0
55
+ )
56
+ return null;
57
+ const col = position.col / itemSpan.cols;
58
+ if (col >= perRow) return null;
59
+ return (position.row / itemSpan.rows) * perRow + col;
60
+ }
61
+
62
+ /**
63
+ * Give ordered placements their reading-order positions, reusing an existing
64
+ * placement object whenever the item keeps the same slot.
65
+ */
66
+ export function packOrderedPlacements(
67
+ placements: readonly GridPlacement[],
68
+ itemSpan: CellSpan,
69
+ perRow: number,
70
+ previous: readonly GridPlacement[] = [],
71
+ ): GridPlacement[] {
72
+ const existing = new Map(previous.map(item => [item.itemId, item]));
73
+ return placements.map((placement, index) => {
74
+ const packed: GridPlacement = {
75
+ itemId: placement.itemId,
76
+ position: slotPosition(index, itemSpan, perRow),
77
+ span: { rows: itemSpan.rows, cols: itemSpan.cols },
78
+ ...(placement.placement === undefined
79
+ ? {}
80
+ : { placement: placement.placement }),
81
+ };
82
+ const before = existing.get(placement.itemId);
83
+ return before && samePlacement(before, packed) ? before : packed;
84
+ });
85
+ }
@@ -0,0 +1,99 @@
1
+ export { computeZoneLayout } from './layout';
2
+ export { resolveListVirtualization } from '../adapters/virtualizedList';
3
+ export type {
4
+ ListVirtualizationOptions,
5
+ ListVirtualizationResult,
6
+ } from '../adapters/virtualizedList';
7
+ export { computeListLayout, computeListIndex, ListSizeCache } from './list';
8
+ export type {
9
+ ListLayout,
10
+ ListLayoutEntry,
11
+ ListLayoutInput,
12
+ ListLayoutResult,
13
+ ListMeasurementContext,
14
+ } from './list';
15
+ export { computeLayoutMove } from './layoutMove';
16
+ export type {
17
+ GridItemLayout,
18
+ LayoutMoveInput,
19
+ LayoutMoveResult,
20
+ } from './layoutMove';
21
+ export {
22
+ createDndStateStore,
23
+ respondToDndProposal,
24
+ } from '../controller/dndState';
25
+ export type {
26
+ DndValue,
27
+ DndProposal,
28
+ DndProposalResult,
29
+ DndStateStore,
30
+ DndStateUpdate,
31
+ } from '../controller/dndState';
32
+ export {
33
+ computeAutoScroll,
34
+ constrainDragPoint,
35
+ validateAutoScrollOptions,
36
+ } from '../adapters/scroll';
37
+ export type {
38
+ AutoScrollInput,
39
+ AutoScrollOptions,
40
+ AutoScrollOptionsValidationResult,
41
+ AutoScrollResult,
42
+ DragAxis,
43
+ } from '../adapters/scroll';
44
+ export type * from '../contracts';
45
+ export { validateLayoutState, fromGridValue, toGridValue } from './layoutState';
46
+ export {
47
+ screenToContent,
48
+ contentToScreen,
49
+ screenRectToContent,
50
+ contentRectToScreen,
51
+ validateRectMeasurement,
52
+ } from '../adapters/coordinates';
53
+ export { DEFAULT_SEARCH_BUDGET } from '../defaults';
54
+ export {
55
+ createGridStateStore,
56
+ respondToGridProposal,
57
+ } from '../controller/gridState';
58
+ export type {
59
+ GridStateStore,
60
+ GridStateUpdate,
61
+ GridProposalResult,
62
+ } from '../controller/gridState';
63
+ export type { GridProposal, RevisionedGridValue } from '../types';
64
+ export { computeMove } from './move';
65
+ export {
66
+ DEFAULT_GRID_MOVEMENT_POLICY,
67
+ HOME_GRID_MOVEMENT_POLICY,
68
+ resolveGridMovementPolicy,
69
+ validateGridMovementPolicy,
70
+ } from './movementPolicy';
71
+ export type {
72
+ GridMoveCandidate,
73
+ GridMovementPolicy,
74
+ ResolvedGridMovementPolicy,
75
+ } from './movementPolicy';
76
+ export type { OrderedIndexInput, OrderedIndexResult } from './orderedTarget';
77
+ export { computeOrderedIndex } from './orderedTarget';
78
+ export { packItems } from './pack';
79
+ export { hasSameLayout } from './sameLayout';
80
+ export { validateValue } from './validation';
81
+ export type {
82
+ CellPosition,
83
+ CellSpan,
84
+ GridItem,
85
+ GridValue,
86
+ GridZone,
87
+ ItemLocation,
88
+ ItemPlacement,
89
+ MoveInput,
90
+ MoveResult,
91
+ OrderedZone,
92
+ PackInput,
93
+ PackResult,
94
+ PositionedItem,
95
+ SpatialZone,
96
+ ValidationIssue,
97
+ ValidationResult,
98
+ ZoneLayoutResult,
99
+ } from '../types';
@@ -0,0 +1,29 @@
1
+ import type { GridZone, ZoneLayoutResult } from '../types';
2
+
3
+ import { validateZone } from './validation';
4
+
5
+ /** Compute cell positions only. This never packs or changes the supplied zone. */
6
+ export function computeZoneLayout<T>(zone: GridZone<T>): ZoneLayoutResult<T> {
7
+ const validation = validateZone(zone);
8
+ if (!validation.valid) {
9
+ return validation;
10
+ }
11
+ if (zone.strategy === 'spatial') {
12
+ return { valid: true, items: zone.items.slice() };
13
+ }
14
+ // A valid empty zone can have zero slots; avoid any division in that case.
15
+ if (zone.items.length === 0) {
16
+ return { valid: true, items: [] };
17
+ }
18
+ const slotsPerRow = Math.floor(zone.columns / zone.itemSpan.cols);
19
+ return {
20
+ valid: true,
21
+ items: zone.items.map((item, index) => ({
22
+ ...item,
23
+ position: {
24
+ row: Math.floor(index / slotsPerRow) * zone.itemSpan.rows,
25
+ col: (index % slotsPerRow) * zone.itemSpan.cols,
26
+ },
27
+ })),
28
+ };
29
+ }
@@ -0,0 +1,401 @@
1
+ import type {
2
+ GridPlacement,
3
+ LayoutConversionIssue,
4
+ LayoutLocation,
5
+ LayoutState,
6
+ LayoutZone,
7
+ } from '../contracts';
8
+ import { DEFAULT_SEARCH_BUDGET } from '../defaults';
9
+ import type { CellSpan, ItemPlacement, PositionedItem } from '../types';
10
+
11
+ import {
12
+ orderedSlots,
13
+ packOrderedPlacements,
14
+ samePlacement,
15
+ slotIndex,
16
+ slotPosition,
17
+ } from './gridPlacements';
18
+ import { validateLayoutState } from './layoutState';
19
+ import {
20
+ validateGridMovementPolicy,
21
+ type GridMovementPolicy,
22
+ } from './movementPolicy';
23
+ import { resolveSpatialMove } from './spatial';
24
+ import {
25
+ isCellPosition,
26
+ isCellSpan,
27
+ isPositiveInteger,
28
+ isRecord,
29
+ } from './validation';
30
+
31
+ /** Caller-owned target grid geometry; list membership never stores a grid span. */
32
+ export type GridItemLayout = { span: CellSpan; placement?: ItemPlacement };
33
+
34
+ export type LayoutMoveInput<T> = {
35
+ value: LayoutState<T>;
36
+ itemId: string;
37
+ /** List indices address the target order after removing the active item. */
38
+ to: LayoutLocation;
39
+ /** Required on list-to-grid moves; optional conversion on cross-grid moves. */
40
+ gridItem?: GridItemLayout;
41
+ movementPolicy?: GridMovementPolicy;
42
+ searchBudget?: number;
43
+ };
44
+
45
+ export type LayoutMoveResult<T> =
46
+ | {
47
+ status: 'ok';
48
+ value: LayoutState<T>;
49
+ from: LayoutLocation;
50
+ to: LayoutLocation;
51
+ changed: boolean;
52
+ attempts: number;
53
+ }
54
+ | { status: 'impossible' | 'unresolved'; attempts: number }
55
+ | { status: 'invalid'; issues: LayoutConversionIssue[] };
56
+
57
+ function invalid<T>(
58
+ code: LayoutConversionIssue['code'],
59
+ message: string,
60
+ context: Pick<LayoutConversionIssue, 'itemId' | 'zoneId'> = {},
61
+ ): LayoutMoveResult<T> {
62
+ return { status: 'invalid', issues: [{ code, message, ...context }] };
63
+ }
64
+
65
+ function positioned(placement: GridPlacement): PositionedItem<undefined> {
66
+ return {
67
+ id: placement.itemId,
68
+ span: placement.span,
69
+ position: placement.position,
70
+ ...(placement.placement === undefined
71
+ ? {}
72
+ : { placement: placement.placement }),
73
+ data: undefined,
74
+ };
75
+ }
76
+
77
+ /** Remove an item; an ordered grid closes the gap it leaves. */
78
+ function withoutItem(zone: LayoutZone, itemId: string): LayoutZone {
79
+ if (zone.kind === 'list')
80
+ return { ...zone, itemIds: zone.itemIds.filter(id => id !== itemId) };
81
+ const remaining = zone.placements.filter(item => item.itemId !== itemId);
82
+ if (!zone.itemSpan) return { ...zone, placements: remaining };
83
+ const slots = orderedSlots(zone, zone.itemSpan);
84
+ return {
85
+ ...zone,
86
+ placements: packOrderedPlacements(
87
+ remaining,
88
+ zone.itemSpan,
89
+ slots.perRow,
90
+ zone.placements,
91
+ ),
92
+ };
93
+ }
94
+
95
+ /**
96
+ * Compute an atomic move from one complete baseline. The existing spatial
97
+ * solver owns grid movement policy and bounded search; list membership drops
98
+ * grid geometry. Opaque data is never read, copied, or passed to the solver.
99
+ */
100
+ export function computeLayoutMove<T>(
101
+ input: LayoutMoveInput<T>,
102
+ ): LayoutMoveResult<T> {
103
+ if (!isRecord(input)) {
104
+ return invalid('invalid-structure', 'Expected a layout move input object.');
105
+ }
106
+ const validation = validateLayoutState(input.value);
107
+ if (!validation.valid)
108
+ return { status: 'invalid', issues: validation.issues };
109
+ return computeValidatedLayoutMove(input);
110
+ }
111
+
112
+ /**
113
+ * Internal entry for a caller that has already passed `input.value` through
114
+ * `validateLayoutState` and holds it immutably since (the session keeps its
115
+ * committed value). Only that baseline pass is skipped; the complete candidate
116
+ * is still validated before it is returned.
117
+ */
118
+ export function computeValidatedLayoutMove<T>(
119
+ input: LayoutMoveInput<T>,
120
+ ): LayoutMoveResult<T> {
121
+ const policyValidation = validateGridMovementPolicy(input.movementPolicy);
122
+ if (!policyValidation.valid)
123
+ return { status: 'invalid', issues: policyValidation.issues };
124
+ const searchBudget =
125
+ input.searchBudget === undefined
126
+ ? DEFAULT_SEARCH_BUDGET
127
+ : input.searchBudget;
128
+ if (!isPositiveInteger(searchBudget)) {
129
+ return invalid(
130
+ 'invalid-budget',
131
+ 'searchBudget must be a positive safe integer.',
132
+ );
133
+ }
134
+ if (input.gridItem !== undefined) {
135
+ if (!isRecord(input.gridItem) || !isCellSpan(input.gridItem.span)) {
136
+ return invalid(
137
+ 'invalid-span',
138
+ 'gridItem.span must contain positive safe integer rows and cols.',
139
+ );
140
+ }
141
+ if (
142
+ input.gridItem.placement !== undefined &&
143
+ input.gridItem.placement !== 'insert' &&
144
+ input.gridItem.placement !== 'exchange'
145
+ ) {
146
+ return invalid(
147
+ 'invalid-placement',
148
+ 'gridItem.placement must be insert or exchange when supplied.',
149
+ );
150
+ }
151
+ }
152
+ if (typeof input.itemId !== 'string') {
153
+ return invalid('invalid-structure', 'The moving item ID must be a string.');
154
+ }
155
+ if (!isRecord(input.to) || typeof input.to.zoneId !== 'string') {
156
+ return invalid('invalid-structure', 'A target zone is required.');
157
+ }
158
+ const { value, itemId, to } = input;
159
+ const source = value.zones.find(zone =>
160
+ zone.kind === 'list'
161
+ ? zone.itemIds.includes(itemId)
162
+ : zone.placements.some(placement => placement.itemId === itemId),
163
+ );
164
+ const target = value.zones.find(zone => zone.id === to.zoneId);
165
+ if (!source || !target) {
166
+ return invalid(
167
+ 'invalid-structure',
168
+ 'The item and target zone must exist.',
169
+ {
170
+ itemId,
171
+ zoneId: to.zoneId,
172
+ },
173
+ );
174
+ }
175
+ if (to.kind !== target.kind) {
176
+ return invalid('invalid-structure', 'Target kind must match its zone.', {
177
+ itemId,
178
+ zoneId: target.id,
179
+ });
180
+ }
181
+ const original =
182
+ source.kind === 'grid'
183
+ ? source.placements.find(item => item.itemId === itemId)!
184
+ : undefined;
185
+ const from: LayoutLocation =
186
+ source.kind === 'list'
187
+ ? {
188
+ kind: 'list',
189
+ zoneId: source.id,
190
+ index: source.itemIds.indexOf(itemId),
191
+ }
192
+ : {
193
+ kind: 'grid',
194
+ zoneId: source.id,
195
+ position: { ...original!.position },
196
+ };
197
+ const sameZone = source.id === target.id;
198
+ let nextTarget: LayoutZone;
199
+ let resolvedTo: LayoutLocation;
200
+ let changed: boolean;
201
+ let attempts = 0;
202
+ if (target.kind === 'list' && to.kind === 'list') {
203
+ const remaining = target.itemIds.filter(id => id !== itemId);
204
+ if (
205
+ !Number.isSafeInteger(to.index) ||
206
+ to.index < 0 ||
207
+ to.index > remaining.length
208
+ ) {
209
+ return invalid(
210
+ 'invalid-position',
211
+ 'Insertion index must address the array after removal.',
212
+ { itemId, zoneId: target.id },
213
+ );
214
+ }
215
+ remaining.splice(to.index, 0, itemId);
216
+ nextTarget = { ...target, itemIds: remaining };
217
+ resolvedTo = { kind: 'list', zoneId: target.id, index: to.index };
218
+ changed = !sameZone || from.kind !== 'list' || from.index !== to.index;
219
+ } else if (target.kind === 'grid' && to.kind === 'grid') {
220
+ if (!isCellPosition(to.position)) {
221
+ return invalid(
222
+ 'invalid-position',
223
+ 'A grid target requires safe integer row and col coordinates.',
224
+ { itemId, zoneId: target.id },
225
+ );
226
+ }
227
+ const descriptor = input.gridItem ?? original;
228
+ if (!descriptor) {
229
+ return invalid(
230
+ 'missing-item-span',
231
+ 'A list-to-grid move requires an explicit gridItem descriptor.',
232
+ { itemId, zoneId: target.id },
233
+ );
234
+ }
235
+ if (
236
+ sameZone &&
237
+ original &&
238
+ (descriptor.span.rows !== original.span.rows ||
239
+ descriptor.span.cols !== original.span.cols)
240
+ ) {
241
+ return invalid(
242
+ 'span-mismatch',
243
+ 'An in-grid move cannot change the item footprint.',
244
+ { itemId, zoneId: target.id },
245
+ );
246
+ }
247
+ if (
248
+ to.position.row < 0 ||
249
+ to.position.col < 0 ||
250
+ to.position.row > target.rows - descriptor.span.rows ||
251
+ to.position.col > target.columns - descriptor.span.cols
252
+ ) {
253
+ return { status: 'impossible', attempts };
254
+ }
255
+ if (target.itemSpan) {
256
+ // Ordered grid: the target position names a slot after removal; the
257
+ // zone re-packs in reading order and its capacity is the slot count.
258
+ const { itemSpan } = target;
259
+ if (
260
+ descriptor.span.rows !== itemSpan.rows ||
261
+ descriptor.span.cols !== itemSpan.cols
262
+ )
263
+ return { status: 'impossible', attempts };
264
+ const slots = orderedSlots(target, itemSpan);
265
+ const remaining = target.placements.filter(
266
+ item => item.itemId !== itemId,
267
+ );
268
+ if (
269
+ slots.perRow === 0 ||
270
+ slots.rows === 0 ||
271
+ Math.ceil((remaining.length + 1) / slots.perRow) > slots.rows
272
+ )
273
+ return { status: 'impossible', attempts };
274
+ const index = slotIndex(to.position, itemSpan, slots.perRow);
275
+ if (index === null || index > remaining.length)
276
+ return invalid(
277
+ 'invalid-position',
278
+ 'An ordered target must address a slot after removal.',
279
+ { itemId, zoneId: target.id },
280
+ );
281
+ remaining.splice(index, 0, {
282
+ itemId,
283
+ position: slotPosition(index, itemSpan, slots.perRow),
284
+ span: { rows: itemSpan.rows, cols: itemSpan.cols },
285
+ ...(descriptor.placement === undefined
286
+ ? {}
287
+ : { placement: descriptor.placement }),
288
+ });
289
+ const placements = packOrderedPlacements(
290
+ remaining,
291
+ itemSpan,
292
+ slots.perRow,
293
+ target.placements,
294
+ );
295
+ nextTarget = { ...target, placements };
296
+ resolvedTo = {
297
+ kind: 'grid',
298
+ zoneId: target.id,
299
+ position: slotPosition(index, itemSpan, slots.perRow),
300
+ };
301
+ changed =
302
+ !sameZone ||
303
+ placements.length !== target.placements.length ||
304
+ placements.some(
305
+ (placement, position) =>
306
+ !samePlacement(placement, target.placements[position]),
307
+ );
308
+ } else {
309
+ const active: PositionedItem<undefined> = {
310
+ id: itemId,
311
+ data: undefined,
312
+ position: { ...to.position },
313
+ span: { ...descriptor.span },
314
+ ...(descriptor.placement === undefined
315
+ ? {}
316
+ : { placement: descriptor.placement }),
317
+ };
318
+ const result = resolveSpatialMove({
319
+ items: target.placements
320
+ .filter(item => item.itemId !== itemId)
321
+ .map(positioned),
322
+ active,
323
+ origin: sameZone && original ? positioned(original) : undefined,
324
+ rows: target.rows,
325
+ columns: target.columns,
326
+ searchBudget,
327
+ movementPolicy: input.movementPolicy,
328
+ });
329
+ if (result.status !== 'ok') return result;
330
+ attempts = result.attempts;
331
+ const computed = new Map(result.placements.map(item => [item.id, item]));
332
+ const targetIds = target.placements.map(item => item.itemId);
333
+ if (!sameZone) targetIds.push(itemId);
334
+ const existing = new Map(
335
+ target.placements.map(item => [item.itemId, item]),
336
+ );
337
+ const placements = targetIds.map(id => {
338
+ const item = computed.get(id)!;
339
+ const placement: GridPlacement = {
340
+ itemId: item.id,
341
+ position: { ...item.position },
342
+ span: { ...item.span },
343
+ ...(item.placement === undefined
344
+ ? {}
345
+ : { placement: item.placement }),
346
+ };
347
+ const previous = existing.get(id);
348
+ return previous && samePlacement(previous, placement)
349
+ ? previous
350
+ : placement;
351
+ });
352
+ nextTarget = { ...target, placements };
353
+ resolvedTo = {
354
+ kind: 'grid',
355
+ zoneId: target.id,
356
+ position: { ...computed.get(itemId)!.position },
357
+ };
358
+ changed =
359
+ !sameZone ||
360
+ placements.some(
361
+ (placement, index) =>
362
+ !samePlacement(placement, target.placements[index]),
363
+ );
364
+ }
365
+ } else {
366
+ return invalid('invalid-structure', 'Unsupported target kind.', {
367
+ itemId,
368
+ zoneId: target.id,
369
+ });
370
+ }
371
+ if (!changed) {
372
+ return { status: 'ok', value, from, to: resolvedTo, changed, attempts };
373
+ }
374
+ if (value.revision === Number.MAX_SAFE_INTEGER) {
375
+ return invalid(
376
+ 'invalid-revision',
377
+ 'The layout revision cannot exceed a safe integer.',
378
+ );
379
+ }
380
+ const candidate: LayoutState<T> = {
381
+ revision: value.revision + 1,
382
+ items: value.items,
383
+ zones: value.zones.map(zone => {
384
+ if (zone.id === target.id) return nextTarget;
385
+ if (zone.id === source.id) return withoutItem(source, itemId);
386
+ return zone;
387
+ }),
388
+ };
389
+ const candidateValidation = validateLayoutState(candidate);
390
+ if (!candidateValidation.valid) {
391
+ return { status: 'invalid', issues: candidateValidation.issues };
392
+ }
393
+ return {
394
+ status: 'ok',
395
+ value: candidate,
396
+ from,
397
+ to: resolvedTo,
398
+ changed,
399
+ attempts,
400
+ };
401
+ }