@ankhorage/zora 25.7.0 → 25.8.1

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 (311) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +1 -1
  3. package/dist/capabilities/index.d.ts +2 -74
  4. package/dist/capabilities/index.d.ts.map +1 -1
  5. package/dist/capabilities/index.js +6170 -59
  6. package/dist/capabilities/index.js.map +119 -1
  7. package/dist/cli/index.d.ts +2 -74
  8. package/dist/cli/index.d.ts.map +1 -1
  9. package/dist/cli/index.js +2 -1
  10. package/dist/cli/index.js.map +1 -1
  11. package/dist/features/grid-interactions/adapters/inbound/GridInteractions.d.ts +5 -0
  12. package/dist/features/grid-interactions/adapters/inbound/GridInteractions.d.ts.map +1 -0
  13. package/dist/features/grid-interactions/adapters/inbound/GridInteractions.js +40 -0
  14. package/dist/features/grid-interactions/adapters/inbound/GridInteractions.js.map +1 -0
  15. package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.d.ts +2 -0
  16. package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.d.ts.map +1 -0
  17. package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.js +2 -0
  18. package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.js.map +1 -0
  19. package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.native.d.ts +11 -0
  20. package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.native.d.ts.map +1 -0
  21. package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.native.js +5 -0
  22. package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.native.js.map +1 -0
  23. package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.d.ts +11 -0
  24. package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.d.ts.map +1 -0
  25. package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.js +20 -0
  26. package/dist/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.js.map +1 -0
  27. package/dist/features/grid-interactions/adapters/inbound/createGridInteractionsPointerQueue.d.ts +17 -0
  28. package/dist/features/grid-interactions/adapters/inbound/createGridInteractionsPointerQueue.d.ts.map +1 -0
  29. package/dist/features/grid-interactions/adapters/inbound/createGridInteractionsPointerQueue.js +76 -0
  30. package/dist/features/grid-interactions/adapters/inbound/createGridInteractionsPointerQueue.js.map +1 -0
  31. package/dist/features/grid-interactions/adapters/inbound/resolveGridInteractionPointer.d.ts +21 -0
  32. package/dist/features/grid-interactions/adapters/inbound/resolveGridInteractionPointer.d.ts.map +1 -0
  33. package/dist/features/grid-interactions/adapters/inbound/resolveGridInteractionPointer.js +20 -0
  34. package/dist/features/grid-interactions/adapters/inbound/resolveGridInteractionPointer.js.map +1 -0
  35. package/dist/features/grid-interactions/application/createGridInteractionsController.d.ts +4 -0
  36. package/dist/features/grid-interactions/application/createGridInteractionsController.d.ts.map +1 -0
  37. package/dist/features/grid-interactions/application/createGridInteractionsController.js +200 -0
  38. package/dist/features/grid-interactions/application/createGridInteractionsController.js.map +1 -0
  39. package/dist/features/grid-interactions/meta.d.ts +11 -0
  40. package/dist/features/grid-interactions/meta.d.ts.map +1 -0
  41. package/dist/features/grid-interactions/meta.js +11 -0
  42. package/dist/features/grid-interactions/meta.js.map +1 -0
  43. package/dist/features/grid-interactions/public.d.ts +4 -0
  44. package/dist/features/grid-interactions/public.d.ts.map +1 -0
  45. package/dist/features/grid-interactions/public.js +3 -0
  46. package/dist/features/grid-interactions/public.js.map +1 -0
  47. package/dist/features/matrix-grid/adapters/inbound/MatrixGrid.d.ts.map +1 -1
  48. package/dist/features/matrix-grid/adapters/inbound/MatrixGrid.js +73 -19
  49. package/dist/features/matrix-grid/adapters/inbound/MatrixGrid.js.map +1 -1
  50. package/dist/features/matrix-grid/adapters/inbound/MatrixGridKeyboardProxy.d.ts +9 -0
  51. package/dist/features/matrix-grid/adapters/inbound/MatrixGridKeyboardProxy.d.ts.map +1 -0
  52. package/dist/features/matrix-grid/adapters/inbound/MatrixGridKeyboardProxy.js +5 -0
  53. package/dist/features/matrix-grid/adapters/inbound/MatrixGridKeyboardProxy.js.map +1 -0
  54. package/dist/features/matrix-grid/adapters/inbound/MatrixGridKeyboardProxy.native.d.ts +9 -0
  55. package/dist/features/matrix-grid/adapters/inbound/MatrixGridKeyboardProxy.native.d.ts.map +1 -0
  56. package/dist/features/matrix-grid/adapters/inbound/MatrixGridKeyboardProxy.native.js +5 -0
  57. package/dist/features/matrix-grid/adapters/inbound/MatrixGridKeyboardProxy.native.js.map +1 -0
  58. package/dist/features/matrix-grid/adapters/inbound/MatrixGridKeyboardProxy.web.d.ts +9 -0
  59. package/dist/features/matrix-grid/adapters/inbound/MatrixGridKeyboardProxy.web.d.ts.map +1 -0
  60. package/dist/features/matrix-grid/adapters/inbound/MatrixGridKeyboardProxy.web.js +28 -0
  61. package/dist/features/matrix-grid/adapters/inbound/MatrixGridKeyboardProxy.web.js.map +1 -0
  62. package/dist/features/matrix-grid/adapters/inbound/useMatrixGridKeyboardFocus.d.ts +14 -0
  63. package/dist/features/matrix-grid/adapters/inbound/useMatrixGridKeyboardFocus.d.ts.map +1 -0
  64. package/dist/features/matrix-grid/adapters/inbound/useMatrixGridKeyboardFocus.js +50 -0
  65. package/dist/features/matrix-grid/adapters/inbound/useMatrixGridKeyboardFocus.js.map +1 -0
  66. package/dist/features/matrix-grid/application/createMatrixGridSparseIndex.d.ts +18 -0
  67. package/dist/features/matrix-grid/application/createMatrixGridSparseIndex.d.ts.map +1 -0
  68. package/dist/features/matrix-grid/application/createMatrixGridSparseIndex.js +56 -0
  69. package/dist/features/matrix-grid/application/createMatrixGridSparseIndex.js.map +1 -0
  70. package/dist/features/matrix-grid/application/getMatrixGridNextFocusId.d.ts +4 -0
  71. package/dist/features/matrix-grid/application/getMatrixGridNextFocusId.d.ts.map +1 -0
  72. package/dist/features/matrix-grid/application/getMatrixGridNextFocusId.js +65 -0
  73. package/dist/features/matrix-grid/application/getMatrixGridNextFocusId.js.map +1 -0
  74. package/dist/features/matrix-grid/application/resolveMatrixGridSelection.d.ts +8 -1
  75. package/dist/features/matrix-grid/application/resolveMatrixGridSelection.d.ts.map +1 -1
  76. package/dist/features/matrix-grid/application/resolveMatrixGridSelection.js +15 -4
  77. package/dist/features/matrix-grid/application/resolveMatrixGridSelection.js.map +1 -1
  78. package/dist/features/registry/ZORA_COMPONENT_REGISTRY.d.ts.map +1 -1
  79. package/dist/features/registry/ZORA_COMPONENT_REGISTRY.js +2 -0
  80. package/dist/features/registry/ZORA_COMPONENT_REGISTRY.js.map +1 -1
  81. package/dist/features/registry/componentMeta.d.ts.map +1 -1
  82. package/dist/features/registry/componentMeta.js +2 -0
  83. package/dist/features/registry/componentMeta.js.map +1 -1
  84. package/dist/features/registry/createEventCapabilities.d.ts +4 -0
  85. package/dist/features/registry/createEventCapabilities.d.ts.map +1 -0
  86. package/dist/features/registry/createEventCapabilities.js +76 -0
  87. package/dist/features/registry/createEventCapabilities.js.map +1 -0
  88. package/dist/index.d.ts +1 -0
  89. package/dist/index.d.ts.map +1 -1
  90. package/dist/index.js +1 -0
  91. package/dist/index.js.map +1 -1
  92. package/dist/types/grid-interactions.d.ts +58 -0
  93. package/dist/types/grid-interactions.d.ts.map +1 -0
  94. package/dist/types/grid-interactions.js +2 -0
  95. package/dist/types/grid-interactions.js.map +1 -0
  96. package/dist/types/matrix-grid.d.ts +2 -0
  97. package/dist/types/matrix-grid.d.ts.map +1 -1
  98. package/dist/types/matrix-grid.js.map +1 -1
  99. package/package.json +1825 -7
  100. package/src/capabilities/index.test.ts +87 -15
  101. package/src/capabilities/index.ts +5 -2
  102. package/src/cli/index.test.ts +5 -3
  103. package/src/cli/index.ts +5 -2
  104. package/src/features/grid-interactions/adapters/inbound/GridInteractions.test.tsx +143 -0
  105. package/src/features/grid-interactions/adapters/inbound/GridInteractions.tsx +79 -0
  106. package/src/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.native.tsx +14 -0
  107. package/src/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.tsx +1 -0
  108. package/src/features/grid-interactions/adapters/inbound/GridInteractionsKeyboardProxy.web.tsx +41 -0
  109. package/src/features/grid-interactions/adapters/inbound/createGridInteractionsPointerQueue.test.ts +98 -0
  110. package/src/features/grid-interactions/adapters/inbound/createGridInteractionsPointerQueue.ts +94 -0
  111. package/src/features/grid-interactions/adapters/inbound/resolveGridInteractionPointer.test.ts +33 -0
  112. package/src/features/grid-interactions/adapters/inbound/resolveGridInteractionPointer.ts +45 -0
  113. package/src/features/grid-interactions/application/createGridInteractionsController.test.ts +229 -0
  114. package/src/features/grid-interactions/application/createGridInteractionsController.ts +248 -0
  115. package/src/features/grid-interactions/meta.ts +12 -0
  116. package/src/features/grid-interactions/public.ts +11 -0
  117. package/src/features/matrix-grid/adapters/inbound/MatrixGrid.test.tsx +280 -2
  118. package/src/features/matrix-grid/adapters/inbound/MatrixGrid.tsx +121 -31
  119. package/src/features/matrix-grid/adapters/inbound/MatrixGridKeyboardProxy.native.tsx +11 -0
  120. package/src/features/matrix-grid/adapters/inbound/MatrixGridKeyboardProxy.tsx +11 -0
  121. package/src/features/matrix-grid/adapters/inbound/MatrixGridKeyboardProxy.web.tsx +43 -0
  122. package/src/features/matrix-grid/adapters/inbound/useMatrixGridKeyboardFocus.ts +70 -0
  123. package/src/features/matrix-grid/application/createMatrixGridSparseIndex.test.ts +35 -0
  124. package/src/features/matrix-grid/application/createMatrixGridSparseIndex.ts +105 -0
  125. package/src/features/matrix-grid/application/getMatrixGridNextFocusId.test.ts +93 -0
  126. package/src/features/matrix-grid/application/getMatrixGridNextFocusId.ts +112 -0
  127. package/src/features/matrix-grid/application/resolveMatrixGridSelection.test.ts +63 -2
  128. package/src/features/matrix-grid/application/resolveMatrixGridSelection.ts +27 -4
  129. package/src/features/registry/ZORA_COMPONENT_REGISTRY.ts +2 -0
  130. package/src/features/registry/componentMeta.ts +2 -0
  131. package/src/features/registry/createEventCapabilities.ts +108 -0
  132. package/src/index.ts +11 -0
  133. package/src/structureOwnership.test.ts +5 -0
  134. package/src/types/grid-interactions.ts +72 -0
  135. package/src/types/matrix-grid.ts +2 -0
  136. package/web-dist/chunks/{Accordion-5g05kfmx.js → Accordion-0q2wzjdd.js} +4 -4
  137. package/web-dist/chunks/{Accordion-b96evb46.js → Accordion-1fsrnq73.js} +1 -1
  138. package/web-dist/chunks/{Accordion-h3y8qev2.js → Accordion-38ts3gnn.js} +2 -2
  139. package/web-dist/chunks/{Accordion-jnc315h7.js → Accordion-8pkw32q9.js} +2 -2
  140. package/web-dist/chunks/{Accordion-h7zsx9eg.js → Accordion-9jrxktne.js} +3 -3
  141. package/web-dist/chunks/{Accordion-9w8wg9mf.js → Accordion-9rnsg9pk.js} +1 -1
  142. package/web-dist/chunks/{Accordion-4zqd7twp.js → Accordion-9ygakvwk.js} +5 -5
  143. package/web-dist/chunks/{Accordion-fhnazn3n.js → Accordion-b74j4hdt.js} +1 -1
  144. package/web-dist/chunks/{Accordion-nssr5gep.js → Accordion-c50pywj3.js} +2 -2
  145. package/web-dist/chunks/{Accordion-pvj5npzc.js → Accordion-ddrn94v3.js} +3 -3
  146. package/web-dist/chunks/{Accordion-090y18cv.js → Accordion-dk0dtyc0.js} +3 -3
  147. package/web-dist/chunks/{Accordion-59pa0677.js → Accordion-fcczvwgg.js} +0 -1
  148. package/web-dist/chunks/{Accordion-sp4k08kw.js → Accordion-geah5s4f.js} +4 -4
  149. package/web-dist/chunks/{Accordion-6rrqpb2h.js → Accordion-h4ekm5cp.js} +2 -2
  150. package/web-dist/chunks/{Accordion-3d4wkzg7.js → Accordion-hecpczme.js} +5 -5
  151. package/web-dist/chunks/{Accordion-a6551mh4.js → Accordion-hn43wt45.js} +1 -1
  152. package/web-dist/chunks/{Accordion-sw7ep29d.js → Accordion-hnx7hx55.js} +2 -2
  153. package/web-dist/chunks/{Accordion-rsvphbe4.js → Accordion-j1chy29e.js} +1 -1
  154. package/web-dist/chunks/{Accordion-ewf1zen9.js → Accordion-k9cxpg08.js} +5 -5
  155. package/web-dist/chunks/{Accordion-5df6cgyf.js → Accordion-md1g54wg.js} +1 -1
  156. package/web-dist/chunks/{Accordion-kjtcbe09.js → Accordion-p1ebrt17.js} +3 -3
  157. package/web-dist/chunks/{Accordion-qn398m1x.js → Accordion-r2g18vxa.js} +1 -1
  158. package/web-dist/chunks/{Accordion-zt9yghdz.js → Accordion-rctfgyfv.js} +4 -4
  159. package/web-dist/chunks/{Accordion-88t8fgxv.js → Accordion-rfc5mdbv.js} +1 -1
  160. package/web-dist/chunks/{Accordion-fmzxat3v.js → Accordion-sdnzqktc.js} +4 -4
  161. package/web-dist/chunks/{Accordion-v0grdh6q.js → Accordion-sq7ya2ye.js} +2 -2
  162. package/web-dist/chunks/{Accordion-ejx5gcf9.js → Accordion-t5scffdr.js} +9 -9
  163. package/web-dist/chunks/{Accordion-sy1cc5my.js → Accordion-ts788t7b.js} +3 -3
  164. package/web-dist/chunks/{Accordion-4s68t3h7.js → Accordion-v3rzy273.js} +6 -6
  165. package/web-dist/chunks/{Accordion-7tjz25jh.js → Accordion-vbzjq3z8.js} +1 -1
  166. package/web-dist/chunks/{Accordion-jh72azm2.js → Accordion-vg337250.js} +2 -2
  167. package/web-dist/chunks/{Accordion-42jfy1a1.js → Accordion-vj1m2wck.js} +1 -1
  168. package/web-dist/chunks/{Accordion-tqpsmdrk.js → Accordion-vxg5sw01.js} +4 -4
  169. package/web-dist/chunks/{Accordion-61ntvh14.js → Accordion-w59y0jdf.js} +4 -4
  170. package/web-dist/chunks/{Accordion-fjxcn6g9.js → Accordion-wcmrasqx.js} +8 -8
  171. package/web-dist/chunks/{Accordion-r1cj2w9e.js → Accordion-wpk8dxqp.js} +3 -3
  172. package/web-dist/chunks/{Accordion-w6qedeba.js → Accordion-x055n3b3.js} +1 -1
  173. package/web-dist/chunks/{Accordion-dbf93gxh.js → Accordion-xcm1qc39.js} +1 -1
  174. package/web-dist/chunks/{Accordion-7kq500r1.js → Accordion-xgxxg1hz.js} +1 -1
  175. package/web-dist/chunks/{Accordion-a4cma5b6.js → Accordion-y3083394.js} +3 -3
  176. package/web-dist/chunks/{Accordion-40tenzcg.js → Accordion-yexfbrzg.js} +4 -4
  177. package/web-dist/chunks/{Accordion-2r0gqk3x.js → Accordion-ztw7zp6v.js} +5 -5
  178. package/web-dist/chunks/{Accordion-bz04casa.js → Accordion-zypyr9xw.js} +211 -6
  179. package/web-dist/components/accordion/Accordion.js +5 -5
  180. package/web-dist/components/accordion-item/AccordionItem.js +5 -5
  181. package/web-dist/components/activity-indicator/ActivityIndicator.js +1 -1
  182. package/web-dist/components/app-bar/AppBar.js +7 -7
  183. package/web-dist/components/app-header/AppHeader.js +7 -7
  184. package/web-dist/components/app-shell/AppShell.js +2 -2
  185. package/web-dist/components/auth-screen/AuthScreen.js +10 -10
  186. package/web-dist/components/avatar/Avatar.js +5 -5
  187. package/web-dist/components/avatar-group/AvatarGroup.js +5 -5
  188. package/web-dist/components/badge/Badge.js +2 -2
  189. package/web-dist/components/barcode-scanner-view/BarcodeScannerView.js +10 -10
  190. package/web-dist/components/bottom-sheet/BottomSheet.js +3 -3
  191. package/web-dist/components/bottom-sheet-flat-list/BottomSheetFlatList.js +3 -3
  192. package/web-dist/components/bottom-sheet-scroll-view/BottomSheetScrollView.js +3 -3
  193. package/web-dist/components/bottom-sheet-section-list/BottomSheetSectionList.js +3 -3
  194. package/web-dist/components/bottom-sheet-view/BottomSheetView.js +3 -3
  195. package/web-dist/components/bottom-sheet-virtualized-list/BottomSheetVirtualizedList.js +3 -3
  196. package/web-dist/components/breadcrumbs/Breadcrumbs.js +5 -5
  197. package/web-dist/components/button/Button.js +3 -3
  198. package/web-dist/components/button-group/ButtonGroup.js +3 -3
  199. package/web-dist/components/camera-permission-view/CameraPermissionView.js +10 -10
  200. package/web-dist/components/card/Card.js +9 -9
  201. package/web-dist/components/card-back/CardBack.js +2 -2
  202. package/web-dist/components/card-hand/CardHand.js +2 -2
  203. package/web-dist/components/chat-list-item/ChatListItem.js +7 -7
  204. package/web-dist/components/checkbox/Checkbox.js +4 -4
  205. package/web-dist/components/checkbox-group/CheckboxGroup.js +4 -4
  206. package/web-dist/components/chess-board/ChessBoard.js +2 -2
  207. package/web-dist/components/chip/Chip.js +5 -5
  208. package/web-dist/components/chip-group/ChipGroup.js +5 -5
  209. package/web-dist/components/collection-editor/CollectionEditor.js +9 -9
  210. package/web-dist/components/content-rail/ContentRail.js +4 -4
  211. package/web-dist/components/data-table/DataTable.js +14 -14
  212. package/web-dist/components/date-picker/DatePicker.js +5 -5
  213. package/web-dist/components/dialog/Dialog.js +4 -4
  214. package/web-dist/components/divider/Divider.js +2 -2
  215. package/web-dist/components/empty-state/EmptyState.js +10 -10
  216. package/web-dist/components/explorer/Explorer.js +7 -7
  217. package/web-dist/components/field/Field.js +4 -4
  218. package/web-dist/components/file-explorer/FileExplorer.js +7 -7
  219. package/web-dist/components/flat-list/FlatList.js +5 -5
  220. package/web-dist/components/forgot-password-form/ForgotPasswordForm.js +10 -10
  221. package/web-dist/components/form/Form.js +6 -6
  222. package/web-dist/components/form-actions/FormActions.js +6 -6
  223. package/web-dist/components/form-error/FormError.js +6 -6
  224. package/web-dist/components/game/Game.js +3 -3
  225. package/web-dist/components/game-entity/GameEntity.js +3 -3
  226. package/web-dist/components/game-field/GameField.js +3 -3
  227. package/web-dist/components/game-input-zone/GameInputZone.js +3 -3
  228. package/web-dist/components/game-measurement-probe/GameMeasurementProbe.js +3 -3
  229. package/web-dist/components/game-overlay/GameOverlay.js +3 -3
  230. package/web-dist/components/gradient/Gradient.js +3 -3
  231. package/web-dist/components/gradient-renderer-provider/GradientRendererProvider.js +3 -3
  232. package/web-dist/components/graph-view/GraphView.js +2 -2
  233. package/web-dist/components/grid/Grid.js +2 -2
  234. package/web-dist/components/grid-interactions/GridInteractions.d.ts +3 -0
  235. package/web-dist/components/grid-interactions/GridInteractions.js +379 -0
  236. package/web-dist/components/grid-interactions/index.d.ts +1 -0
  237. package/web-dist/components/grid-interactions/index.js +2 -0
  238. package/web-dist/components/grid-line-overlay/GridLineOverlay.js +2 -2
  239. package/web-dist/components/grid-ruler/GridRuler.js +2 -2
  240. package/web-dist/components/grid-view/GridView.js +1 -1
  241. package/web-dist/components/heading/Heading.js +2 -2
  242. package/web-dist/components/hero/Hero.js +9 -9
  243. package/web-dist/components/icon/Icon.js +2 -2
  244. package/web-dist/components/icon-button/IconButton.js +3 -3
  245. package/web-dist/components/image/Image.js +4 -4
  246. package/web-dist/components/keyboard-avoiding-view/KeyboardAvoidingView.js +2 -2
  247. package/web-dist/components/list/List.js +5 -5
  248. package/web-dist/components/list-item/ListItem.js +5 -5
  249. package/web-dist/components/list-section/ListSection.js +5 -5
  250. package/web-dist/components/matrix-grid/MatrixGrid.js +296 -47
  251. package/web-dist/components/media-card/MediaCard.js +9 -9
  252. package/web-dist/components/media-explorer/MediaExplorer.js +7 -7
  253. package/web-dist/components/message-bubble/MessageBubble.js +7 -7
  254. package/web-dist/components/metric-card/MetricCard.js +9 -9
  255. package/web-dist/components/missing-element/MissingElement.js +4 -4
  256. package/web-dist/components/o-auth-provider-button/OAuthProviderButton.js +10 -10
  257. package/web-dist/components/o-auth-provider-list/OAuthProviderList.js +10 -10
  258. package/web-dist/components/opening-book/OpeningBook.js +2 -2
  259. package/web-dist/components/otp-form/OtpForm.js +10 -10
  260. package/web-dist/components/pagination/Pagination.js +4 -4
  261. package/web-dist/components/palette-item/PaletteItem.js +9 -9
  262. package/web-dist/components/playing-card/PlayingCard.js +2 -2
  263. package/web-dist/components/poker-training-table/PokerTrainingTable.js +2 -2
  264. package/web-dist/components/popover-menu/PopoverMenu.js +4 -4
  265. package/web-dist/components/post-card/PostCard.js +9 -9
  266. package/web-dist/components/product-card/ProductCard.js +9 -9
  267. package/web-dist/components/progress/Progress.js +4 -4
  268. package/web-dist/components/progress-ring/ProgressRing.js +4 -4
  269. package/web-dist/components/radio/Radio.js +5 -5
  270. package/web-dist/components/radio-group/RadioGroup.js +5 -5
  271. package/web-dist/components/rating/Rating.js +3 -3
  272. package/web-dist/components/reader/Reader.js +10 -10
  273. package/web-dist/components/scan-overlay/ScanOverlay.js +10 -10
  274. package/web-dist/components/screen/Screen.js +2 -2
  275. package/web-dist/components/screen-section/ScreenSection.js +4 -4
  276. package/web-dist/components/scroll-view/ScrollView.js +2 -2
  277. package/web-dist/components/search-input/SearchInput.js +4 -4
  278. package/web-dist/components/section-header/SectionHeader.js +4 -4
  279. package/web-dist/components/section-list/SectionList.js +5 -5
  280. package/web-dist/components/select/Select.js +4 -4
  281. package/web-dist/components/selectable-item/SelectableItem.js +2 -2
  282. package/web-dist/components/selection-provider/SelectionProvider.js +2 -2
  283. package/web-dist/components/sign-in-form/SignInForm.js +10 -10
  284. package/web-dist/components/sign-up-form/SignUpForm.js +10 -10
  285. package/web-dist/components/skeleton/Skeleton.js +12 -12
  286. package/web-dist/components/skeleton-card/SkeletonCard.js +12 -12
  287. package/web-dist/components/skeleton-list/SkeletonList.js +12 -12
  288. package/web-dist/components/skeleton-text/SkeletonText.js +12 -12
  289. package/web-dist/components/spatial-grid/SpatialGrid.js +1 -1
  290. package/web-dist/components/surface/Surface.js +2 -2
  291. package/web-dist/components/switch/Switch.js +1 -1
  292. package/web-dist/components/tab/Tab.js +2 -2
  293. package/web-dist/components/tab-list/TabList.js +2 -2
  294. package/web-dist/components/tab-panel/TabPanel.js +2 -2
  295. package/web-dist/components/tabletop-table/TabletopTable.js +2 -2
  296. package/web-dist/components/tabs/Tabs.js +2 -2
  297. package/web-dist/components/text/Text.js +2 -2
  298. package/web-dist/components/text-input/TextInput.js +4 -4
  299. package/web-dist/components/tile-grid/TileGrid.js +1 -1
  300. package/web-dist/components/time-grid/TimeGrid.js +2 -2
  301. package/web-dist/components/time-picker/TimePicker.js +5 -5
  302. package/web-dist/components/timeline/Timeline.js +4 -4
  303. package/web-dist/components/toast/Toast.js +2 -2
  304. package/web-dist/components/toast-provider/ToastProvider.js +2 -2
  305. package/web-dist/components/toolbar/Toolbar.js +9 -9
  306. package/web-dist/components/tree-item/TreeItem.js +9 -9
  307. package/web-dist/components/tree-view/TreeView.js +1 -1
  308. package/web-dist/components/uploader/Uploader.js +12 -12
  309. package/web-dist/components/view/View.js +2 -2
  310. package/web-dist/manifest.json +787 -772
  311. package/web-dist/runtime/ZoraProvider.js +3 -3
@@ -0,0 +1,35 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+
3
+ import { createMatrixGridSparseIndex } from './createMatrixGridSparseIndex';
4
+ import { getMatrixGridNextFocusId } from './getMatrixGridNextFocusId';
5
+
6
+ describe('createMatrixGridSparseIndex', () => {
7
+ test('indexes more than 10k sparse cells without creating placement rectangles', () => {
8
+ const layout = {
9
+ columns: Array.from({ length: 1_000 }, (_, index) => ({ id: `column-${index}`, size: 10 })),
10
+ rows: Array.from({ length: 1_000 }, (_, index) => ({ id: `row-${index}`, size: 10 })),
11
+ };
12
+ const cells = layout.rows.flatMap((row) =>
13
+ layout.columns.slice(0, 10).map((column) => ({
14
+ columnId: column.id,
15
+ id: `${row.id}-${column.id}`,
16
+ rowId: row.id,
17
+ })),
18
+ );
19
+ const index = createMatrixGridSparseIndex(layout, [...cells].reverse());
20
+
21
+ expect(index.entries).toHaveLength(10_000);
22
+ expect(index.entries[0]).toEqual({
23
+ cell: cells.at(-1),
24
+ columnIndex: 9,
25
+ id: 'row-999-column-9',
26
+ rowIndex: 999,
27
+ });
28
+ expect(getMatrixGridNextFocusId(index, 'row-500-column-5', 'ArrowRight')).toBe(
29
+ 'row-500-column-6',
30
+ );
31
+ expect(getMatrixGridNextFocusId(index, 'row-500-column-5', 'ArrowDown')).toBe(
32
+ 'row-501-column-5',
33
+ );
34
+ });
35
+ });
@@ -0,0 +1,105 @@
1
+ import type { GridMatrixCell, GridMatrixLayout } from '@ankhorage/grid-view';
2
+
3
+ /*** Build a geometry-independent lookup for sparse matrix navigation, selection, and focus recovery. */
4
+ export function createMatrixGridSparseIndex(
5
+ layout: GridMatrixLayout,
6
+ cells: readonly GridMatrixCell[],
7
+ ): MatrixGridSparseIndex {
8
+ const rowIndexes = new Map(layout.rows.map((row, index) => [row.id, index]));
9
+ const columnIndexes = new Map(layout.columns.map((column, index) => [column.id, index]));
10
+ const entries = cells.map((cell) => createSparseEntry(cell, rowIndexes, columnIndexes));
11
+ const entriesByRow = groupEntries(entries, 'columnIndex');
12
+ const entriesByColumn = groupEntries(entries, 'rowIndex');
13
+ const orderedEntries = [...entries].sort(compareRowThenColumn);
14
+
15
+ return {
16
+ cellById: new Map(entries.map((entry) => [entry.id, entry.cell])),
17
+ entries,
18
+ entriesByColumn,
19
+ entriesByRow,
20
+ firstId: orderedEntries.at(0)?.id ?? null,
21
+ positionById: new Map(entries.map((entry) => [entry.id, entry])),
22
+ };
23
+ }
24
+
25
+ /*** Resolve validated matrix coordinates once without materializing every cell rectangle. */
26
+ function createSparseEntry(
27
+ cell: GridMatrixCell,
28
+ rowIndexes: ReadonlyMap<string, number>,
29
+ columnIndexes: ReadonlyMap<string, number>,
30
+ ): MatrixGridSparseIndexEntry {
31
+ const rowIndex = rowIndexes.get(cell.rowId);
32
+ const columnIndex = columnIndexes.get(cell.columnId);
33
+ if (rowIndex === undefined || columnIndex === undefined) {
34
+ throw new Error(`Matrix cell ${cell.id} references an unknown row or column.`);
35
+ }
36
+ return { cell, columnIndex, id: cell.id, rowIndex };
37
+ }
38
+
39
+ /*** Group and sort directional neighbours so arrow navigation is logarithmic within one axis. */
40
+ function groupEntries(
41
+ entries: readonly MatrixGridSparseIndexEntry[],
42
+ axis: 'columnIndex' | 'rowIndex',
43
+ ): ReadonlyMap<number, readonly MatrixGridSparseIndexEntry[]> {
44
+ const groups = new Map<number, MatrixGridSparseIndexEntry[]>();
45
+ for (const entry of entries) {
46
+ const groupKey = axis === 'columnIndex' ? entry.rowIndex : entry.columnIndex;
47
+ const group = groups.get(groupKey) ?? [];
48
+ group.push(entry);
49
+ groups.set(groupKey, group);
50
+ }
51
+ return new Map(
52
+ [...groups].map(([key, group]) => [
53
+ key,
54
+ group.sort((left, right) => compareAxisThenId(left, right, axis)),
55
+ ]),
56
+ );
57
+ }
58
+
59
+ /*** Keep the initial roving target deterministic even when sparse input arrives in arbitrary order. */
60
+ function compareRowThenColumn(
61
+ left: MatrixGridSparseIndexEntry,
62
+ right: MatrixGridSparseIndexEntry,
63
+ ): number {
64
+ return (
65
+ left.rowIndex - right.rowIndex ||
66
+ left.columnIndex - right.columnIndex ||
67
+ left.id.localeCompare(right.id)
68
+ );
69
+ }
70
+
71
+ /*** Sort a row or column by its navigated coordinate and stable identity. */
72
+ function compareAxisThenId(
73
+ left: MatrixGridSparseIndexEntry,
74
+ right: MatrixGridSparseIndexEntry,
75
+ axis: 'columnIndex' | 'rowIndex',
76
+ ): number {
77
+ return (
78
+ getAxisCoordinate(left, axis) - getAxisCoordinate(right, axis) ||
79
+ left.id.localeCompare(right.id)
80
+ );
81
+ }
82
+
83
+ /*** Read a selected coordinate without dynamically indexing an object. */
84
+ function getAxisCoordinate(
85
+ entry: MatrixGridSparseIndexEntry,
86
+ axis: 'columnIndex' | 'rowIndex',
87
+ ): number {
88
+ return axis === 'columnIndex' ? entry.columnIndex : entry.rowIndex;
89
+ }
90
+
91
+ export type MatrixGridSparseIndex = Readonly<{
92
+ cellById: ReadonlyMap<string, GridMatrixCell>;
93
+ entries: readonly MatrixGridSparseIndexEntry[];
94
+ entriesByColumn: ReadonlyMap<number, readonly MatrixGridSparseIndexEntry[]>;
95
+ entriesByRow: ReadonlyMap<number, readonly MatrixGridSparseIndexEntry[]>;
96
+ firstId: string | null;
97
+ positionById: ReadonlyMap<string, MatrixGridSparseIndexEntry>;
98
+ }>;
99
+
100
+ export type MatrixGridSparseIndexEntry = Readonly<{
101
+ cell: GridMatrixCell;
102
+ columnIndex: number;
103
+ id: string;
104
+ rowIndex: number;
105
+ }>;
@@ -0,0 +1,93 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+
3
+ import { createMatrixGridSparseIndex } from './createMatrixGridSparseIndex';
4
+ import { getMatrixGridNextFocusId } from './getMatrixGridNextFocusId';
5
+
6
+ const cells = [
7
+ {
8
+ columnId: 'a',
9
+ columnIndex: 0,
10
+ height: 1,
11
+ id: 'a1',
12
+ rowId: '1',
13
+ rowIndex: 0,
14
+ width: 1,
15
+ x: 0,
16
+ y: 0,
17
+ },
18
+ {
19
+ columnId: 'c',
20
+ columnIndex: 2,
21
+ height: 1,
22
+ id: 'c1',
23
+ rowId: '1',
24
+ rowIndex: 0,
25
+ width: 1,
26
+ x: 2,
27
+ y: 0,
28
+ },
29
+ {
30
+ columnId: 'a',
31
+ columnIndex: 0,
32
+ height: 1,
33
+ id: 'a3',
34
+ rowId: '3',
35
+ rowIndex: 2,
36
+ width: 1,
37
+ x: 0,
38
+ y: 2,
39
+ },
40
+ {
41
+ columnId: 'c',
42
+ columnIndex: 2,
43
+ height: 1,
44
+ id: 'c3',
45
+ rowId: '3',
46
+ rowIndex: 2,
47
+ width: 1,
48
+ x: 2,
49
+ y: 2,
50
+ },
51
+ ] as const;
52
+
53
+ const layout = {
54
+ columns: [
55
+ { id: 'a', size: 1 },
56
+ { id: 'b', size: 1 },
57
+ { id: 'c', size: 1 },
58
+ ],
59
+ rows: [
60
+ { id: '1', size: 1 },
61
+ { id: '2', size: 1 },
62
+ { id: '3', size: 1 },
63
+ ],
64
+ } as const;
65
+
66
+ const index = createMatrixGridSparseIndex(layout, cells);
67
+
68
+ describe('getMatrixGridNextFocusId', () => {
69
+ test('moves over sparse rows and columns without materializing missing cells', () => {
70
+ expect(getMatrixGridNextFocusId(index, 'a1', 'ArrowRight')).toBe('c1');
71
+ expect(getMatrixGridNextFocusId(index, 'c3', 'ArrowLeft')).toBe('a3');
72
+ expect(getMatrixGridNextFocusId(index, 'a1', 'ArrowDown')).toBe('a3');
73
+ expect(getMatrixGridNextFocusId(index, 'c3', 'ArrowUp')).toBe('c1');
74
+ });
75
+
76
+ test('retains focus at a sparse edge and initializes from the first real cell', () => {
77
+ expect(getMatrixGridNextFocusId(index, 'a1', 'ArrowLeft')).toBe('a1');
78
+ expect(getMatrixGridNextFocusId(index, null, 'ArrowRight')).toBe('a1');
79
+ expect(
80
+ getMatrixGridNextFocusId(createMatrixGridSparseIndex(layout, []), null, 'ArrowRight'),
81
+ ).toBeNull();
82
+ });
83
+
84
+ test('chooses geometric neighbours from an intentionally unsorted sparse input', () => {
85
+ const unsorted = createMatrixGridSparseIndex(layout, [cells[3], cells[1], cells[2], cells[0]]);
86
+
87
+ expect(getMatrixGridNextFocusId(unsorted, 'a1', 'ArrowRight')).toBe('c1');
88
+ expect(getMatrixGridNextFocusId(unsorted, 'c3', 'ArrowLeft')).toBe('a3');
89
+ expect(getMatrixGridNextFocusId(unsorted, 'a1', 'ArrowDown')).toBe('a3');
90
+ expect(getMatrixGridNextFocusId(unsorted, 'c3', 'ArrowUp')).toBe('c1');
91
+ expect(getMatrixGridNextFocusId(unsorted, null, 'ArrowRight')).toBe('a1');
92
+ });
93
+ });
@@ -0,0 +1,112 @@
1
+ import type {
2
+ MatrixGridSparseIndex,
3
+ MatrixGridSparseIndexEntry,
4
+ } from './createMatrixGridSparseIndex';
5
+
6
+ /*** Resolve arrow-key focus among existing sparse cells without inventing placeholder geometry. */
7
+ export function getMatrixGridNextFocusId(
8
+ index: MatrixGridSparseIndex,
9
+ currentId: string | null,
10
+ key: string,
11
+ ): string | null {
12
+ const current = currentId === null ? undefined : index.positionById.get(currentId);
13
+ if (!current) return index.firstId;
14
+
15
+ return resolveDirectionalCandidate(index, current, key)?.id ?? current.id;
16
+ }
17
+
18
+ /*** Find the closest existing sparse cell along the requested matrix axis. */
19
+ function resolveDirectionalCandidate(
20
+ index: MatrixGridSparseIndex,
21
+ current: MatrixGridSparseIndexEntry,
22
+ key: string,
23
+ ): MatrixGridSparseIndexEntry | undefined {
24
+ switch (key) {
25
+ case 'ArrowLeft':
26
+ return findDirectionalNeighbour(
27
+ index.entriesByRow.get(current.rowIndex),
28
+ current,
29
+ 'columnIndex',
30
+ -1,
31
+ );
32
+ case 'ArrowRight':
33
+ return findDirectionalNeighbour(
34
+ index.entriesByRow.get(current.rowIndex),
35
+ current,
36
+ 'columnIndex',
37
+ 1,
38
+ );
39
+ case 'ArrowUp':
40
+ return findDirectionalNeighbour(
41
+ index.entriesByColumn.get(current.columnIndex),
42
+ current,
43
+ 'rowIndex',
44
+ -1,
45
+ );
46
+ case 'ArrowDown':
47
+ return findDirectionalNeighbour(
48
+ index.entriesByColumn.get(current.columnIndex),
49
+ current,
50
+ 'rowIndex',
51
+ 1,
52
+ );
53
+ default:
54
+ return undefined;
55
+ }
56
+ }
57
+
58
+ /*** Read the nearest strictly directional neighbour from a pre-sorted sparse axis. */
59
+ function findDirectionalNeighbour(
60
+ entries: readonly MatrixGridSparseIndexEntry[] | undefined,
61
+ current: MatrixGridSparseIndexEntry,
62
+ axis: 'columnIndex' | 'rowIndex',
63
+ direction: -1 | 1,
64
+ ): MatrixGridSparseIndexEntry | undefined {
65
+ if (!entries) return undefined;
66
+ const coordinate = getAxisCoordinate(current, axis);
67
+ const insertionIndex = findFirstGreaterOrEqual(entries, coordinate, axis);
68
+ if (direction === -1) return insertionIndex === 0 ? undefined : entries[insertionIndex - 1];
69
+ return entries[findFirstGreater(entries, coordinate, axis)];
70
+ }
71
+
72
+ /*** Locate the first entry strictly after a matrix coordinate in a sorted sparse axis. */
73
+ function findFirstGreater(
74
+ entries: readonly MatrixGridSparseIndexEntry[],
75
+ coordinate: number,
76
+ axis: 'columnIndex' | 'rowIndex',
77
+ ): number {
78
+ let lower = 0;
79
+ let upper = entries.length;
80
+ while (lower < upper) {
81
+ const middle = Math.floor((lower + upper) / 2);
82
+ const entry = entries.at(middle);
83
+ if (entry && getAxisCoordinate(entry, axis) <= coordinate) lower = middle + 1;
84
+ else upper = middle;
85
+ }
86
+ return lower;
87
+ }
88
+
89
+ /*** Locate the first index at or after a matrix coordinate in a sorted sparse axis. */
90
+ function findFirstGreaterOrEqual(
91
+ entries: readonly MatrixGridSparseIndexEntry[],
92
+ coordinate: number,
93
+ axis: 'columnIndex' | 'rowIndex',
94
+ ): number {
95
+ let lower = 0;
96
+ let upper = entries.length;
97
+ while (lower < upper) {
98
+ const middle = Math.floor((lower + upper) / 2);
99
+ const entry = entries.at(middle);
100
+ if (entry && getAxisCoordinate(entry, axis) < coordinate) lower = middle + 1;
101
+ else upper = middle;
102
+ }
103
+ return lower;
104
+ }
105
+
106
+ /*** Read a selected coordinate without dynamically indexing sparse entries. */
107
+ function getAxisCoordinate(
108
+ entry: MatrixGridSparseIndexEntry,
109
+ axis: 'columnIndex' | 'rowIndex',
110
+ ): number {
111
+ return axis === 'columnIndex' ? entry.columnIndex : entry.rowIndex;
112
+ }
@@ -2,9 +2,70 @@ import { describe, expect, test } from 'bun:test';
2
2
 
3
3
  import { resolveMatrixGridSelection } from './resolveMatrixGridSelection';
4
4
 
5
+ const cells = [
6
+ {
7
+ columnId: 'a',
8
+ columnIndex: 0,
9
+ height: 1,
10
+ id: 'a1',
11
+ rowId: '1',
12
+ rowIndex: 0,
13
+ width: 1,
14
+ x: 0,
15
+ y: 0,
16
+ },
17
+ {
18
+ columnId: 'b',
19
+ columnIndex: 1,
20
+ height: 1,
21
+ id: 'b1',
22
+ rowId: '1',
23
+ rowIndex: 0,
24
+ width: 1,
25
+ x: 1,
26
+ y: 0,
27
+ },
28
+ {
29
+ columnId: 'a',
30
+ columnIndex: 0,
31
+ height: 1,
32
+ id: 'a2',
33
+ rowId: '2',
34
+ rowIndex: 1,
35
+ width: 1,
36
+ x: 0,
37
+ y: 1,
38
+ },
39
+ {
40
+ columnId: 'b',
41
+ columnIndex: 1,
42
+ height: 1,
43
+ id: 'b2',
44
+ rowId: '2',
45
+ rowIndex: 1,
46
+ width: 1,
47
+ x: 1,
48
+ y: 1,
49
+ },
50
+ ] as const;
51
+
5
52
  describe('resolveMatrixGridSelection', () => {
6
53
  test('preserves the canonical single and multi selection intent rules', () => {
7
- expect(resolveMatrixGridSelection(['a'], 'b', 'toggle', 'single')).toEqual(['b']);
8
- expect(resolveMatrixGridSelection(['a'], 'b', 'toggle', 'multi')).toEqual(['a', 'b']);
54
+ expect(resolveMatrixGridSelection(cells, ['a1'], 'b1', 'a1', 'toggle', 'single')).toEqual([
55
+ 'b1',
56
+ ]);
57
+ expect(resolveMatrixGridSelection(cells, ['a1'], 'b1', 'a1', 'toggle', 'multi')).toEqual([
58
+ 'a1',
59
+ 'b1',
60
+ ]);
61
+ });
62
+
63
+ test('uses the selection contract for a sparse rectangular shift range', () => {
64
+ expect(resolveMatrixGridSelection(cells, ['a1'], 'b2', 'a1', 'range', 'multi')).toEqual([
65
+ 'a1',
66
+ 'b1',
67
+ 'a2',
68
+ 'b2',
69
+ ]);
9
70
  });
10
71
  });
@@ -2,14 +2,37 @@ import { applySelectionIntent, type SelectionIntent } from '@ankhorage/utility/s
2
2
 
3
3
  import type { SelectionMode } from '../../../types/selection';
4
4
 
5
+ type MatrixGridSelectionIntent = SelectionIntent | 'range';
6
+
5
7
  /*** Apply ZORA's canonical selection intent while retaining the configured selection mode. */
6
8
  export function resolveMatrixGridSelection(
9
+ cells: readonly MatrixGridSelectionCell[],
7
10
  selectedIds: readonly string[],
8
11
  cellId: string,
9
- intent: SelectionIntent,
12
+ anchorId: string | null,
13
+ intent: MatrixGridSelectionIntent,
10
14
  mode: SelectionMode,
11
15
  ): readonly string[] {
12
- return mode === 'single'
13
- ? applySelectionIntent(selectedIds, cellId, 'replace')
14
- : applySelectionIntent(selectedIds, cellId, intent);
16
+ if (mode === 'single') return applySelectionIntent(selectedIds, cellId, 'replace');
17
+ if (intent !== 'range') return applySelectionIntent(selectedIds, cellId, intent);
18
+
19
+ const anchor = cells.find((cell) => cell.id === anchorId);
20
+ const target = cells.find((cell) => cell.id === cellId);
21
+ if (!anchor || !target) return applySelectionIntent(selectedIds, cellId, 'replace');
22
+
23
+ return cells
24
+ .filter(
25
+ (cell) =>
26
+ cell.rowIndex >= Math.min(anchor.rowIndex, target.rowIndex) &&
27
+ cell.rowIndex <= Math.max(anchor.rowIndex, target.rowIndex) &&
28
+ cell.columnIndex >= Math.min(anchor.columnIndex, target.columnIndex) &&
29
+ cell.columnIndex <= Math.max(anchor.columnIndex, target.columnIndex),
30
+ )
31
+ .map((cell) => cell.id);
15
32
  }
33
+
34
+ type MatrixGridSelectionCell = Readonly<{
35
+ columnIndex: number;
36
+ id: string;
37
+ rowIndex: number;
38
+ }>;
@@ -47,6 +47,7 @@ import {
47
47
  GameOverlay,
48
48
  } from '../game/public';
49
49
  import { Gradient } from '../gradient/public';
50
+ import { GridInteractions } from '../grid-interactions/public';
50
51
  import { GridLineOverlay, GridRuler } from '../grid-rulers/public';
51
52
  import { GridView, TileGrid } from '../grid-view/public';
52
53
  import { Hero } from '../hero/public';
@@ -200,6 +201,7 @@ const _ZORA_COMPONENT_REGISTRY = {
200
201
  FileExplorer,
201
202
  GridLineOverlay,
202
203
  GridRuler,
204
+ GridInteractions,
203
205
  GridView,
204
206
  TileGrid,
205
207
  TimeGrid,
@@ -53,6 +53,7 @@ import { gameMeasurementProbeMeta } from '../game/meta/gameMeasurementProbeMeta'
53
53
  import { gameMeta } from '../game/meta/gameMeta';
54
54
  import { gameOverlayMeta } from '../game/meta/gameOverlayMeta';
55
55
  import { gradientMeta } from '../gradient/gradientMeta';
56
+ import { gridInteractionsMeta } from '../grid-interactions/meta';
56
57
  import { gridLineOverlayMeta, gridRulerMeta } from '../grid-rulers/meta';
57
58
  import { gridViewMeta, tileGridMeta } from '../grid-view/meta';
58
59
  import { heroMeta } from '../hero/heroMeta';
@@ -204,6 +205,7 @@ export const ZORA_COMPONENT_META: ZoraComponentMetaRegistry = finalizeFeatureMet
204
205
  MediaExplorer: mediaExplorerMeta,
205
206
  GridLineOverlay: gridLineOverlayMeta,
206
207
  GridRuler: gridRulerMeta,
208
+ GridInteractions: gridInteractionsMeta,
207
209
  GridView: gridViewMeta,
208
210
  TileGrid: tileGridMeta,
209
211
  TimeGrid: timeGridMeta,
@@ -0,0 +1,108 @@
1
+ import { areCapabilitiesEqual, isCapabilityId } from '@ankhorage/capability';
2
+ import type { Capability } from '@ankhorage/contracts/capability';
3
+ import type { DataSchema } from '@ankhorage/contracts/data';
4
+
5
+ import type {
6
+ ZoraComponentEventMeta,
7
+ ZoraComponentEventPayloadFieldMeta,
8
+ } from '../../types/authoring';
9
+ import { ZORA_COMPONENT_META } from './componentMeta';
10
+
11
+ /*** Projects direct-manifest ZORA event metadata into unique portable event capabilities. */
12
+ export function createEventCapabilities(): readonly Capability[] {
13
+ const capabilitiesById = Object.values(ZORA_COMPONENT_META)
14
+ .filter((component) => component.directManifestNode)
15
+ .flatMap((component) => Object.values(component.events ?? {}))
16
+ .reduce<ReadonlyMap<Capability['id'], Capability>>(
17
+ (capabilities, event) => addEventCapability(capabilities, event),
18
+ new Map(),
19
+ );
20
+
21
+ return [...capabilitiesById.values()].sort((left, right) => left.id.localeCompare(right.id));
22
+ }
23
+
24
+ /*** Adds one namespaced metadata event while rejecting conflicting duplicate identities. */
25
+ function addEventCapability(
26
+ capabilities: ReadonlyMap<Capability['id'], Capability>,
27
+ event: ZoraComponentEventMeta,
28
+ ): ReadonlyMap<Capability['id'], Capability> {
29
+ if (!isCapabilityId(event.eventType)) return capabilities;
30
+
31
+ const capability = createEventCapability(event);
32
+ const existing = capabilities.get(capability.id);
33
+ if (existing !== undefined && !areCapabilitiesEqual(existing, capability)) {
34
+ throw new Error(`ZORA event metadata defines conflicting capability "${capability.id}".`);
35
+ }
36
+
37
+ return new Map([...capabilities, [capability.id, capability]]);
38
+ }
39
+
40
+ /*** Converts one emitted component event into its serializable canonical descriptor. */
41
+ function createEventCapability(event: ZoraComponentEventMeta): Capability {
42
+ if (!isCapabilityId(event.eventType)) {
43
+ throw new Error(
44
+ `ZORA event metadata must use a namespaced capability id: "${event.eventType}".`,
45
+ );
46
+ }
47
+
48
+ return {
49
+ id: event.eventType,
50
+ owner: '@ankhorage/zora',
51
+ access: ['emit'],
52
+ binding: { kind: 'event', bindableAs: ['source'] },
53
+ label: event.label,
54
+ ...(event.description === undefined ? {} : { description: event.description }),
55
+ output: { schema: createEventPayloadSchema(event.payloadFields ?? []) },
56
+ };
57
+ }
58
+
59
+ /*** Projects the documented event payload fields into an open object schema. */
60
+ function createEventPayloadSchema(
61
+ fields: readonly ZoraComponentEventPayloadFieldMeta[],
62
+ ): DataSchema {
63
+ return {
64
+ type: 'object',
65
+ properties: fields.reduce<Readonly<Record<string, DataSchema>>>(addPayloadField, {}),
66
+ };
67
+ }
68
+
69
+ /*** Adds one dotted metadata path to an immutable object-schema property map. */
70
+ function addPayloadField(
71
+ properties: Readonly<Record<string, DataSchema>>,
72
+ field: ZoraComponentEventPayloadFieldMeta,
73
+ ): Readonly<Record<string, DataSchema>> {
74
+ return addPayloadFieldAtPath(properties, field.path.split('.'), createPayloadFieldSchema(field));
75
+ }
76
+
77
+ /*** Recursively embeds a leaf schema below its documented object-property path. */
78
+ function addPayloadFieldAtPath(
79
+ properties: Readonly<Record<string, DataSchema>>,
80
+ path: readonly string[],
81
+ schema: DataSchema,
82
+ ): Readonly<Record<string, DataSchema>> {
83
+ const [key, ...remainingPath] = path;
84
+ if (key === undefined || key === '') return properties;
85
+ if (remainingPath.length === 0) return { ...properties, [key]: schema };
86
+
87
+ const current = Object.entries(properties).find(([propertyKey]) => propertyKey === key)?.[1];
88
+ const nestedProperties = addPayloadFieldAtPath(current?.properties ?? {}, remainingPath, schema);
89
+ return {
90
+ ...properties,
91
+ [key]: { ...current, type: 'object', properties: nestedProperties },
92
+ };
93
+ }
94
+
95
+ /*** Maps one authoring payload-field type and annotations to its portable data schema. */
96
+ function createPayloadFieldSchema(field: ZoraComponentEventPayloadFieldMeta): DataSchema {
97
+ return {
98
+ ...(field.type === 'object'
99
+ ? { type: 'object' as const }
100
+ : field.type === 'record'
101
+ ? { type: 'object' as const, additionalProperties: true }
102
+ : field.type === 'unknown'
103
+ ? {}
104
+ : { type: field.type }),
105
+ ...(field.label === undefined ? {} : { title: field.label }),
106
+ ...(field.description === undefined ? {} : { description: field.description }),
107
+ };
108
+ }
package/src/index.ts CHANGED
@@ -263,6 +263,17 @@ export type {
263
263
  GradientRendererProviderProps,
264
264
  } from './features/gradient/public';
265
265
  export { Gradient, GradientRendererProvider } from './features/gradient/public';
266
+ export {
267
+ createGridInteractionsController,
268
+ type GridInteractionIntent,
269
+ type GridInteractionItem,
270
+ type GridInteractionPointer,
271
+ GridInteractions,
272
+ type GridInteractionsController,
273
+ type GridInteractionsControllerProps,
274
+ type GridInteractionSnap,
275
+ type GridInteractionsProps,
276
+ } from './features/grid-interactions/public';
266
277
  export type {
267
278
  GridGuide,
268
279
  GridLineOverlayProps,
@@ -63,4 +63,9 @@ describe('src ownership', () => {
63
63
  expect(existsSync(path), path).toBe(false);
64
64
  }
65
65
  });
66
+
67
+ test('keeps event capability projection with the registry metadata owner', () => {
68
+ expect(existsSync('src/features/registry/createEventCapabilities.ts')).toBe(true);
69
+ expect(existsSync('src/capabilities/createEventCapabilities.ts')).toBe(false);
70
+ });
66
71
  });