@ankhorage/zora 25.6.2 → 25.8.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 (295) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +1 -1
  3. package/dist/capabilities/index.js +13 -1
  4. package/dist/capabilities/index.js.map +5 -4
  5. package/dist/features/grid-interactions/adapters/inbound/GridInteractions.d.ts +5 -0
  6. package/dist/features/grid-interactions/adapters/inbound/GridInteractions.d.ts.map +1 -0
  7. package/dist/features/grid-interactions/adapters/inbound/GridInteractions.js +40 -0
  8. package/dist/features/grid-interactions/adapters/inbound/GridInteractions.js.map +1 -0
  9. package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.d.ts +2 -0
  10. package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.d.ts.map +1 -0
  11. package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.js +2 -0
  12. package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.js.map +1 -0
  13. package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.native.d.ts +11 -0
  14. package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.native.d.ts.map +1 -0
  15. package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.native.js +5 -0
  16. package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.native.js.map +1 -0
  17. package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.d.ts +11 -0
  18. package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.d.ts.map +1 -0
  19. package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.js +20 -0
  20. package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.js.map +1 -0
  21. package/dist/features/grid-interactions/adapters/inbound/createGridInteractionsPointerQueue.d.ts +17 -0
  22. package/dist/features/grid-interactions/adapters/inbound/createGridInteractionsPointerQueue.d.ts.map +1 -0
  23. package/dist/features/grid-interactions/adapters/inbound/createGridInteractionsPointerQueue.js +76 -0
  24. package/dist/features/grid-interactions/adapters/inbound/createGridInteractionsPointerQueue.js.map +1 -0
  25. package/dist/features/grid-interactions/adapters/inbound/resolveGridInteractionPointer.d.ts +21 -0
  26. package/dist/features/grid-interactions/adapters/inbound/resolveGridInteractionPointer.d.ts.map +1 -0
  27. package/dist/features/grid-interactions/adapters/inbound/resolveGridInteractionPointer.js +20 -0
  28. package/dist/features/grid-interactions/adapters/inbound/resolveGridInteractionPointer.js.map +1 -0
  29. package/dist/features/grid-interactions/application/createGridInteractionsController.d.ts +4 -0
  30. package/dist/features/grid-interactions/application/createGridInteractionsController.d.ts.map +1 -0
  31. package/dist/features/grid-interactions/application/createGridInteractionsController.js +191 -0
  32. package/dist/features/grid-interactions/application/createGridInteractionsController.js.map +1 -0
  33. package/dist/features/grid-interactions/meta.d.ts +11 -0
  34. package/dist/features/grid-interactions/meta.d.ts.map +1 -0
  35. package/dist/features/grid-interactions/meta.js +11 -0
  36. package/dist/features/grid-interactions/meta.js.map +1 -0
  37. package/dist/features/grid-interactions/public.d.ts +4 -0
  38. package/dist/features/grid-interactions/public.d.ts.map +1 -0
  39. package/dist/features/grid-interactions/public.js +3 -0
  40. package/dist/features/grid-interactions/public.js.map +1 -0
  41. package/dist/features/matrix-grid/adapters/inbound/MatrixGrid.d.ts.map +1 -1
  42. package/dist/features/matrix-grid/adapters/inbound/MatrixGrid.js +73 -19
  43. package/dist/features/matrix-grid/adapters/inbound/MatrixGrid.js.map +1 -1
  44. package/dist/features/matrix-grid/adapters/inbound/MatrixGridKeyboardProxy.d.ts +9 -0
  45. package/dist/features/matrix-grid/adapters/inbound/MatrixGridKeyboardProxy.d.ts.map +1 -0
  46. package/dist/features/matrix-grid/adapters/inbound/MatrixGridKeyboardProxy.js +5 -0
  47. package/dist/features/matrix-grid/adapters/inbound/MatrixGridKeyboardProxy.js.map +1 -0
  48. package/dist/features/matrix-grid/adapters/inbound/MatrixGridKeyboardProxy.native.d.ts +9 -0
  49. package/dist/features/matrix-grid/adapters/inbound/MatrixGridKeyboardProxy.native.d.ts.map +1 -0
  50. package/dist/features/matrix-grid/adapters/inbound/MatrixGridKeyboardProxy.native.js +5 -0
  51. package/dist/features/matrix-grid/adapters/inbound/MatrixGridKeyboardProxy.native.js.map +1 -0
  52. package/dist/features/matrix-grid/adapters/inbound/MatrixGridKeyboardProxy.web.d.ts +9 -0
  53. package/dist/features/matrix-grid/adapters/inbound/MatrixGridKeyboardProxy.web.d.ts.map +1 -0
  54. package/dist/features/matrix-grid/adapters/inbound/MatrixGridKeyboardProxy.web.js +28 -0
  55. package/dist/features/matrix-grid/adapters/inbound/MatrixGridKeyboardProxy.web.js.map +1 -0
  56. package/dist/features/matrix-grid/adapters/inbound/useMatrixGridKeyboardFocus.d.ts +14 -0
  57. package/dist/features/matrix-grid/adapters/inbound/useMatrixGridKeyboardFocus.d.ts.map +1 -0
  58. package/dist/features/matrix-grid/adapters/inbound/useMatrixGridKeyboardFocus.js +50 -0
  59. package/dist/features/matrix-grid/adapters/inbound/useMatrixGridKeyboardFocus.js.map +1 -0
  60. package/dist/features/matrix-grid/application/createMatrixGridSparseIndex.d.ts +18 -0
  61. package/dist/features/matrix-grid/application/createMatrixGridSparseIndex.d.ts.map +1 -0
  62. package/dist/features/matrix-grid/application/createMatrixGridSparseIndex.js +56 -0
  63. package/dist/features/matrix-grid/application/createMatrixGridSparseIndex.js.map +1 -0
  64. package/dist/features/matrix-grid/application/getMatrixGridNextFocusId.d.ts +4 -0
  65. package/dist/features/matrix-grid/application/getMatrixGridNextFocusId.d.ts.map +1 -0
  66. package/dist/features/matrix-grid/application/getMatrixGridNextFocusId.js +65 -0
  67. package/dist/features/matrix-grid/application/getMatrixGridNextFocusId.js.map +1 -0
  68. package/dist/features/matrix-grid/application/resolveMatrixGridSelection.d.ts +8 -1
  69. package/dist/features/matrix-grid/application/resolveMatrixGridSelection.d.ts.map +1 -1
  70. package/dist/features/matrix-grid/application/resolveMatrixGridSelection.js +15 -4
  71. package/dist/features/matrix-grid/application/resolveMatrixGridSelection.js.map +1 -1
  72. package/dist/features/registry/ZORA_COMPONENT_REGISTRY.d.ts.map +1 -1
  73. package/dist/features/registry/ZORA_COMPONENT_REGISTRY.js +2 -0
  74. package/dist/features/registry/ZORA_COMPONENT_REGISTRY.js.map +1 -1
  75. package/dist/features/registry/componentMeta.d.ts.map +1 -1
  76. package/dist/features/registry/componentMeta.js +2 -0
  77. package/dist/features/registry/componentMeta.js.map +1 -1
  78. package/dist/index.d.ts +1 -0
  79. package/dist/index.d.ts.map +1 -1
  80. package/dist/index.js +1 -0
  81. package/dist/index.js.map +1 -1
  82. package/dist/types/grid-interactions.d.ts +58 -0
  83. package/dist/types/grid-interactions.d.ts.map +1 -0
  84. package/dist/types/grid-interactions.js +2 -0
  85. package/dist/types/grid-interactions.js.map +1 -0
  86. package/dist/types/matrix-grid.d.ts +2 -0
  87. package/dist/types/matrix-grid.d.ts.map +1 -1
  88. package/dist/types/matrix-grid.js.map +1 -1
  89. package/package.json +5 -5
  90. package/src/features/grid-interactions/adapters/inbound/GridInteractions.test.tsx +143 -0
  91. package/src/features/grid-interactions/adapters/inbound/GridInteractions.tsx +79 -0
  92. package/src/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.native.tsx +14 -0
  93. package/src/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.tsx +1 -0
  94. package/src/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.tsx +41 -0
  95. package/src/features/grid-interactions/adapters/inbound/createGridInteractionsPointerQueue.test.ts +98 -0
  96. package/src/features/grid-interactions/adapters/inbound/createGridInteractionsPointerQueue.ts +94 -0
  97. package/src/features/grid-interactions/adapters/inbound/resolveGridInteractionPointer.test.ts +33 -0
  98. package/src/features/grid-interactions/adapters/inbound/resolveGridInteractionPointer.ts +45 -0
  99. package/src/features/grid-interactions/application/createGridInteractionsController.test.ts +115 -0
  100. package/src/features/grid-interactions/application/createGridInteractionsController.ts +231 -0
  101. package/src/features/grid-interactions/meta.ts +12 -0
  102. package/src/features/grid-interactions/public.ts +11 -0
  103. package/src/features/matrix-grid/adapters/inbound/MatrixGrid.test.tsx +280 -2
  104. package/src/features/matrix-grid/adapters/inbound/MatrixGrid.tsx +121 -31
  105. package/src/features/matrix-grid/adapters/inbound/MatrixGridKeyboardProxy.native.tsx +11 -0
  106. package/src/features/matrix-grid/adapters/inbound/MatrixGridKeyboardProxy.tsx +11 -0
  107. package/src/features/matrix-grid/adapters/inbound/MatrixGridKeyboardProxy.web.tsx +43 -0
  108. package/src/features/matrix-grid/adapters/inbound/useMatrixGridKeyboardFocus.ts +70 -0
  109. package/src/features/matrix-grid/application/createMatrixGridSparseIndex.test.ts +35 -0
  110. package/src/features/matrix-grid/application/createMatrixGridSparseIndex.ts +105 -0
  111. package/src/features/matrix-grid/application/getMatrixGridNextFocusId.test.ts +93 -0
  112. package/src/features/matrix-grid/application/getMatrixGridNextFocusId.ts +112 -0
  113. package/src/features/matrix-grid/application/resolveMatrixGridSelection.test.ts +63 -2
  114. package/src/features/matrix-grid/application/resolveMatrixGridSelection.ts +27 -4
  115. package/src/features/registry/ZORA_COMPONENT_REGISTRY.ts +2 -0
  116. package/src/features/registry/componentMeta.ts +2 -0
  117. package/src/index.ts +11 -0
  118. package/src/types/grid-interactions.ts +72 -0
  119. package/src/types/matrix-grid.ts +2 -0
  120. package/web-dist/chunks/{Accordion-5g05kfmx.js → Accordion-0q2wzjdd.js} +4 -4
  121. package/web-dist/chunks/{Accordion-b96evb46.js → Accordion-1fsrnq73.js} +1 -1
  122. package/web-dist/chunks/{Accordion-h3y8qev2.js → Accordion-38ts3gnn.js} +2 -2
  123. package/web-dist/chunks/{Accordion-jnc315h7.js → Accordion-8pkw32q9.js} +2 -2
  124. package/web-dist/chunks/{Accordion-h7zsx9eg.js → Accordion-9jrxktne.js} +3 -3
  125. package/web-dist/chunks/{Accordion-9w8wg9mf.js → Accordion-9rnsg9pk.js} +1 -1
  126. package/web-dist/chunks/{Accordion-4zqd7twp.js → Accordion-9ygakvwk.js} +5 -5
  127. package/web-dist/chunks/{Accordion-fhnazn3n.js → Accordion-b74j4hdt.js} +1 -1
  128. package/web-dist/chunks/{Accordion-nssr5gep.js → Accordion-c50pywj3.js} +2 -2
  129. package/web-dist/chunks/{Accordion-pvj5npzc.js → Accordion-ddrn94v3.js} +3 -3
  130. package/web-dist/chunks/{Accordion-090y18cv.js → Accordion-dk0dtyc0.js} +3 -3
  131. package/web-dist/chunks/{Accordion-59pa0677.js → Accordion-fcczvwgg.js} +0 -1
  132. package/web-dist/chunks/{Accordion-sp4k08kw.js → Accordion-geah5s4f.js} +4 -4
  133. package/web-dist/chunks/{Accordion-6rrqpb2h.js → Accordion-h4ekm5cp.js} +2 -2
  134. package/web-dist/chunks/{Accordion-3d4wkzg7.js → Accordion-hecpczme.js} +5 -5
  135. package/web-dist/chunks/{Accordion-a6551mh4.js → Accordion-hn43wt45.js} +1 -1
  136. package/web-dist/chunks/{Accordion-sw7ep29d.js → Accordion-hnx7hx55.js} +2 -2
  137. package/web-dist/chunks/{Accordion-rsvphbe4.js → Accordion-j1chy29e.js} +1 -1
  138. package/web-dist/chunks/{Accordion-ewf1zen9.js → Accordion-k9cxpg08.js} +5 -5
  139. package/web-dist/chunks/{Accordion-5df6cgyf.js → Accordion-md1g54wg.js} +1 -1
  140. package/web-dist/chunks/{Accordion-kjtcbe09.js → Accordion-p1ebrt17.js} +3 -3
  141. package/web-dist/chunks/{Accordion-qn398m1x.js → Accordion-r2g18vxa.js} +1 -1
  142. package/web-dist/chunks/{Accordion-zt9yghdz.js → Accordion-rctfgyfv.js} +4 -4
  143. package/web-dist/chunks/{Accordion-88t8fgxv.js → Accordion-rfc5mdbv.js} +1 -1
  144. package/web-dist/chunks/{Accordion-fmzxat3v.js → Accordion-sdnzqktc.js} +4 -4
  145. package/web-dist/chunks/{Accordion-v0grdh6q.js → Accordion-sq7ya2ye.js} +2 -2
  146. package/web-dist/chunks/{Accordion-ejx5gcf9.js → Accordion-t5scffdr.js} +9 -9
  147. package/web-dist/chunks/{Accordion-sy1cc5my.js → Accordion-ts788t7b.js} +3 -3
  148. package/web-dist/chunks/{Accordion-4s68t3h7.js → Accordion-v3rzy273.js} +6 -6
  149. package/web-dist/chunks/{Accordion-7tjz25jh.js → Accordion-vbzjq3z8.js} +1 -1
  150. package/web-dist/chunks/{Accordion-jh72azm2.js → Accordion-vg337250.js} +2 -2
  151. package/web-dist/chunks/{Accordion-42jfy1a1.js → Accordion-vj1m2wck.js} +1 -1
  152. package/web-dist/chunks/{Accordion-tqpsmdrk.js → Accordion-vxg5sw01.js} +4 -4
  153. package/web-dist/chunks/{Accordion-61ntvh14.js → Accordion-w59y0jdf.js} +4 -4
  154. package/web-dist/chunks/{Accordion-fjxcn6g9.js → Accordion-wcmrasqx.js} +8 -8
  155. package/web-dist/chunks/{Accordion-r1cj2w9e.js → Accordion-wpk8dxqp.js} +3 -3
  156. package/web-dist/chunks/{Accordion-w6qedeba.js → Accordion-x055n3b3.js} +1 -1
  157. package/web-dist/chunks/{Accordion-dbf93gxh.js → Accordion-xcm1qc39.js} +1 -1
  158. package/web-dist/chunks/{Accordion-7kq500r1.js → Accordion-xgxxg1hz.js} +1 -1
  159. package/web-dist/chunks/{Accordion-a4cma5b6.js → Accordion-y3083394.js} +3 -3
  160. package/web-dist/chunks/{Accordion-40tenzcg.js → Accordion-yexfbrzg.js} +4 -4
  161. package/web-dist/chunks/{Accordion-2r0gqk3x.js → Accordion-ztw7zp6v.js} +5 -5
  162. package/web-dist/chunks/{Accordion-bz04casa.js → Accordion-zypyr9xw.js} +211 -6
  163. package/web-dist/components/accordion/Accordion.js +5 -5
  164. package/web-dist/components/accordion-item/AccordionItem.js +5 -5
  165. package/web-dist/components/activity-indicator/ActivityIndicator.js +1 -1
  166. package/web-dist/components/app-bar/AppBar.js +7 -7
  167. package/web-dist/components/app-header/AppHeader.js +7 -7
  168. package/web-dist/components/app-shell/AppShell.js +2 -2
  169. package/web-dist/components/auth-screen/AuthScreen.js +10 -10
  170. package/web-dist/components/avatar/Avatar.js +5 -5
  171. package/web-dist/components/avatar-group/AvatarGroup.js +5 -5
  172. package/web-dist/components/badge/Badge.js +2 -2
  173. package/web-dist/components/barcode-scanner-view/BarcodeScannerView.js +10 -10
  174. package/web-dist/components/bottom-sheet/BottomSheet.js +3 -3
  175. package/web-dist/components/bottom-sheet-flat-list/BottomSheetFlatList.js +3 -3
  176. package/web-dist/components/bottom-sheet-scroll-view/BottomSheetScrollView.js +3 -3
  177. package/web-dist/components/bottom-sheet-section-list/BottomSheetSectionList.js +3 -3
  178. package/web-dist/components/bottom-sheet-view/BottomSheetView.js +3 -3
  179. package/web-dist/components/bottom-sheet-virtualized-list/BottomSheetVirtualizedList.js +3 -3
  180. package/web-dist/components/breadcrumbs/Breadcrumbs.js +5 -5
  181. package/web-dist/components/button/Button.js +3 -3
  182. package/web-dist/components/button-group/ButtonGroup.js +3 -3
  183. package/web-dist/components/camera-permission-view/CameraPermissionView.js +10 -10
  184. package/web-dist/components/card/Card.js +9 -9
  185. package/web-dist/components/card-back/CardBack.js +2 -2
  186. package/web-dist/components/card-hand/CardHand.js +2 -2
  187. package/web-dist/components/chat-list-item/ChatListItem.js +7 -7
  188. package/web-dist/components/checkbox/Checkbox.js +4 -4
  189. package/web-dist/components/checkbox-group/CheckboxGroup.js +4 -4
  190. package/web-dist/components/chess-board/ChessBoard.js +2 -2
  191. package/web-dist/components/chip/Chip.js +5 -5
  192. package/web-dist/components/chip-group/ChipGroup.js +5 -5
  193. package/web-dist/components/collection-editor/CollectionEditor.js +9 -9
  194. package/web-dist/components/content-rail/ContentRail.js +4 -4
  195. package/web-dist/components/data-table/DataTable.js +14 -14
  196. package/web-dist/components/date-picker/DatePicker.js +5 -5
  197. package/web-dist/components/dialog/Dialog.js +4 -4
  198. package/web-dist/components/divider/Divider.js +2 -2
  199. package/web-dist/components/empty-state/EmptyState.js +10 -10
  200. package/web-dist/components/explorer/Explorer.js +7 -7
  201. package/web-dist/components/field/Field.js +4 -4
  202. package/web-dist/components/file-explorer/FileExplorer.js +7 -7
  203. package/web-dist/components/flat-list/FlatList.js +5 -5
  204. package/web-dist/components/forgot-password-form/ForgotPasswordForm.js +10 -10
  205. package/web-dist/components/form/Form.js +6 -6
  206. package/web-dist/components/form-actions/FormActions.js +6 -6
  207. package/web-dist/components/form-error/FormError.js +6 -6
  208. package/web-dist/components/game/Game.js +3 -3
  209. package/web-dist/components/game-entity/GameEntity.js +3 -3
  210. package/web-dist/components/game-field/GameField.js +3 -3
  211. package/web-dist/components/game-input-zone/GameInputZone.js +3 -3
  212. package/web-dist/components/game-measurement-probe/GameMeasurementProbe.js +3 -3
  213. package/web-dist/components/game-overlay/GameOverlay.js +3 -3
  214. package/web-dist/components/gradient/Gradient.js +3 -3
  215. package/web-dist/components/gradient-renderer-provider/GradientRendererProvider.js +3 -3
  216. package/web-dist/components/graph-view/GraphView.js +2 -2
  217. package/web-dist/components/grid/Grid.js +2 -2
  218. package/web-dist/components/grid-interactions/GridInteractions.d.ts +3 -0
  219. package/web-dist/components/grid-interactions/GridInteractions.js +371 -0
  220. package/web-dist/components/grid-interactions/index.d.ts +1 -0
  221. package/web-dist/components/grid-interactions/index.js +2 -0
  222. package/web-dist/components/grid-line-overlay/GridLineOverlay.js +2 -2
  223. package/web-dist/components/grid-ruler/GridRuler.js +2 -2
  224. package/web-dist/components/grid-view/GridView.js +1 -1
  225. package/web-dist/components/heading/Heading.js +2 -2
  226. package/web-dist/components/hero/Hero.js +9 -9
  227. package/web-dist/components/icon/Icon.js +2 -2
  228. package/web-dist/components/icon-button/IconButton.js +3 -3
  229. package/web-dist/components/image/Image.js +4 -4
  230. package/web-dist/components/keyboard-avoiding-view/KeyboardAvoidingView.js +2 -2
  231. package/web-dist/components/list/List.js +5 -5
  232. package/web-dist/components/list-item/ListItem.js +5 -5
  233. package/web-dist/components/list-section/ListSection.js +5 -5
  234. package/web-dist/components/matrix-grid/MatrixGrid.js +296 -47
  235. package/web-dist/components/media-card/MediaCard.js +9 -9
  236. package/web-dist/components/media-explorer/MediaExplorer.js +7 -7
  237. package/web-dist/components/message-bubble/MessageBubble.js +7 -7
  238. package/web-dist/components/metric-card/MetricCard.js +9 -9
  239. package/web-dist/components/missing-element/MissingElement.js +4 -4
  240. package/web-dist/components/o-auth-provider-button/OAuthProviderButton.js +10 -10
  241. package/web-dist/components/o-auth-provider-list/OAuthProviderList.js +10 -10
  242. package/web-dist/components/opening-book/OpeningBook.js +2 -2
  243. package/web-dist/components/otp-form/OtpForm.js +10 -10
  244. package/web-dist/components/pagination/Pagination.js +4 -4
  245. package/web-dist/components/palette-item/PaletteItem.js +9 -9
  246. package/web-dist/components/playing-card/PlayingCard.js +2 -2
  247. package/web-dist/components/poker-training-table/PokerTrainingTable.js +2 -2
  248. package/web-dist/components/popover-menu/PopoverMenu.js +4 -4
  249. package/web-dist/components/post-card/PostCard.js +9 -9
  250. package/web-dist/components/product-card/ProductCard.js +9 -9
  251. package/web-dist/components/progress/Progress.js +4 -4
  252. package/web-dist/components/progress-ring/ProgressRing.js +4 -4
  253. package/web-dist/components/radio/Radio.js +5 -5
  254. package/web-dist/components/radio-group/RadioGroup.js +5 -5
  255. package/web-dist/components/rating/Rating.js +3 -3
  256. package/web-dist/components/reader/Reader.js +10 -10
  257. package/web-dist/components/scan-overlay/ScanOverlay.js +10 -10
  258. package/web-dist/components/screen/Screen.js +2 -2
  259. package/web-dist/components/screen-section/ScreenSection.js +4 -4
  260. package/web-dist/components/scroll-view/ScrollView.js +2 -2
  261. package/web-dist/components/search-input/SearchInput.js +4 -4
  262. package/web-dist/components/section-header/SectionHeader.js +4 -4
  263. package/web-dist/components/section-list/SectionList.js +5 -5
  264. package/web-dist/components/select/Select.js +4 -4
  265. package/web-dist/components/selectable-item/SelectableItem.js +2 -2
  266. package/web-dist/components/selection-provider/SelectionProvider.js +2 -2
  267. package/web-dist/components/sign-in-form/SignInForm.js +10 -10
  268. package/web-dist/components/sign-up-form/SignUpForm.js +10 -10
  269. package/web-dist/components/skeleton/Skeleton.js +12 -12
  270. package/web-dist/components/skeleton-card/SkeletonCard.js +12 -12
  271. package/web-dist/components/skeleton-list/SkeletonList.js +12 -12
  272. package/web-dist/components/skeleton-text/SkeletonText.js +12 -12
  273. package/web-dist/components/spatial-grid/SpatialGrid.js +1 -1
  274. package/web-dist/components/surface/Surface.js +2 -2
  275. package/web-dist/components/switch/Switch.js +1 -1
  276. package/web-dist/components/tab/Tab.js +2 -2
  277. package/web-dist/components/tab-list/TabList.js +2 -2
  278. package/web-dist/components/tab-panel/TabPanel.js +2 -2
  279. package/web-dist/components/tabletop-table/TabletopTable.js +2 -2
  280. package/web-dist/components/tabs/Tabs.js +2 -2
  281. package/web-dist/components/text/Text.js +2 -2
  282. package/web-dist/components/text-input/TextInput.js +4 -4
  283. package/web-dist/components/tile-grid/TileGrid.js +1 -1
  284. package/web-dist/components/time-grid/TimeGrid.js +2 -2
  285. package/web-dist/components/time-picker/TimePicker.js +5 -5
  286. package/web-dist/components/timeline/Timeline.js +4 -4
  287. package/web-dist/components/toast/Toast.js +2 -2
  288. package/web-dist/components/toast-provider/ToastProvider.js +2 -2
  289. package/web-dist/components/toolbar/Toolbar.js +9 -9
  290. package/web-dist/components/tree-item/TreeItem.js +9 -9
  291. package/web-dist/components/tree-view/TreeView.js +1 -1
  292. package/web-dist/components/uploader/Uploader.js +12 -12
  293. package/web-dist/components/view/View.js +2 -2
  294. package/web-dist/manifest.json +787 -772
  295. package/web-dist/runtime/ZoraProvider.js +3 -3
@@ -0,0 +1,143 @@
1
+ import { join } from 'node:path';
2
+ import { pathToFileURL } from 'node:url';
3
+
4
+ import { expect, test } from 'bun:test';
5
+ import { Window } from 'happy-dom';
6
+ import React, { act } from 'react';
7
+ import { createRoot } from 'react-dom/client';
8
+
9
+ import type { GridInteractions as GridInteractionsComponent } from './GridInteractions';
10
+
11
+ const webDistRoot = join(import.meta.dir, '../../../../../web-dist');
12
+
13
+ /*** Loads the independently bundled RNW interaction adapter through its public artifact path. */
14
+ function loadGridInteractions() {
15
+ return import(
16
+ pathToFileURL(join(webDistRoot, 'components/grid-interactions/index.js')).href
17
+ ) as Promise<{ GridInteractions: typeof GridInteractionsComponent }>;
18
+ }
19
+
20
+ test('keeps a mounted RNW keyboard boundary current across parent rerenders', async () => {
21
+ const browser = new Window();
22
+ const restore = installBrowserGlobals(browser);
23
+ const host = document.createElement('div');
24
+ document.body.append(host);
25
+ const root = createRoot(host);
26
+ const { GridInteractions } = await loadGridInteractions();
27
+ const firstIntents: unknown[] = [];
28
+ const secondIntents: unknown[] = [];
29
+ const render = (
30
+ onIntent: (intent: unknown) => void,
31
+ interactionPolicy?: 'passive',
32
+ resizeHandle?: 'bottom-right',
33
+ ) => (
34
+ <GridInteractions
35
+ interactionPolicy={interactionPolicy}
36
+ items={[{ height: 2, id: 'a', width: 3, x: 1, y: 1 }]}
37
+ onIntent={onIntent}
38
+ selectedIds={['a']}
39
+ testID="grid-interactions"
40
+ resizeHandle={resizeHandle}
41
+ viewport={{
42
+ height: 100,
43
+ offsetX: 0,
44
+ offsetY: 0,
45
+ pixelsPerUnitX: 2,
46
+ pixelsPerUnitY: 4,
47
+ width: 100,
48
+ }}
49
+ >
50
+ <span>nested target</span>
51
+ </GridInteractions>
52
+ );
53
+
54
+ try {
55
+ await renderGridInteractions(
56
+ root,
57
+ render((intent) => firstIntents.push(intent)),
58
+ );
59
+ await renderGridInteractions(
60
+ root,
61
+ render((intent) => secondIntents.push(intent)),
62
+ );
63
+
64
+ const keyboard = host.querySelector<HTMLElement>(
65
+ '[aria-label="Grid interaction keyboard controls"]',
66
+ );
67
+ if (!keyboard) throw new Error('Missing keyboard interaction proxy');
68
+ const resize = new browser.KeyboardEvent('keydown', {
69
+ altKey: true,
70
+ bubbles: true,
71
+ cancelable: true,
72
+ key: 'ArrowRight',
73
+ });
74
+ await dispatch(keyboard, resize);
75
+ const acceleratedMove = new browser.KeyboardEvent('keydown', {
76
+ bubbles: true,
77
+ cancelable: true,
78
+ key: 'ArrowDown',
79
+ shiftKey: true,
80
+ });
81
+ await dispatch(keyboard, acceleratedMove);
82
+ await renderGridInteractions(
83
+ root,
84
+ render((intent) => secondIntents.push(intent), 'passive'),
85
+ );
86
+ const passiveKey = new browser.KeyboardEvent('keydown', { bubbles: true, key: 'ArrowLeft' });
87
+ await dispatch(keyboard, passiveKey);
88
+ expect(keyboard.getAttribute('tabindex')).toBe('-1');
89
+
90
+ expect(firstIntents).toEqual([]);
91
+ expect(secondIntents).toEqual([
92
+ { type: 'resize', itemIds: ['a'], rects: [{ height: 2, id: 'a', width: 4, x: 1, y: 1 }] },
93
+ { type: 'move', itemIds: ['a'], rects: [{ height: 2, id: 'a', width: 3, x: 1, y: 11 }] },
94
+ ]);
95
+ expect(resize.defaultPrevented).toBe(true);
96
+ expect(acceleratedMove.defaultPrevented).toBe(true);
97
+ expect(passiveKey.defaultPrevented).toBe(false);
98
+ } finally {
99
+ await act(() => Promise.resolve().then(() => root.unmount()));
100
+ host.remove();
101
+ browser.close();
102
+ restore();
103
+ }
104
+ });
105
+
106
+ /*** Installs the DOM globals that the independently bundled RNW adapter requires. */
107
+ function installBrowserGlobals(browser: Window): () => void {
108
+ const keys = [
109
+ 'window',
110
+ 'document',
111
+ 'Node',
112
+ 'navigator',
113
+ 'ShadowRoot',
114
+ 'IS_REACT_ACT_ENVIRONMENT',
115
+ ] as const;
116
+ const previous = keys.map(
117
+ (key) => [key, Object.getOwnPropertyDescriptor(globalThis, key)] as const,
118
+ );
119
+ Object.assign(globalThis, {
120
+ IS_REACT_ACT_ENVIRONMENT: true,
121
+ Node: browser.Node,
122
+ ShadowRoot: browser.ShadowRoot,
123
+ document: browser.document,
124
+ navigator: browser.navigator,
125
+ window: browser,
126
+ });
127
+ return () => {
128
+ for (const [key, descriptor] of previous) {
129
+ if (descriptor) Object.defineProperty(globalThis, key, descriptor);
130
+ else Reflect.deleteProperty(globalThis, key);
131
+ }
132
+ };
133
+ }
134
+
135
+ /*** Flushes one mounted RNW render before the next synthetic input event. */
136
+ function renderGridInteractions(root: ReturnType<typeof createRoot>, component: React.ReactNode) {
137
+ return act(() => Promise.resolve().then(() => root.render(component)));
138
+ }
139
+
140
+ /*** Dispatches one browser input event through React's RNW adapter boundary. */
141
+ function dispatch(target: HTMLElement, event: Event) {
142
+ return act(() => Promise.resolve().then(() => target.dispatchEvent(event)));
143
+ }
@@ -0,0 +1,79 @@
1
+ import React from 'react';
2
+ import { View } from 'react-native';
3
+
4
+ import type {
5
+ GridInteractionsController,
6
+ GridInteractionsProps,
7
+ } from '../../../../types/grid-interactions';
8
+ import { createGridInteractionsController } from '../../application/createGridInteractionsController';
9
+ import { createGridInteractionsPointerQueue } from './createGridInteractionsPointerQueue';
10
+ import { GridInteractionsKeyboardProxy } from './GridInteractionsKeyboardProxy';
11
+ import { resolveGridInteractionPointer } from './resolveGridInteractionPointer';
12
+
13
+ /*** Adapts native responder and keyboard events to the controlled grid interaction boundary. */
14
+ export function GridInteractions({
15
+ children,
16
+ interactionPolicy,
17
+ resizeHandle,
18
+ testID,
19
+ ...props
20
+ }: GridInteractionsProps) {
21
+ const controllerProps = React.useMemo(
22
+ () => ({ ...props, interactionPolicy }),
23
+ [interactionPolicy, props],
24
+ );
25
+ const [controller] = React.useState<GridInteractionsController>(() =>
26
+ createGridInteractionsController(controllerProps),
27
+ );
28
+ const surfaceRef = React.useRef<View>(null);
29
+ const [pointerQueue] = React.useState(() => createGridInteractionsPointerQueue());
30
+ React.useLayoutEffect(() => controller.update(controllerProps), [controller, controllerProps]);
31
+ React.useLayoutEffect(() => {
32
+ pointerQueue.activate();
33
+ return () => {
34
+ pointerQueue.dispose();
35
+ controller.cancel();
36
+ };
37
+ }, [controller, pointerQueue]);
38
+ const withPointer = (
39
+ event: Parameters<typeof resolveGridInteractionPointer>[0],
40
+ handlePointer: (pointer: ReturnType<typeof resolveGridInteractionPointer>) => void,
41
+ ) => {
42
+ const surface = surfaceRef.current;
43
+ if (!surface) return;
44
+ const eventSnapshot = { nativeEvent: { ...event.nativeEvent } };
45
+ pointerQueue.enqueue(surface, (origin) =>
46
+ handlePointer(resolveGridInteractionPointer(eventSnapshot, origin)),
47
+ );
48
+ };
49
+ const enabled = interactionPolicy !== 'passive';
50
+
51
+ return (
52
+ <View
53
+ accessible
54
+ accessibilityLabel="Grid interaction surface"
55
+ accessibilityRole="adjustable"
56
+ ref={surfaceRef}
57
+ testID={testID}
58
+ onResponderGrant={(event) =>
59
+ withPointer(event, (pointer) => controller.begin(pointer, resizeHandle))
60
+ }
61
+ onResponderMove={(event) => withPointer(event, controller.move)}
62
+ onResponderRelease={(event) => withPointer(event, controller.end)}
63
+ onResponderTerminate={() => {
64
+ pointerQueue.cancel();
65
+ controller.cancel();
66
+ }}
67
+ onStartShouldSetResponder={() => enabled}
68
+ >
69
+ <GridInteractionsKeyboardProxy
70
+ enabled={enabled}
71
+ onKeyDown={(key, keyPointer) => {
72
+ return controller.keyDown(key, keyPointer);
73
+ }}
74
+ >
75
+ {children}
76
+ </GridInteractionsKeyboardProxy>
77
+ </View>
78
+ );
79
+ }
@@ -0,0 +1,14 @@
1
+ import type React from 'react';
2
+
3
+ import type { GridInteractionPointer } from '../../../../types/grid-interactions';
4
+
5
+ /*** Leaves native accessibility focus untouched where browser keyboard events are unavailable. */
6
+ export function GridInteractionsKeyboardProxy({ children }: GridInteractionsKeyboardProxyProps) {
7
+ return <>{children}</>;
8
+ }
9
+
10
+ interface GridInteractionsKeyboardProxyProps {
11
+ readonly children: React.ReactNode;
12
+ readonly enabled: boolean;
13
+ readonly onKeyDown: (key: string, pointer: GridInteractionPointer) => boolean;
14
+ }
@@ -0,0 +1 @@
1
+ export { GridInteractionsKeyboardProxy } from './GridInteractionsKeyboardProxy.native';
@@ -0,0 +1,41 @@
1
+ import type React from 'react';
2
+
3
+ import type { GridInteractionPointer } from '../../../../types/grid-interactions';
4
+
5
+ /*** Adapts web keyboard movement and resize commands without changing native responder behavior. */
6
+ export function GridInteractionsKeyboardProxy({
7
+ children,
8
+ enabled,
9
+ onKeyDown,
10
+ }: GridInteractionsKeyboardProxyProps) {
11
+ return (
12
+ <div
13
+ aria-label="Grid interaction keyboard controls"
14
+ onKeyDown={(event) => {
15
+ if (!enabled) return;
16
+ if (
17
+ onKeyDown(event.key, {
18
+ altKey: event.altKey,
19
+ ctrlKey: event.ctrlKey,
20
+ metaKey: event.metaKey,
21
+ shiftKey: event.shiftKey,
22
+ x: 0,
23
+ y: 0,
24
+ })
25
+ ) {
26
+ event.preventDefault();
27
+ }
28
+ }}
29
+ style={{ display: 'contents' }}
30
+ tabIndex={enabled ? 0 : -1}
31
+ >
32
+ {children}
33
+ </div>
34
+ );
35
+ }
36
+
37
+ interface GridInteractionsKeyboardProxyProps {
38
+ readonly children: React.ReactNode;
39
+ readonly enabled: boolean;
40
+ readonly onKeyDown: (key: string, pointer: GridInteractionPointer) => boolean;
41
+ }
@@ -0,0 +1,98 @@
1
+ import { expect, test } from 'bun:test';
2
+
3
+ import { createGridInteractionsPointerQueue } from './createGridInteractionsPointerQueue';
4
+ import { resolveGridInteractionPointer } from './resolveGridInteractionPointer';
5
+
6
+ test('invalidates pending native responder callbacks after cancellation', () => {
7
+ const callbacks: ((x: number, y: number) => void)[] = [];
8
+ const origins: unknown[] = [];
9
+ const queue = createGridInteractionsPointerQueue();
10
+ const surface = {
11
+ measureInWindow: (callback: (x: number, y: number) => void) => callbacks.push(callback),
12
+ };
13
+
14
+ queue.enqueue(surface, (origin) => origins.push(origin));
15
+ queue.enqueue(surface, (origin) => origins.push(origin));
16
+ queue.cancel();
17
+ callbacks[0]?.(10, 20);
18
+
19
+ expect(origins).toEqual([]);
20
+ expect(callbacks).toHaveLength(1);
21
+ });
22
+
23
+ test('invalidates an active native responder measurement across unmount and remount', () => {
24
+ const callbacks: ((x: number, y: number) => void)[] = [];
25
+ const origins: unknown[] = [];
26
+ const queue = createGridInteractionsPointerQueue();
27
+ const surface = {
28
+ measureInWindow: (callback: (x: number, y: number) => void) => callbacks.push(callback),
29
+ };
30
+
31
+ queue.enqueue(surface, (origin) => origins.push(origin));
32
+ queue.dispose();
33
+ queue.activate();
34
+ queue.enqueue(surface, (origin) => origins.push(origin));
35
+ callbacks[1]?.(30, 40);
36
+ callbacks[0]?.(10, 20);
37
+
38
+ expect(origins).toEqual([{ x: 30, y: 40 }]);
39
+ });
40
+
41
+ test('retires a canceled measurement slot without waiting for its callback', () => {
42
+ const callbacks: ((x: number, y: number) => void)[] = [];
43
+ const origins: unknown[] = [];
44
+ const queue = createGridInteractionsPointerQueue();
45
+ const surface = {
46
+ measureInWindow: (callback: (x: number, y: number) => void) => callbacks.push(callback),
47
+ };
48
+
49
+ queue.enqueue(surface, (origin) => origins.push(origin));
50
+ queue.cancel();
51
+ queue.enqueue(surface, (origin) => origins.push(origin));
52
+ callbacks[1]?.(30, 40);
53
+ queue.enqueue(surface, (origin) => origins.push(origin));
54
+ callbacks[0]?.(10, 20);
55
+ callbacks[2]?.(50, 60);
56
+
57
+ expect(origins).toEqual([
58
+ { x: 30, y: 40 },
59
+ { x: 50, y: 60 },
60
+ ]);
61
+ });
62
+
63
+ test('uses the latest RNW surface origin for each serialized pointer', () => {
64
+ const origins = [
65
+ { left: 10, top: 20 },
66
+ { left: 15, top: 25 },
67
+ ];
68
+ const resolvedOrigins: unknown[] = [];
69
+ const queue = createGridInteractionsPointerQueue();
70
+ const surface = {
71
+ getBoundingClientRect: () => origins.shift(),
72
+ };
73
+
74
+ queue.enqueue(surface, (origin) => resolvedOrigins.push(origin));
75
+ queue.enqueue(surface, (origin) => resolvedOrigins.push(origin));
76
+
77
+ expect(resolvedOrigins).toEqual([
78
+ { x: 10, y: 20 },
79
+ { x: 15, y: 25 },
80
+ ]);
81
+ });
82
+
83
+ test('preserves native responder-local coordinates while measuring asynchronously', () => {
84
+ const callbacks: ((x: number, y: number) => void)[] = [];
85
+ const pointers: unknown[] = [];
86
+ const queue = createGridInteractionsPointerQueue();
87
+ const surface = {
88
+ measureInWindow: (callback: (x: number, y: number) => void) => callbacks.push(callback),
89
+ };
90
+ const event = { nativeEvent: { locationX: 7, locationY: 9 } };
91
+
92
+ queue.enqueue(surface, (origin) => {
93
+ pointers.push(resolveGridInteractionPointer(event, origin));
94
+ });
95
+ callbacks[0]?.(10, 20);
96
+
97
+ expect(pointers).toEqual([{ x: 7, y: 9 }]);
98
+ });
@@ -0,0 +1,94 @@
1
+ /*** Serializes native responder measurements and invalidates work from canceled interaction lifecycles. */
2
+ export function createGridInteractionsPointerQueue() {
3
+ let active = true;
4
+ let generation = 0;
5
+ let measuring = false;
6
+ let measurementId = 0;
7
+ let activeMeasurementId: number | undefined;
8
+ let pending: readonly GridInteractionPointerWork[] = [];
9
+
10
+ const processNext = () => {
11
+ if (measuring) return;
12
+ const pointerWork = pending.at(0);
13
+ if (pointerWork === undefined) return;
14
+ pending = pending.slice(1);
15
+ measuring = true;
16
+ const currentMeasurementId = ++measurementId;
17
+ activeMeasurementId = currentMeasurementId;
18
+ let settled = false;
19
+ measureSurfaceOrigin(pointerWork.surface, (origin) => {
20
+ if (settled) return;
21
+ settled = true;
22
+ if (activeMeasurementId !== currentMeasurementId) return;
23
+ try {
24
+ if (active && pointerWork.generation === generation) {
25
+ pointerWork.handleOrigin(origin);
26
+ }
27
+ } finally {
28
+ activeMeasurementId = undefined;
29
+ measuring = false;
30
+ processNext();
31
+ }
32
+ });
33
+ };
34
+
35
+ return {
36
+ activate: () => {
37
+ active = true;
38
+ },
39
+ enqueue: (
40
+ surface: GridInteractionSurface,
41
+ handleOrigin: (origin: GridInteractionSurfaceOrigin) => void,
42
+ ) => {
43
+ if (!active) return;
44
+ pending = [...pending, { generation, handleOrigin, surface }];
45
+ processNext();
46
+ },
47
+ cancel: () => {
48
+ generation += 1;
49
+ pending = [];
50
+ activeMeasurementId = undefined;
51
+ measuring = false;
52
+ },
53
+ dispose: () => {
54
+ active = false;
55
+ generation += 1;
56
+ pending = [];
57
+ activeMeasurementId = undefined;
58
+ measuring = false;
59
+ },
60
+ };
61
+ }
62
+
63
+ interface GridInteractionPointerWork {
64
+ readonly generation: number;
65
+ readonly handleOrigin: (origin: GridInteractionSurfaceOrigin) => void;
66
+ readonly surface: GridInteractionSurface;
67
+ }
68
+
69
+ interface GridInteractionSurface {
70
+ readonly getBoundingClientRect?: () => Pick<DOMRect, 'left' | 'top'>;
71
+ readonly measureInWindow?: (callback: (x: number, y: number) => void) => void;
72
+ }
73
+
74
+ interface GridInteractionSurfaceOrigin {
75
+ readonly x: number;
76
+ readonly y: number;
77
+ }
78
+
79
+ /*** Resolves the current surface origin through RNW DOM geometry or native asynchronous measurement. */
80
+ function measureSurfaceOrigin(
81
+ surface: GridInteractionSurface,
82
+ onOrigin: (origin: GridInteractionSurfaceOrigin) => void,
83
+ ) {
84
+ const rect = surface.getBoundingClientRect?.();
85
+ if (rect) {
86
+ onOrigin({ x: rect.left, y: rect.top });
87
+ return;
88
+ }
89
+ if (surface.measureInWindow !== undefined) {
90
+ surface.measureInWindow((x, y) => onOrigin({ x, y }));
91
+ return;
92
+ }
93
+ onOrigin({ x: 0, y: 0 });
94
+ }
@@ -0,0 +1,33 @@
1
+ import { expect, test } from 'bun:test';
2
+
3
+ import { resolveGridInteractionPointer } from './resolveGridInteractionPointer';
4
+
5
+ test('normalizes nested responder coordinates against the interaction surface origin', () => {
6
+ expect(
7
+ resolveGridInteractionPointer(
8
+ {
9
+ nativeEvent: {
10
+ locationX: 4,
11
+ locationY: 6,
12
+ pageX: 124,
13
+ pageY: 86,
14
+ },
15
+ },
16
+ { x: 120, y: 80 },
17
+ ),
18
+ ).toEqual({ x: 4, y: 6 });
19
+ });
20
+
21
+ test('uses responder-local coordinates when page coordinates are unavailable on native', () => {
22
+ expect(
23
+ resolveGridInteractionPointer(
24
+ {
25
+ nativeEvent: {
26
+ locationX: 4,
27
+ locationY: 6,
28
+ },
29
+ },
30
+ { x: 120, y: 80 },
31
+ ),
32
+ ).toEqual({ x: 4, y: 6 });
33
+ });
@@ -0,0 +1,45 @@
1
+ import type { GridInteractionPointer } from '../../../../types/grid-interactions';
2
+
3
+ /*** Converts a responder event into coordinates local to the interaction surface. */
4
+ export function resolveGridInteractionPointer(
5
+ event: GridInteractionNativePointerEvent,
6
+ surfaceOrigin: GridInteractionSurfaceOrigin,
7
+ ): GridInteractionPointer {
8
+ const { nativeEvent } = event;
9
+ const { pageX, pageY } = nativeEvent;
10
+ const x =
11
+ typeof pageX === 'number' && Number.isFinite(pageX)
12
+ ? pageX - surfaceOrigin.x
13
+ : nativeEvent.locationX;
14
+ const y =
15
+ typeof pageY === 'number' && Number.isFinite(pageY)
16
+ ? pageY - surfaceOrigin.y
17
+ : nativeEvent.locationY;
18
+
19
+ return {
20
+ altKey: nativeEvent.altKey,
21
+ ctrlKey: nativeEvent.ctrlKey,
22
+ metaKey: nativeEvent.metaKey,
23
+ shiftKey: nativeEvent.shiftKey,
24
+ x,
25
+ y,
26
+ };
27
+ }
28
+
29
+ interface GridInteractionNativePointerEvent {
30
+ readonly nativeEvent: {
31
+ readonly altKey?: boolean;
32
+ readonly ctrlKey?: boolean;
33
+ readonly locationX: number;
34
+ readonly locationY: number;
35
+ readonly metaKey?: boolean;
36
+ readonly pageX?: number;
37
+ readonly pageY?: number;
38
+ readonly shiftKey?: boolean;
39
+ };
40
+ }
41
+
42
+ interface GridInteractionSurfaceOrigin {
43
+ readonly x: number;
44
+ readonly y: number;
45
+ }
@@ -0,0 +1,115 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+
3
+ import { createGridInteractionsController } from './createGridInteractionsController';
4
+
5
+ const viewport = {
6
+ width: 100,
7
+ height: 100,
8
+ offsetX: 0,
9
+ offsetY: 0,
10
+ pixelsPerUnitX: 2,
11
+ pixelsPerUnitY: 4,
12
+ };
13
+ const items = [
14
+ { id: 'a', x: 1, y: 1, width: 3, height: 2 },
15
+ { id: 'locked', x: 10, y: 1, width: 2, height: 2, locked: true },
16
+ ];
17
+
18
+ describe('createGridInteractionsController', () => {
19
+ test('maps independent viewport scales to a move preview without mutating items', () => {
20
+ const intents: unknown[] = [];
21
+ const controller = createGridInteractionsController({
22
+ viewport,
23
+ items,
24
+ onIntent: (intent) => intents.push(intent),
25
+ });
26
+ controller.begin({ x: 3, y: 5 });
27
+ controller.move({ x: 7, y: 13 });
28
+ expect(intents).toEqual([
29
+ { type: 'move', itemIds: ['a'], rects: [{ id: 'a', x: 3, y: 3, width: 3, height: 2 }] },
30
+ ]);
31
+ expect(items[0]).toEqual({ id: 'a', x: 1, y: 1, width: 3, height: 2 });
32
+ });
33
+
34
+ test('normalizes negative marquee drags and protects locked items', () => {
35
+ const intents: unknown[] = [];
36
+ const controller = createGridInteractionsController({
37
+ viewport,
38
+ items,
39
+ marqueeMode: 'intersect',
40
+ onIntent: (intent) => intents.push(intent),
41
+ });
42
+ controller.begin({ x: 24, y: 12 });
43
+ controller.end({ x: 0, y: 0 });
44
+ expect(intents).toEqual([
45
+ { type: 'marquee', itemIds: ['a'], marquee: { x: 0, y: 0, width: 12, height: 3 } },
46
+ ]);
47
+ });
48
+
49
+ test('keeps an active gesture while updating controlled props after a parent rerender', () => {
50
+ const initialIntents: unknown[] = [];
51
+ const rerenderedIntents: unknown[] = [];
52
+ const controller = createGridInteractionsController({
53
+ viewport,
54
+ items,
55
+ onIntent: (intent) => initialIntents.push(intent),
56
+ });
57
+
58
+ controller.begin({ x: 3, y: 5 });
59
+ controller.update({
60
+ viewport,
61
+ items,
62
+ onIntent: (intent) => rerenderedIntents.push(intent),
63
+ });
64
+ controller.end({ x: 7, y: 13 });
65
+
66
+ expect(initialIntents).toEqual([]);
67
+ expect(rerenderedIntents).toEqual([
68
+ { type: 'move', itemIds: ['a'], rects: [{ id: 'a', x: 3, y: 3, width: 3, height: 2 }] },
69
+ ]);
70
+ });
71
+
72
+ test('emits keyboard resize and accelerated movement only while interaction is enabled', () => {
73
+ const intents: unknown[] = [];
74
+ const controller = createGridInteractionsController({
75
+ viewport,
76
+ items,
77
+ onIntent: (intent) => intents.push(intent),
78
+ selectedIds: ['a'],
79
+ });
80
+
81
+ expect(controller.keyDown('ArrowRight', { x: 0, y: 0, altKey: true })).toBe(true);
82
+ expect(controller.keyDown('ArrowDown', { x: 0, y: 0, shiftKey: true })).toBe(true);
83
+ controller.update({
84
+ interactionPolicy: 'passive',
85
+ viewport,
86
+ items,
87
+ onIntent: (intent) => intents.push(intent),
88
+ selectedIds: ['a'],
89
+ });
90
+ expect(controller.keyDown('ArrowLeft', { x: 0, y: 0 })).toBe(false);
91
+ controller.begin({ x: 3, y: 5 });
92
+ controller.end({ x: 7, y: 13 });
93
+
94
+ expect(intents).toEqual([
95
+ { type: 'resize', itemIds: ['a'], rects: [{ id: 'a', x: 1, y: 1, width: 4, height: 2 }] },
96
+ { type: 'move', itemIds: ['a'], rects: [{ id: 'a', x: 1, y: 11, width: 3, height: 2 }] },
97
+ ]);
98
+ });
99
+
100
+ test('emits handle-driven resize intents for a pointer gesture', () => {
101
+ const intents: unknown[] = [];
102
+ const controller = createGridInteractionsController({
103
+ viewport,
104
+ items,
105
+ onIntent: (intent) => intents.push(intent),
106
+ });
107
+
108
+ controller.begin({ x: 3, y: 5 }, 'bottom-right');
109
+ controller.end({ x: 7, y: 13 });
110
+
111
+ expect(intents).toEqual([
112
+ { type: 'resize', itemIds: ['a'], rects: [{ id: 'a', x: 1, y: 1, width: 5, height: 4 }] },
113
+ ]);
114
+ });
115
+ });