@dreamboard-games/ui-sdk 0.0.41

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 (533) hide show
  1. package/LICENSE +89 -0
  2. package/NOTICE +1 -0
  3. package/README.md +154 -0
  4. package/dist/components/ActionButton.d.ts +13 -0
  5. package/dist/components/ActionButton.d.ts.map +1 -0
  6. package/dist/components/ActionButton.js +14 -0
  7. package/dist/components/ActionPanel.d.ts +33 -0
  8. package/dist/components/ActionPanel.d.ts.map +1 -0
  9. package/dist/components/ActionPanel.js +148 -0
  10. package/dist/components/Card.d.ts +29 -0
  11. package/dist/components/Card.d.ts.map +1 -0
  12. package/dist/components/Card.js +220 -0
  13. package/dist/components/ChromeSuppressionContext.d.ts +7 -0
  14. package/dist/components/ChromeSuppressionContext.d.ts.map +1 -0
  15. package/dist/components/ChromeSuppressionContext.js +34 -0
  16. package/dist/components/CostDisplay.d.ts +22 -0
  17. package/dist/components/CostDisplay.d.ts.map +1 -0
  18. package/dist/components/CostDisplay.js +41 -0
  19. package/dist/components/DiceRoller.d.ts +30 -0
  20. package/dist/components/DiceRoller.d.ts.map +1 -0
  21. package/dist/components/DiceRoller.js +319 -0
  22. package/dist/components/Drawer.d.ts +19 -0
  23. package/dist/components/Drawer.d.ts.map +1 -0
  24. package/dist/components/Drawer.js +55 -0
  25. package/dist/components/ErrorBoundary.d.ts +24 -0
  26. package/dist/components/ErrorBoundary.d.ts.map +1 -0
  27. package/dist/components/ErrorBoundary.js +37 -0
  28. package/dist/components/GameEndDisplay.d.ts +27 -0
  29. package/dist/components/GameEndDisplay.d.ts.map +1 -0
  30. package/dist/components/GameEndDisplay.js +185 -0
  31. package/dist/components/GameSkeleton.d.ts +12 -0
  32. package/dist/components/GameSkeleton.d.ts.map +1 -0
  33. package/dist/components/GameSkeleton.js +54 -0
  34. package/dist/components/Hand.d.ts +99 -0
  35. package/dist/components/Hand.d.ts.map +1 -0
  36. package/dist/components/Hand.js +162 -0
  37. package/dist/components/HandDock.d.ts +35 -0
  38. package/dist/components/HandDock.d.ts.map +1 -0
  39. package/dist/components/HandDock.js +124 -0
  40. package/dist/components/InteractionForm.d.ts +50 -0
  41. package/dist/components/InteractionForm.d.ts.map +1 -0
  42. package/dist/components/InteractionForm.js +402 -0
  43. package/dist/components/MoreActions.d.ts +49 -0
  44. package/dist/components/MoreActions.d.ts.map +1 -0
  45. package/dist/components/MoreActions.js +64 -0
  46. package/dist/components/PhaseIndicator.d.ts +35 -0
  47. package/dist/components/PhaseIndicator.d.ts.map +1 -0
  48. package/dist/components/PhaseIndicator.js +212 -0
  49. package/dist/components/PlayArea.d.ts +28 -0
  50. package/dist/components/PlayArea.d.ts.map +1 -0
  51. package/dist/components/PlayArea.js +48 -0
  52. package/dist/components/PluginRuntime.d.ts +37 -0
  53. package/dist/components/PluginRuntime.d.ts.map +1 -0
  54. package/dist/components/PluginRuntime.js +47 -0
  55. package/dist/components/PrimaryActionButton.d.ts +98 -0
  56. package/dist/components/PrimaryActionButton.d.ts.map +1 -0
  57. package/dist/components/PrimaryActionButton.js +183 -0
  58. package/dist/components/PrimaryButton.d.ts +20 -0
  59. package/dist/components/PrimaryButton.d.ts.map +1 -0
  60. package/dist/components/PrimaryButton.js +5 -0
  61. package/dist/components/PromptDialogHost.d.ts +15 -0
  62. package/dist/components/PromptDialogHost.d.ts.map +1 -0
  63. package/dist/components/PromptDialogHost.js +22 -0
  64. package/dist/components/ResourceCounter.d.ts +38 -0
  65. package/dist/components/ResourceCounter.d.ts.map +1 -0
  66. package/dist/components/ResourceCounter.js +118 -0
  67. package/dist/components/ThemedButton.d.ts +12 -0
  68. package/dist/components/ThemedButton.d.ts.map +1 -0
  69. package/dist/components/ThemedButton.js +38 -0
  70. package/dist/components/Toast.d.ts +35 -0
  71. package/dist/components/Toast.d.ts.map +1 -0
  72. package/dist/components/Toast.js +116 -0
  73. package/dist/components/board/HexGrid.d.ts +344 -0
  74. package/dist/components/board/HexGrid.d.ts.map +1 -0
  75. package/dist/components/board/HexGrid.js +340 -0
  76. package/dist/components/board/NetworkGraph.d.ts +100 -0
  77. package/dist/components/board/NetworkGraph.d.ts.map +1 -0
  78. package/dist/components/board/NetworkGraph.js +123 -0
  79. package/dist/components/board/SlotSystem.d.ts +71 -0
  80. package/dist/components/board/SlotSystem.d.ts.map +1 -0
  81. package/dist/components/board/SlotSystem.js +87 -0
  82. package/dist/components/board/SquareGrid.d.ts +188 -0
  83. package/dist/components/board/SquareGrid.d.ts.map +1 -0
  84. package/dist/components/board/SquareGrid.js +328 -0
  85. package/dist/components/board/TrackBoard.d.ts +113 -0
  86. package/dist/components/board/TrackBoard.d.ts.map +1 -0
  87. package/dist/components/board/TrackBoard.js +135 -0
  88. package/dist/components/board/ZoneMap.d.ts +88 -0
  89. package/dist/components/board/ZoneMap.d.ts.map +1 -0
  90. package/dist/components/board/ZoneMap.js +133 -0
  91. package/dist/components/board/hex-board-view.d.ts +69 -0
  92. package/dist/components/board/hex-board-view.d.ts.map +1 -0
  93. package/dist/components/board/hex-board-view.js +60 -0
  94. package/dist/components/board/index.d.ts +23 -0
  95. package/dist/components/board/index.d.ts.map +1 -0
  96. package/dist/components/board/index.js +40 -0
  97. package/dist/components/board/interaction-accessibility.d.ts +5 -0
  98. package/dist/components/board/interaction-accessibility.d.ts.map +1 -0
  99. package/dist/components/board/interaction-accessibility.js +13 -0
  100. package/dist/components/board/target-layer.d.ts +13 -0
  101. package/dist/components/board/target-layer.d.ts.map +1 -0
  102. package/dist/components/board/target-layer.js +10 -0
  103. package/dist/components/card-render-content.type-test.d.ts +2 -0
  104. package/dist/components/card-render-content.type-test.d.ts.map +1 -0
  105. package/dist/components/card-render-content.type-test.js +1 -0
  106. package/dist/components/index.d.ts +34 -0
  107. package/dist/components/index.d.ts.map +1 -0
  108. package/dist/components/index.js +35 -0
  109. package/dist/components/interaction-dialog-behavior.d.ts +15 -0
  110. package/dist/components/interaction-dialog-behavior.d.ts.map +1 -0
  111. package/dist/components/interaction-dialog-behavior.js +9 -0
  112. package/dist/components/surfaces/BlockerSurface.d.ts +27 -0
  113. package/dist/components/surfaces/BlockerSurface.d.ts.map +1 -0
  114. package/dist/components/surfaces/BlockerSurface.js +38 -0
  115. package/dist/components/surfaces/BoardSurface.d.ts +77 -0
  116. package/dist/components/surfaces/BoardSurface.d.ts.map +1 -0
  117. package/dist/components/surfaces/BoardSurface.js +180 -0
  118. package/dist/components/surfaces/ChromeSurface.d.ts +29 -0
  119. package/dist/components/surfaces/ChromeSurface.d.ts.map +1 -0
  120. package/dist/components/surfaces/ChromeSurface.js +34 -0
  121. package/dist/components/surfaces/ExhaustivenessAudit.d.ts +32 -0
  122. package/dist/components/surfaces/ExhaustivenessAudit.d.ts.map +1 -0
  123. package/dist/components/surfaces/ExhaustivenessAudit.js +65 -0
  124. package/dist/components/surfaces/InboxSurface.d.ts +40 -0
  125. package/dist/components/surfaces/InboxSurface.d.ts.map +1 -0
  126. package/dist/components/surfaces/InboxSurface.js +99 -0
  127. package/dist/components/surfaces/MarketSurface.d.ts +62 -0
  128. package/dist/components/surfaces/MarketSurface.d.ts.map +1 -0
  129. package/dist/components/surfaces/MarketSurface.js +242 -0
  130. package/dist/components/surfaces/PanelSurface.d.ts +111 -0
  131. package/dist/components/surfaces/PanelSurface.d.ts.map +1 -0
  132. package/dist/components/surfaces/PanelSurface.js +180 -0
  133. package/dist/components/surfaces/PlayerCardsSurface.d.ts +104 -0
  134. package/dist/components/surfaces/PlayerCardsSurface.d.ts.map +1 -0
  135. package/dist/components/surfaces/PlayerCardsSurface.js +178 -0
  136. package/dist/components/surfaces/internal/CardZoneFollowUpForm.d.ts +7 -0
  137. package/dist/components/surfaces/internal/CardZoneFollowUpForm.d.ts.map +1 -0
  138. package/dist/components/surfaces/internal/CardZoneFollowUpForm.js +9 -0
  139. package/dist/components/surfaces/internal/DefaultInteractionButton.d.ts +71 -0
  140. package/dist/components/surfaces/internal/DefaultInteractionButton.d.ts.map +1 -0
  141. package/dist/components/surfaces/internal/DefaultInteractionButton.js +82 -0
  142. package/dist/components/surfaces/internal/useCardZoneInteractions.d.ts +21 -0
  143. package/dist/components/surfaces/internal/useCardZoneInteractions.d.ts.map +1 -0
  144. package/dist/components/surfaces/internal/useCardZoneInteractions.js +202 -0
  145. package/dist/components/surfaces/types.d.ts +59 -0
  146. package/dist/components/surfaces/types.d.ts.map +1 -0
  147. package/dist/components/surfaces/types.js +1 -0
  148. package/dist/context/ClientParamSchemaContext.d.ts +21 -0
  149. package/dist/context/ClientParamSchemaContext.d.ts.map +1 -0
  150. package/dist/context/ClientParamSchemaContext.js +12 -0
  151. package/dist/context/InteractionDraftContext.d.ts +69 -0
  152. package/dist/context/InteractionDraftContext.d.ts.map +1 -0
  153. package/dist/context/InteractionDraftContext.js +145 -0
  154. package/dist/context/PluginSessionContext.d.ts +33 -0
  155. package/dist/context/PluginSessionContext.d.ts.map +1 -0
  156. package/dist/context/PluginSessionContext.js +38 -0
  157. package/dist/context/PluginStateContext.d.ts +116 -0
  158. package/dist/context/PluginStateContext.d.ts.map +1 -0
  159. package/dist/context/PluginStateContext.js +186 -0
  160. package/dist/context/RuntimeContext.d.ts +49 -0
  161. package/dist/context/RuntimeContext.d.ts.map +1 -0
  162. package/dist/context/RuntimeContext.js +67 -0
  163. package/dist/defaults/components.d.ts +52 -0
  164. package/dist/defaults/components.d.ts.map +1 -0
  165. package/dist/defaults/components.js +159 -0
  166. package/dist/defaults/index.d.ts +2 -0
  167. package/dist/defaults/index.d.ts.map +1 -0
  168. package/dist/defaults/index.js +1 -0
  169. package/dist/errors/ValidationError.d.ts +10 -0
  170. package/dist/errors/ValidationError.d.ts.map +1 -0
  171. package/dist/errors/ValidationError.js +23 -0
  172. package/dist/helpers/cards.d.ts +3 -0
  173. package/dist/helpers/cards.d.ts.map +1 -0
  174. package/dist/helpers/cards.js +11 -0
  175. package/dist/helpers/track-board.d.ts +79 -0
  176. package/dist/helpers/track-board.d.ts.map +1 -0
  177. package/dist/helpers/track-board.js +56 -0
  178. package/dist/hooks/useActivePlayers.d.ts +16 -0
  179. package/dist/hooks/useActivePlayers.d.ts.map +1 -0
  180. package/dist/hooks/useActivePlayers.js +17 -0
  181. package/dist/hooks/useBoardInteractions.d.ts +110 -0
  182. package/dist/hooks/useBoardInteractions.d.ts.map +1 -0
  183. package/dist/hooks/useBoardInteractions.js +248 -0
  184. package/dist/hooks/useBoardTopology.d.ts +23 -0
  185. package/dist/hooks/useBoardTopology.d.ts.map +1 -0
  186. package/dist/hooks/useBoardTopology.js +128 -0
  187. package/dist/hooks/useCards.d.ts +3 -0
  188. package/dist/hooks/useCards.d.ts.map +1 -0
  189. package/dist/hooks/useCards.js +5 -0
  190. package/dist/hooks/useGameSelector.d.ts +13 -0
  191. package/dist/hooks/useGameSelector.d.ts.map +1 -0
  192. package/dist/hooks/useGameSelector.js +67 -0
  193. package/dist/hooks/useGameView.d.ts +6 -0
  194. package/dist/hooks/useGameView.d.ts.map +1 -0
  195. package/dist/hooks/useGameView.js +7 -0
  196. package/dist/hooks/useHandLayout.d.ts +120 -0
  197. package/dist/hooks/useHandLayout.d.ts.map +1 -0
  198. package/dist/hooks/useHandLayout.js +235 -0
  199. package/dist/hooks/useHexBoard.d.ts +19 -0
  200. package/dist/hooks/useHexBoard.d.ts.map +1 -0
  201. package/dist/hooks/useHexBoard.js +28 -0
  202. package/dist/hooks/useHexGrid.d.ts +56 -0
  203. package/dist/hooks/useHexGrid.d.ts.map +1 -0
  204. package/dist/hooks/useHexGrid.js +112 -0
  205. package/dist/hooks/useInteractionByKey.d.ts +29 -0
  206. package/dist/hooks/useInteractionByKey.d.ts.map +1 -0
  207. package/dist/hooks/useInteractionByKey.js +263 -0
  208. package/dist/hooks/useInteractionHandle.d.ts +103 -0
  209. package/dist/hooks/useInteractionHandle.d.ts.map +1 -0
  210. package/dist/hooks/useInteractionHandle.js +254 -0
  211. package/dist/hooks/useIsMobile.d.ts +7 -0
  212. package/dist/hooks/useIsMobile.d.ts.map +1 -0
  213. package/dist/hooks/useIsMobile.js +29 -0
  214. package/dist/hooks/useIsMyTurn.d.ts +6 -0
  215. package/dist/hooks/useIsMyTurn.d.ts.map +1 -0
  216. package/dist/hooks/useIsMyTurn.js +11 -0
  217. package/dist/hooks/useLobby.d.ts +28 -0
  218. package/dist/hooks/useLobby.d.ts.map +1 -0
  219. package/dist/hooks/useLobby.js +60 -0
  220. package/dist/hooks/useMe.d.ts +11 -0
  221. package/dist/hooks/useMe.d.ts.map +1 -0
  222. package/dist/hooks/useMe.js +32 -0
  223. package/dist/hooks/usePanZoom.d.ts +113 -0
  224. package/dist/hooks/usePanZoom.d.ts.map +1 -0
  225. package/dist/hooks/usePanZoom.js +165 -0
  226. package/dist/hooks/usePlayerInfo.d.ts +4 -0
  227. package/dist/hooks/usePlayerInfo.d.ts.map +1 -0
  228. package/dist/hooks/usePlayerInfo.js +21 -0
  229. package/dist/hooks/usePlayerTurnOrder.d.ts +15 -0
  230. package/dist/hooks/usePlayerTurnOrder.d.ts.map +1 -0
  231. package/dist/hooks/usePlayerTurnOrder.js +22 -0
  232. package/dist/hooks/usePluginRuntime.d.ts +45 -0
  233. package/dist/hooks/usePluginRuntime.d.ts.map +1 -0
  234. package/dist/hooks/usePluginRuntime.js +92 -0
  235. package/dist/hooks/useSeatInbox.d.ts +22 -0
  236. package/dist/hooks/useSeatInbox.d.ts.map +1 -0
  237. package/dist/hooks/useSeatInbox.js +43 -0
  238. package/dist/hooks/useSimultaneousPhase.d.ts +7 -0
  239. package/dist/hooks/useSimultaneousPhase.d.ts.map +1 -0
  240. package/dist/hooks/useSimultaneousPhase.js +8 -0
  241. package/dist/hooks/useSquareBoard.d.ts +21 -0
  242. package/dist/hooks/useSquareBoard.d.ts.map +1 -0
  243. package/dist/hooks/useSquareBoard.js +67 -0
  244. package/dist/hooks/useSquareGrid.d.ts +96 -0
  245. package/dist/hooks/useSquareGrid.d.ts.map +1 -0
  246. package/dist/hooks/useSquareGrid.js +152 -0
  247. package/dist/index.d.ts +30 -0
  248. package/dist/index.d.ts.map +1 -0
  249. package/dist/index.js +20 -0
  250. package/dist/internal/ui/alert.d.ts +8 -0
  251. package/dist/internal/ui/alert.d.ts.map +1 -0
  252. package/dist/internal/ui/alert.js +11 -0
  253. package/dist/internal/ui/button.d.ts +10 -0
  254. package/dist/internal/ui/button.d.ts.map +1 -0
  255. package/dist/internal/ui/button.js +21 -0
  256. package/dist/internal/ui/dialog.d.ts +16 -0
  257. package/dist/internal/ui/dialog.d.ts.map +1 -0
  258. package/dist/internal/ui/dialog.js +35 -0
  259. package/dist/internal/ui/input.d.ts +3 -0
  260. package/dist/internal/ui/input.d.ts.map +1 -0
  261. package/dist/internal/ui/input.js +5 -0
  262. package/dist/internal/ui/label.d.ts +4 -0
  263. package/dist/internal/ui/label.d.ts.map +1 -0
  264. package/dist/internal/ui/label.js +7 -0
  265. package/dist/internal/ui/select.d.ts +9 -0
  266. package/dist/internal/ui/select.d.ts.map +1 -0
  267. package/dist/internal/ui/select.js +23 -0
  268. package/dist/internal/ui/tooltip.d.ts +7 -0
  269. package/dist/internal/ui/tooltip.d.ts.map +1 -0
  270. package/dist/internal/ui/tooltip.js +16 -0
  271. package/dist/internal/ui/utils.d.ts +3 -0
  272. package/dist/internal/ui/utils.d.ts.map +1 -0
  273. package/dist/internal/ui/utils.js +4 -0
  274. package/dist/internal.d.ts +7 -0
  275. package/dist/internal.d.ts.map +1 -0
  276. package/dist/internal.js +4 -0
  277. package/dist/plugin-styles.css +246 -0
  278. package/dist/primitives/board.d.ts +29 -0
  279. package/dist/primitives/board.d.ts.map +1 -0
  280. package/dist/primitives/board.js +163 -0
  281. package/dist/primitives/game-ui-provider.d.ts +12 -0
  282. package/dist/primitives/game-ui-provider.d.ts.map +1 -0
  283. package/dist/primitives/game-ui-provider.js +7 -0
  284. package/dist/primitives/index.d.ts +8 -0
  285. package/dist/primitives/index.d.ts.map +1 -0
  286. package/dist/primitives/index.js +7 -0
  287. package/dist/primitives/interaction.d.ts +52 -0
  288. package/dist/primitives/interaction.d.ts.map +1 -0
  289. package/dist/primitives/interaction.js +250 -0
  290. package/dist/primitives/phase.d.ts +15 -0
  291. package/dist/primitives/phase.d.ts.map +1 -0
  292. package/dist/primitives/phase.js +18 -0
  293. package/dist/primitives/player-roster.d.ts +64 -0
  294. package/dist/primitives/player-roster.d.ts.map +1 -0
  295. package/dist/primitives/player-roster.js +149 -0
  296. package/dist/primitives/primitive-props.d.ts +15 -0
  297. package/dist/primitives/primitive-props.d.ts.map +1 -0
  298. package/dist/primitives/primitive-props.js +39 -0
  299. package/dist/primitives/prompt.d.ts +44 -0
  300. package/dist/primitives/prompt.d.ts.map +1 -0
  301. package/dist/primitives/prompt.js +101 -0
  302. package/dist/primitives/zone.d.ts +31 -0
  303. package/dist/primitives/zone.d.ts.map +1 -0
  304. package/dist/primitives/zone.js +58 -0
  305. package/dist/reducer.d.ts +21 -0
  306. package/dist/reducer.d.ts.map +1 -0
  307. package/dist/reducer.js +14 -0
  308. package/dist/runtime/createPluginRuntimeAPI.d.ts +67 -0
  309. package/dist/runtime/createPluginRuntimeAPI.d.ts.map +1 -0
  310. package/dist/runtime/createPluginRuntimeAPI.js +419 -0
  311. package/dist/theme/ThemeProvider.d.ts +98 -0
  312. package/dist/theme/ThemeProvider.d.ts.map +1 -0
  313. package/dist/theme/ThemeProvider.js +148 -0
  314. package/dist/theme/board.d.ts +42 -0
  315. package/dist/theme/board.d.ts.map +1 -0
  316. package/dist/theme/board.js +34 -0
  317. package/dist/theme/css-vars.d.ts +31 -0
  318. package/dist/theme/css-vars.d.ts.map +1 -0
  319. package/dist/theme/css-vars.js +88 -0
  320. package/dist/theme/derive.d.ts +66 -0
  321. package/dist/theme/derive.d.ts.map +1 -0
  322. package/dist/theme/derive.js +161 -0
  323. package/dist/theme/index.d.ts +22 -0
  324. package/dist/theme/index.d.ts.map +1 -0
  325. package/dist/theme/index.js +20 -0
  326. package/dist/theme/presets/arcade.d.ts +10 -0
  327. package/dist/theme/presets/arcade.d.ts.map +1 -0
  328. package/dist/theme/presets/arcade.js +257 -0
  329. package/dist/theme/presets/studio.d.ts +10 -0
  330. package/dist/theme/presets/studio.d.ts.map +1 -0
  331. package/dist/theme/presets/studio.js +257 -0
  332. package/dist/theme/presets/tabletop.d.ts +15 -0
  333. package/dist/theme/presets/tabletop.d.ts.map +1 -0
  334. package/dist/theme/presets/tabletop.js +262 -0
  335. package/dist/theme/tokens.d.ts +345 -0
  336. package/dist/theme/tokens.d.ts.map +1 -0
  337. package/dist/theme/tokens.js +57 -0
  338. package/dist/types/player-state.d.ts +337 -0
  339. package/dist/types/player-state.d.ts.map +1 -0
  340. package/dist/types/player-state.js +1 -0
  341. package/dist/types/plugin-state.d.ts +324 -0
  342. package/dist/types/plugin-state.d.ts.map +1 -0
  343. package/dist/types/plugin-state.js +1 -0
  344. package/dist/types/reducer-state.d.ts +10 -0
  345. package/dist/types/reducer-state.d.ts.map +1 -0
  346. package/dist/types/reducer-state.js +1 -0
  347. package/dist/types/runtime-api.d.ts +99 -0
  348. package/dist/types/runtime-api.d.ts.map +1 -0
  349. package/dist/types/runtime-api.js +1 -0
  350. package/dist/types/tiled-board.d.ts +187 -0
  351. package/dist/types/tiled-board.d.ts.map +1 -0
  352. package/dist/types/tiled-board.js +226 -0
  353. package/dist/ui-contract.d.ts +78 -0
  354. package/dist/ui-contract.d.ts.map +1 -0
  355. package/dist/ui-contract.js +15 -0
  356. package/dist/ui-sdk.d.ts +3409 -0
  357. package/dist/utils/interaction-inputs.d.ts +22 -0
  358. package/dist/utils/interaction-inputs.d.ts.map +1 -0
  359. package/dist/utils/interaction-inputs.js +219 -0
  360. package/dist/utils/interaction-labels.d.ts +4 -0
  361. package/dist/utils/interaction-labels.d.ts.map +1 -0
  362. package/dist/utils/interaction-labels.js +18 -0
  363. package/dist/utils/interaction-status.d.ts +15 -0
  364. package/dist/utils/interaction-status.d.ts.map +1 -0
  365. package/dist/utils/interaction-status.js +31 -0
  366. package/package.json +101 -0
  367. package/src/components/ActionButton.tsx +48 -0
  368. package/src/components/ActionPanel.tsx +310 -0
  369. package/src/components/Card.tsx +385 -0
  370. package/src/components/ChromeSuppressionContext.tsx +70 -0
  371. package/src/components/CostDisplay.test.tsx +23 -0
  372. package/src/components/CostDisplay.tsx +145 -0
  373. package/src/components/DiceRoller.tsx +601 -0
  374. package/src/components/Drawer.tsx +179 -0
  375. package/src/components/ErrorBoundary.tsx +119 -0
  376. package/src/components/GameEndDisplay.test.tsx +19 -0
  377. package/src/components/GameEndDisplay.tsx +398 -0
  378. package/src/components/GameSkeleton.tsx +260 -0
  379. package/src/components/Hand.tsx +387 -0
  380. package/src/components/HandDock.tsx +257 -0
  381. package/src/components/InteractionForm.test.tsx +303 -0
  382. package/src/components/InteractionForm.tsx +1029 -0
  383. package/src/components/MoreActions.test.tsx +93 -0
  384. package/src/components/MoreActions.tsx +143 -0
  385. package/src/components/PhaseIndicator.tsx +341 -0
  386. package/src/components/PlayArea.tsx +125 -0
  387. package/src/components/PluginRuntime.tsx +92 -0
  388. package/src/components/PrimaryActionButton.test.tsx +138 -0
  389. package/src/components/PrimaryActionButton.tsx +351 -0
  390. package/src/components/PrimaryButton.tsx +44 -0
  391. package/src/components/PromptDialogHost.tsx +92 -0
  392. package/src/components/ResourceCounter.test.tsx +29 -0
  393. package/src/components/ResourceCounter.tsx +275 -0
  394. package/src/components/ThemedButton.tsx +78 -0
  395. package/src/components/Toast.tsx +251 -0
  396. package/src/components/__fixtures__/ActionButton.fixture.tsx +234 -0
  397. package/src/components/__fixtures__/ActionPanel.fixture.tsx +298 -0
  398. package/src/components/__fixtures__/Card.fixture.tsx +185 -0
  399. package/src/components/__fixtures__/CostDisplay.fixture.tsx +156 -0
  400. package/src/components/__fixtures__/DiceRoller.fixture.tsx +435 -0
  401. package/src/components/__fixtures__/Drawer.fixture.tsx +113 -0
  402. package/src/components/__fixtures__/ErrorBoundary.fixture.tsx +82 -0
  403. package/src/components/__fixtures__/GameEndDisplay.fixture.tsx +188 -0
  404. package/src/components/__fixtures__/GameSkeleton.fixture.tsx +46 -0
  405. package/src/components/__fixtures__/Hand.fixture.tsx +522 -0
  406. package/src/components/__fixtures__/HexGrid.fixture.tsx +1181 -0
  407. package/src/components/__fixtures__/NetworkGraph.fixture.tsx +599 -0
  408. package/src/components/__fixtures__/PhaseIndicator.fixture.tsx +181 -0
  409. package/src/components/__fixtures__/PlayArea.fixture.tsx +221 -0
  410. package/src/components/__fixtures__/ResourceCounter.fixture.tsx +227 -0
  411. package/src/components/__fixtures__/SlotSystem.fixture.tsx +824 -0
  412. package/src/components/__fixtures__/SquareGrid.fixture.tsx +764 -0
  413. package/src/components/__fixtures__/Toast.fixture.tsx +97 -0
  414. package/src/components/__fixtures__/TrackBoard.fixture.tsx +685 -0
  415. package/src/components/__fixtures__/ZoneMap.fixture.tsx +754 -0
  416. package/src/components/board/HexGrid.tsx +1294 -0
  417. package/src/components/board/NetworkGraph.tsx +476 -0
  418. package/src/components/board/SlotSystem.tsx +339 -0
  419. package/src/components/board/SquareGrid.tsx +1165 -0
  420. package/src/components/board/TrackBoard.tsx +496 -0
  421. package/src/components/board/ZoneMap.tsx +448 -0
  422. package/src/components/board/hex-board-view.test.tsx +114 -0
  423. package/src/components/board/hex-board-view.ts +123 -0
  424. package/src/components/board/index.ts +142 -0
  425. package/src/components/board/interaction-accessibility.ts +21 -0
  426. package/src/components/board/target-layer-grids.test.tsx +420 -0
  427. package/src/components/board/target-layer.ts +30 -0
  428. package/src/components/card-render-content.type-test.ts +27 -0
  429. package/src/components/index.ts +208 -0
  430. package/src/components/interaction-dialog-behavior.test.ts +23 -0
  431. package/src/components/interaction-dialog-behavior.ts +22 -0
  432. package/src/components/surfaces/BlockerSurface.test.tsx +158 -0
  433. package/src/components/surfaces/BlockerSurface.tsx +127 -0
  434. package/src/components/surfaces/BoardSurface.tsx +340 -0
  435. package/src/components/surfaces/ChromeSurface.tsx +123 -0
  436. package/src/components/surfaces/ExhaustivenessAudit.tsx +91 -0
  437. package/src/components/surfaces/InboxSurface.test.tsx +149 -0
  438. package/src/components/surfaces/InboxSurface.tsx +245 -0
  439. package/src/components/surfaces/MarketSurface.tsx +544 -0
  440. package/src/components/surfaces/PanelSurface.test.tsx +496 -0
  441. package/src/components/surfaces/PanelSurface.tsx +458 -0
  442. package/src/components/surfaces/PlayerCardsSurface.tsx +525 -0
  443. package/src/components/surfaces/internal/CardZoneFollowUpForm.tsx +35 -0
  444. package/src/components/surfaces/internal/DefaultInteractionButton.tsx +219 -0
  445. package/src/components/surfaces/internal/useCardZoneInteractions.ts +311 -0
  446. package/src/components/surfaces/types.ts +100 -0
  447. package/src/context/ClientParamSchemaContext.tsx +44 -0
  448. package/src/context/InteractionDraftContext.tsx +204 -0
  449. package/src/context/PluginSessionContext.tsx +47 -0
  450. package/src/context/PluginStateContext.tsx +254 -0
  451. package/src/context/RuntimeContext.tsx +96 -0
  452. package/src/defaults/components.tsx +442 -0
  453. package/src/defaults/defaults.test.tsx +230 -0
  454. package/src/defaults/index.ts +1 -0
  455. package/src/errors/ValidationError.ts +29 -0
  456. package/src/helpers/cards.ts +19 -0
  457. package/src/helpers/track-board.ts +211 -0
  458. package/src/hooks/useActivePlayers.ts +19 -0
  459. package/src/hooks/useBoardInteractions.test.tsx +622 -0
  460. package/src/hooks/useBoardInteractions.ts +434 -0
  461. package/src/hooks/useBoardTopology.ts +316 -0
  462. package/src/hooks/useCards.test.tsx +129 -0
  463. package/src/hooks/useCards.ts +10 -0
  464. package/src/hooks/useGameSelector.ts +105 -0
  465. package/src/hooks/useGameView.ts +9 -0
  466. package/src/hooks/useHandLayout.ts +349 -0
  467. package/src/hooks/useHexBoard.ts +74 -0
  468. package/src/hooks/useHexGrid.ts +185 -0
  469. package/src/hooks/useInteractionByKey.ts +349 -0
  470. package/src/hooks/useInteractionHandle.ts +437 -0
  471. package/src/hooks/useIsMobile.ts +35 -0
  472. package/src/hooks/useIsMyTurn.test.tsx +99 -0
  473. package/src/hooks/useIsMyTurn.ts +15 -0
  474. package/src/hooks/useLobby.ts +76 -0
  475. package/src/hooks/useMe.ts +48 -0
  476. package/src/hooks/usePanZoom.ts +278 -0
  477. package/src/hooks/usePlayerInfo.ts +28 -0
  478. package/src/hooks/usePlayerTurnOrder.ts +23 -0
  479. package/src/hooks/usePluginRuntime.test.tsx +102 -0
  480. package/src/hooks/usePluginRuntime.ts +130 -0
  481. package/src/hooks/useSeatInbox.ts +61 -0
  482. package/src/hooks/useSimultaneousPhase.ts +10 -0
  483. package/src/hooks/useSquareBoard.ts +124 -0
  484. package/src/hooks/useSquareGrid.ts +328 -0
  485. package/src/index.test.ts +474 -0
  486. package/src/index.ts +148 -0
  487. package/src/internal/ui/alert.tsx +51 -0
  488. package/src/internal/ui/button.tsx +58 -0
  489. package/src/internal/ui/dialog.tsx +134 -0
  490. package/src/internal/ui/input.tsx +21 -0
  491. package/src/internal/ui/label.tsx +21 -0
  492. package/src/internal/ui/select.tsx +129 -0
  493. package/src/internal/ui/tooltip.tsx +54 -0
  494. package/src/internal/ui/utils.ts +5 -0
  495. package/src/internal.ts +18 -0
  496. package/src/plugin-styles.css +246 -0
  497. package/src/primitives/board.test.tsx +139 -0
  498. package/src/primitives/board.tsx +267 -0
  499. package/src/primitives/game-ui-provider.tsx +35 -0
  500. package/src/primitives/index.ts +83 -0
  501. package/src/primitives/interaction.test.tsx +420 -0
  502. package/src/primitives/interaction.tsx +405 -0
  503. package/src/primitives/phase.test.tsx +82 -0
  504. package/src/primitives/phase.tsx +43 -0
  505. package/src/primitives/player-roster.test.tsx +168 -0
  506. package/src/primitives/player-roster.tsx +301 -0
  507. package/src/primitives/primitive-props.tsx +82 -0
  508. package/src/primitives/prompt.test.tsx +159 -0
  509. package/src/primitives/prompt.tsx +203 -0
  510. package/src/primitives/zone.tsx +113 -0
  511. package/src/reducer.ts +42 -0
  512. package/src/runtime/createPluginRuntimeAPI.ts +605 -0
  513. package/src/theme/ThemeProvider.test.tsx +36 -0
  514. package/src/theme/ThemeProvider.tsx +252 -0
  515. package/src/theme/board.ts +61 -0
  516. package/src/theme/css-vars.ts +105 -0
  517. package/src/theme/derive.ts +240 -0
  518. package/src/theme/index.ts +61 -0
  519. package/src/theme/presets/arcade.ts +261 -0
  520. package/src/theme/presets/studio.ts +261 -0
  521. package/src/theme/presets/tabletop.ts +266 -0
  522. package/src/theme/theme.test.ts +258 -0
  523. package/src/theme/tokens.ts +392 -0
  524. package/src/types/player-state.ts +445 -0
  525. package/src/types/plugin-state.ts +407 -0
  526. package/src/types/reducer-state.ts +24 -0
  527. package/src/types/runtime-api.ts +114 -0
  528. package/src/types/tiled-board.ts +785 -0
  529. package/src/ui-contract.ts +168 -0
  530. package/src/utils/interaction-inputs.test.ts +109 -0
  531. package/src/utils/interaction-inputs.ts +331 -0
  532. package/src/utils/interaction-labels.ts +23 -0
  533. package/src/utils/interaction-status.ts +59 -0
@@ -0,0 +1,349 @@
1
+ import { useState, useRef, useEffect, useMemo, useCallback } from "react";
2
+ import type { RefObject } from "react";
3
+
4
+ // Card dimensions used for fan offset and total-width calculations.
5
+ // These must match the rendered widths from `Card.tsx`'s `sizeClasses`
6
+ // at the `sm` Tailwind breakpoint (≥640px), since that's the size
7
+ // player decks render at on virtually all gameplay viewports. Older
8
+ // values here were ~30% smaller than what `Card` actually paints,
9
+ // which caused cards to overlap by 30+ pixels even when there was
10
+ // plenty of horizontal room in the hand container.
11
+ const CARD_DIMENSIONS = {
12
+ sm: { width: 80, height: 112 },
13
+ md: { width: 96, height: 144 },
14
+ lg: { width: 128, height: 192 },
15
+ } as const;
16
+
17
+ const MIN_VISIBLE_PORTION = 16; // minimum visible pixels per card when overlapping
18
+ const HOVER_LIFT = 20; // pixels to lift on hover
19
+ const SELECTED_LIFT = 8; // pixels to lift when selected
20
+ const DRAWER_THRESHOLD_RATIO = 0.3; // if overlap is less than 30% of card width, use drawer
21
+
22
+ export type CardSize = "sm" | "md" | "lg";
23
+ export type HandLayout = "spread" | "stack" | "overlap";
24
+
25
+ export interface CardPositionProps {
26
+ /** X position (left offset) */
27
+ x: number;
28
+ /** Y position (vertical offset for hover/selected) */
29
+ y: number;
30
+ /** Z-index for layering */
31
+ zIndex: number;
32
+ /** CSS transform origin */
33
+ transformOrigin: string;
34
+ }
35
+
36
+ export interface UseHandLayoutOptions {
37
+ /** Number of cards in the hand */
38
+ cardCount: number;
39
+ /** Card size variant */
40
+ cardSize?: CardSize;
41
+ /** Layout style */
42
+ layout?: HandLayout;
43
+ /** Padding to subtract from container width */
44
+ containerPadding?: number;
45
+ }
46
+
47
+ export interface UseHandLayoutReturn {
48
+ /** Ref to attach to the container element */
49
+ containerRef: RefObject<HTMLDivElement | null>;
50
+ /** Ref to attach to the cards container element (for mouse tracking) */
51
+ cardsContainerRef: RefObject<HTMLDivElement | null>;
52
+ /** Measured container width */
53
+ containerWidth: number;
54
+ /** Calculated offset between cards */
55
+ cardOffset: number;
56
+ /** Total width of all cards */
57
+ totalWidth: number;
58
+ /** Whether drawer mode should be used */
59
+ useDrawerMode: boolean;
60
+ /** Card dimensions for the current size */
61
+ cardDimensions: { width: number; height: number };
62
+ /** Constants for positioning */
63
+ constants: {
64
+ hoverLift: number;
65
+ selectedLift: number;
66
+ };
67
+ /** Currently hovered card index */
68
+ hoveredIndex: number | null;
69
+ /** Mouse move handler for the cards container */
70
+ handleMouseMove: (e: React.MouseEvent<HTMLDivElement>) => void;
71
+ /** Mouse leave handler for the cards container */
72
+ handleMouseLeave: () => void;
73
+ /** Get position props for a card at the given index */
74
+ getCardPosition: (
75
+ index: number,
76
+ isHovered: boolean,
77
+ isSelected: boolean,
78
+ ) => CardPositionProps;
79
+ }
80
+
81
+ /**
82
+ * Hook for managing hand layout calculations and interactions.
83
+ *
84
+ * Provides container measurement, overlap calculations, drawer mode detection,
85
+ * and hover state management for card hand displays.
86
+ *
87
+ * @example
88
+ * ```tsx
89
+ * function MyHand({ cards, selectedIds }) {
90
+ * const {
91
+ * containerRef,
92
+ * cardsContainerRef,
93
+ * totalWidth,
94
+ * useDrawerMode,
95
+ * cardDimensions,
96
+ * hoveredIndex,
97
+ * handleMouseMove,
98
+ * handleMouseLeave,
99
+ * getCardPosition,
100
+ * constants,
101
+ * } = useHandLayout({
102
+ * cardCount: cards.length,
103
+ * cardSize: "md",
104
+ * layout: "overlap",
105
+ * });
106
+ *
107
+ * if (useDrawerMode) {
108
+ * return <MyDrawerUI cards={cards} />;
109
+ * }
110
+ *
111
+ * return (
112
+ * <div ref={containerRef}>
113
+ * <div
114
+ * ref={cardsContainerRef}
115
+ * style={{ width: totalWidth, height: cardDimensions.height + constants.hoverLift }}
116
+ * onMouseMove={handleMouseMove}
117
+ * onMouseLeave={handleMouseLeave}
118
+ * >
119
+ * {cards.map((card, index) => {
120
+ * const isHovered = hoveredIndex === index;
121
+ * const isSelected = selectedIds.includes(card.id);
122
+ * const position = getCardPosition(index, isHovered, isSelected);
123
+ *
124
+ * return (
125
+ * <div
126
+ * key={card.id}
127
+ * style={{
128
+ * position: "absolute",
129
+ * left: position.x,
130
+ * transform: `translateY(${position.y}px)`,
131
+ * zIndex: position.zIndex,
132
+ * }}
133
+ * >
134
+ * <MyCard card={card} />
135
+ * </div>
136
+ * );
137
+ * })}
138
+ * </div>
139
+ * </div>
140
+ * );
141
+ * }
142
+ * ```
143
+ */
144
+ export function useHandLayout({
145
+ cardCount,
146
+ cardSize = "md",
147
+ layout = "overlap",
148
+ containerPadding = 32,
149
+ }: UseHandLayoutOptions): UseHandLayoutReturn {
150
+ const containerRef = useRef<HTMLDivElement>(null);
151
+ const cardsContainerRef = useRef<HTMLDivElement>(null);
152
+ const [containerWidth, setContainerWidth] = useState(0);
153
+ const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
154
+
155
+ const cardDimensions = CARD_DIMENSIONS[cardSize];
156
+
157
+ // Measure container width with ResizeObserver
158
+ useEffect(() => {
159
+ const updateWidth = () => {
160
+ if (containerRef.current) {
161
+ setContainerWidth(containerRef.current.clientWidth - containerPadding);
162
+ }
163
+ };
164
+
165
+ updateWidth();
166
+ const observer = new ResizeObserver(updateWidth);
167
+ if (containerRef.current) {
168
+ observer.observe(containerRef.current);
169
+ }
170
+
171
+ return () => observer.disconnect();
172
+ }, [containerPadding]);
173
+
174
+ // Calculate adaptive overlap based on container width and card count
175
+ const { cardOffset, totalWidth, useDrawerMode } = useMemo(() => {
176
+ if (layout !== "overlap") {
177
+ return {
178
+ cardOffset: cardDimensions.width,
179
+ totalWidth: 0,
180
+ useDrawerMode: false,
181
+ };
182
+ }
183
+
184
+ if (cardCount === 0) {
185
+ return { cardOffset: 0, totalWidth: 0, useDrawerMode: false };
186
+ }
187
+ if (cardCount === 1) {
188
+ return {
189
+ cardOffset: cardDimensions.width,
190
+ totalWidth: cardDimensions.width,
191
+ useDrawerMode: false,
192
+ };
193
+ }
194
+
195
+ // Available width for overlap distribution
196
+ const availableWidth = containerWidth;
197
+ if (availableWidth <= 0) {
198
+ return {
199
+ cardOffset: cardDimensions.width,
200
+ totalWidth: cardDimensions.width * cardCount,
201
+ useDrawerMode: false,
202
+ };
203
+ }
204
+
205
+ // Calculate the offset needed to fit all cards
206
+ // Total width = cardWidth + (cardCount - 1) * offset
207
+ // So offset = (availableWidth - cardWidth) / (cardCount - 1)
208
+ const idealOffset =
209
+ (availableWidth - cardDimensions.width) / (cardCount - 1);
210
+
211
+ // Clamp offset between minimum visible portion and full card width
212
+ const clampedOffset = Math.max(
213
+ MIN_VISIBLE_PORTION,
214
+ Math.min(cardDimensions.width, idealOffset),
215
+ );
216
+
217
+ // Calculate total width with this offset
218
+ const width = cardDimensions.width + (cardCount - 1) * clampedOffset;
219
+
220
+ // Determine if we should use drawer mode
221
+ // Use drawer if cards are overlapping too much (less than threshold of card visible)
222
+ const visiblePortion = clampedOffset / cardDimensions.width;
223
+ const shouldUseDrawer =
224
+ visiblePortion < DRAWER_THRESHOLD_RATIO && cardCount > 2;
225
+
226
+ return {
227
+ cardOffset: clampedOffset,
228
+ totalWidth: width,
229
+ useDrawerMode: shouldUseDrawer,
230
+ };
231
+ }, [cardCount, containerWidth, layout, cardDimensions.width]);
232
+
233
+ // Calculate hovered card index based on mouse X position
234
+ // This allows hovering on adjacent cards even when one is popped up
235
+ const handleMouseMove = useCallback(
236
+ (e: React.MouseEvent<HTMLDivElement>) => {
237
+ if (
238
+ !cardsContainerRef.current ||
239
+ cardCount === 0 ||
240
+ layout === "spread" ||
241
+ layout === "stack"
242
+ ) {
243
+ return;
244
+ }
245
+
246
+ const rect = cardsContainerRef.current.getBoundingClientRect();
247
+ const mouseX = e.clientX - rect.left;
248
+
249
+ let newHoveredIndex: number | null = null;
250
+
251
+ // Find which card the mouse is over based on X position
252
+ for (let i = cardCount - 1; i >= 0; i--) {
253
+ const cardLeft = i * cardOffset;
254
+ const cardRight =
255
+ i === cardCount - 1
256
+ ? cardLeft + cardDimensions.width
257
+ : (i + 1) * cardOffset;
258
+
259
+ if (mouseX >= cardLeft && mouseX < cardRight) {
260
+ newHoveredIndex = i;
261
+ break;
262
+ }
263
+ }
264
+
265
+ // Check if mouse is in the rightmost card's full area
266
+ if (newHoveredIndex === null && mouseX >= 0 && mouseX < totalWidth) {
267
+ const lastCardLeft = (cardCount - 1) * cardOffset;
268
+ if (mouseX >= lastCardLeft) {
269
+ newHoveredIndex = cardCount - 1;
270
+ }
271
+ }
272
+
273
+ setHoveredIndex(newHoveredIndex);
274
+ },
275
+ [cardCount, cardOffset, totalWidth, layout, cardDimensions.width],
276
+ );
277
+
278
+ const handleMouseLeave = useCallback(() => {
279
+ setHoveredIndex(null);
280
+ }, []);
281
+
282
+ // Calculate z-index: hovered > selected > position
283
+ const getZIndex = useCallback(
284
+ (index: number, isHovered: boolean, isSelected: boolean) => {
285
+ if (isHovered) return 200;
286
+ if (isSelected) return 100 + index;
287
+ return index;
288
+ },
289
+ [],
290
+ );
291
+
292
+ // Get card position props for different layouts
293
+ const getCardPosition = useCallback(
294
+ (
295
+ index: number,
296
+ isHovered: boolean,
297
+ isSelected: boolean,
298
+ ): CardPositionProps => {
299
+ const zIndex = getZIndex(index, isHovered, isSelected);
300
+
301
+ if (layout === "stack") {
302
+ return {
303
+ x: index * 4,
304
+ y: 0,
305
+ zIndex,
306
+ transformOrigin: "bottom center",
307
+ };
308
+ }
309
+
310
+ if (layout === "spread") {
311
+ return {
312
+ x: 0,
313
+ y: 0,
314
+ zIndex,
315
+ transformOrigin: "bottom center",
316
+ };
317
+ }
318
+
319
+ // Overlap layout (default) - simple horizontal overlap with lift on hover/select
320
+ const yOffset = isHovered ? -HOVER_LIFT : isSelected ? -SELECTED_LIFT : 0;
321
+
322
+ return {
323
+ x: index * cardOffset,
324
+ y: yOffset,
325
+ zIndex,
326
+ transformOrigin: "bottom center",
327
+ };
328
+ },
329
+ [layout, cardOffset, getZIndex],
330
+ );
331
+
332
+ return {
333
+ containerRef,
334
+ cardsContainerRef,
335
+ containerWidth,
336
+ cardOffset,
337
+ totalWidth,
338
+ useDrawerMode,
339
+ cardDimensions,
340
+ constants: {
341
+ hoverLift: HOVER_LIFT,
342
+ selectedLift: SELECTED_LIFT,
343
+ },
344
+ hoveredIndex,
345
+ handleMouseMove,
346
+ handleMouseLeave,
347
+ getCardPosition,
348
+ };
349
+ }
@@ -0,0 +1,74 @@
1
+ import { useCallback, useMemo } from "react";
2
+ import type {
3
+ AnyHexBoardInput,
4
+ BoardSpaceIdOf,
5
+ NormalizedHexBoard,
6
+ NormalizedHexTileOf,
7
+ } from "../types/tiled-board.js";
8
+ import { normalizeHexBoardInput } from "../types/tiled-board.js";
9
+ import { useBoardTopology } from "./useBoardTopology.js";
10
+
11
+ export function useHexBoard<const TBoard extends AnyHexBoardInput>(
12
+ board: TBoard,
13
+ ) {
14
+ const normalizedBoard = useMemo<NormalizedHexBoard<TBoard>>(
15
+ () => normalizeHexBoardInput(board),
16
+ [board],
17
+ );
18
+ const topology = useBoardTopology(board);
19
+
20
+ const tileByCoordinate = useMemo(
21
+ () =>
22
+ new Map(
23
+ normalizedBoard.tiles.map(
24
+ (tile) => [`${tile.q},${tile.r}`, tile] as const,
25
+ ),
26
+ ),
27
+ [normalizedBoard.tiles],
28
+ );
29
+
30
+ const getTile = useCallback(
31
+ (tileId: BoardSpaceIdOf<TBoard>) => {
32
+ return topology.getSpace(tileId) as
33
+ | NormalizedHexTileOf<TBoard>
34
+ | undefined;
35
+ },
36
+ [topology],
37
+ );
38
+
39
+ const getTileAt = useCallback(
40
+ (q: number, r: number) => {
41
+ return tileByCoordinate.get(`${q},${r}`) as
42
+ | NormalizedHexTileOf<TBoard>
43
+ | undefined;
44
+ },
45
+ [tileByCoordinate],
46
+ );
47
+
48
+ const getNeighbors = useCallback(
49
+ (tileId: BoardSpaceIdOf<TBoard>) => {
50
+ return topology.getAdjacentSpaces(tileId) as Array<
51
+ NormalizedHexTileOf<TBoard>
52
+ >;
53
+ },
54
+ [topology],
55
+ );
56
+
57
+ const getTilesInRange = useCallback(
58
+ (centerTileId: BoardSpaceIdOf<TBoard>, range: number) => {
59
+ return normalizedBoard.tiles.filter(
60
+ (tile) => topology.getDistance(centerTileId, tile.id) <= range,
61
+ ) as Array<NormalizedHexTileOf<TBoard>>;
62
+ },
63
+ [normalizedBoard.tiles, topology],
64
+ );
65
+
66
+ return {
67
+ ...topology,
68
+ board: normalizedBoard,
69
+ getTile,
70
+ getTileAt,
71
+ getNeighbors,
72
+ getTilesInRange,
73
+ };
74
+ }
@@ -0,0 +1,185 @@
1
+ /**
2
+ * useHexGrid hook - Headless logic for hex grid games
3
+ *
4
+ * Provides utilities for:
5
+ * - Coordinate conversion and neighbor finding
6
+ * - Distance calculations
7
+ * - Tile lookups
8
+ *
9
+ * @example
10
+ * ```tsx
11
+ * const { getNeighbors, getDistance, getTile } = useHexGrid(tiles);
12
+ *
13
+ * // Find all adjacent tiles
14
+ * const neighbors = getNeighbors('center');
15
+ *
16
+ * // Check if two tiles are adjacent
17
+ * const isAdjacent = getDistance('tile1', 'tile2') === 1;
18
+ * ```
19
+ */
20
+
21
+ import { useMemo, useCallback } from "react";
22
+
23
+ // ============================================================================
24
+ // Types
25
+ // ============================================================================
26
+
27
+ export interface HexTileData {
28
+ /** Unique tile identifier */
29
+ id: string;
30
+ /** Axial coordinate Q */
31
+ q: number;
32
+ /** Axial coordinate R */
33
+ r: number;
34
+ /** Tile type */
35
+ type?: string;
36
+ /** Additional data */
37
+ data?: Record<string, unknown>;
38
+ }
39
+
40
+ export interface UseHexGridReturn {
41
+ /** Get a tile by ID */
42
+ getTile: (tileId: string) => HexTileData | undefined;
43
+ /** Get a tile by coordinates */
44
+ getTileAt: (q: number, r: number) => HexTileData | undefined;
45
+ /** Get neighboring tiles */
46
+ getNeighbors: (tileId: string) => HexTileData[];
47
+ /** Get distance between two tiles */
48
+ getDistance: (fromId: string, toId: string) => number;
49
+ /** Get all tiles within a range */
50
+ getHexesInRange: (centerId: string, range: number) => HexTileData[];
51
+ /** Convert axial to cube coordinates */
52
+ axialToCube: (q: number, r: number) => { x: number; y: number; z: number };
53
+ /** Convert cube to axial coordinates */
54
+ cubeToAxial: (x: number, y: number, z: number) => { q: number; r: number };
55
+ }
56
+
57
+ // ============================================================================
58
+ // Axial direction vectors
59
+ // ============================================================================
60
+
61
+ const AXIAL_DIRECTIONS = [
62
+ { q: 1, r: 0 },
63
+ { q: 1, r: -1 },
64
+ { q: 0, r: -1 },
65
+ { q: -1, r: 0 },
66
+ { q: -1, r: 1 },
67
+ { q: 0, r: 1 },
68
+ ];
69
+
70
+ // ============================================================================
71
+ // Hook Implementation
72
+ // ============================================================================
73
+
74
+ export function useHexGrid(tiles: HexTileData[]): UseHexGridReturn {
75
+ // Create lookup maps
76
+ const tileById = useMemo(() => {
77
+ return new Map(tiles.map((t) => [t.id, t]));
78
+ }, [tiles]);
79
+
80
+ const tileByCoord = useMemo(() => {
81
+ return new Map(tiles.map((t) => [`${t.q},${t.r}`, t]));
82
+ }, [tiles]);
83
+
84
+ // Get tile by ID
85
+ const getTile = useCallback(
86
+ (tileId: string): HexTileData | undefined => {
87
+ return tileById.get(tileId);
88
+ },
89
+ [tileById],
90
+ );
91
+
92
+ // Get tile by coordinates
93
+ const getTileAt = useCallback(
94
+ (q: number, r: number): HexTileData | undefined => {
95
+ return tileByCoord.get(`${q},${r}`);
96
+ },
97
+ [tileByCoord],
98
+ );
99
+
100
+ // Get neighboring tiles
101
+ const getNeighbors = useCallback(
102
+ (tileId: string): HexTileData[] => {
103
+ const tile = tileById.get(tileId);
104
+ if (!tile) return [];
105
+
106
+ const neighbors: HexTileData[] = [];
107
+ for (const dir of AXIAL_DIRECTIONS) {
108
+ const neighbor = tileByCoord.get(`${tile.q + dir.q},${tile.r + dir.r}`);
109
+ if (neighbor) {
110
+ neighbors.push(neighbor);
111
+ }
112
+ }
113
+ return neighbors;
114
+ },
115
+ [tileById, tileByCoord],
116
+ );
117
+
118
+ // Calculate distance between two tiles
119
+ const getDistance = useCallback(
120
+ (fromId: string, toId: string): number => {
121
+ const from = tileById.get(fromId);
122
+ const to = tileById.get(toId);
123
+ if (!from || !to) return Infinity;
124
+
125
+ // Hex distance formula using axial coordinates
126
+ return (
127
+ (Math.abs(from.q - to.q) +
128
+ Math.abs(from.q + from.r - to.q - to.r) +
129
+ Math.abs(from.r - to.r)) /
130
+ 2
131
+ );
132
+ },
133
+ [tileById],
134
+ );
135
+
136
+ // Get all tiles within range
137
+ const getHexesInRange = useCallback(
138
+ (centerId: string, range: number): HexTileData[] => {
139
+ const center = tileById.get(centerId);
140
+ if (!center) return [];
141
+
142
+ const results: HexTileData[] = [];
143
+
144
+ for (let dq = -range; dq <= range; dq++) {
145
+ const minR = Math.max(-range, -dq - range);
146
+ const maxR = Math.min(range, -dq + range);
147
+ for (let dr = minR; dr <= maxR; dr++) {
148
+ const tile = tileByCoord.get(`${center.q + dq},${center.r + dr}`);
149
+ if (tile) {
150
+ results.push(tile);
151
+ }
152
+ }
153
+ }
154
+
155
+ return results;
156
+ },
157
+ [tileById, tileByCoord],
158
+ );
159
+
160
+ // Convert axial to cube coordinates
161
+ const axialToCube = useCallback(
162
+ (q: number, r: number): { x: number; y: number; z: number } => {
163
+ return { x: q, z: r, y: -q - r };
164
+ },
165
+ [],
166
+ );
167
+
168
+ // Convert cube to axial coordinates
169
+ const cubeToAxial = useCallback(
170
+ (x: number, _y: number, z: number): { q: number; r: number } => {
171
+ return { q: x, r: z };
172
+ },
173
+ [],
174
+ );
175
+
176
+ return {
177
+ getTile,
178
+ getTileAt,
179
+ getNeighbors,
180
+ getDistance,
181
+ getHexesInRange,
182
+ axialToCube,
183
+ cubeToAxial,
184
+ };
185
+ }