@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,1181 @@
1
+ /**
2
+ * React Cosmos fixtures for HexGrid component
3
+ *
4
+ * Demonstrates various hex grid configurations:
5
+ * - Basic hex grid with different tile types
6
+ * - Catan-style board with edges and vertices
7
+ * - Interactive highlighting and selection
8
+ * - Pan and zoom functionality
9
+ * - Using pre-built helper components
10
+ */
11
+
12
+ import { useState } from "react";
13
+ import {
14
+ HexGrid,
15
+ DefaultHexTile,
16
+ DefaultHexEdge,
17
+ DefaultHexVertex,
18
+ DefaultInteractiveVertex,
19
+ DefaultInteractiveEdge,
20
+ } from "../board/HexGrid.js";
21
+ import type {
22
+ HexEdgeState,
23
+ HexTileState,
24
+ HexVertexState,
25
+ } from "../../types/player-state.js";
26
+
27
+ // ============================================================================
28
+ // Sample Data
29
+ // ============================================================================
30
+
31
+ // Basic hex tiles in a standard Catan-like pattern
32
+ const catanTiles: HexTileState[] = [
33
+ // Center
34
+ { id: "center", q: 0, r: 0, typeId: "desert", label: "7" },
35
+ // Ring 1
36
+ { id: "tile-1", q: 1, r: -1, typeId: "hills", label: "6" },
37
+ { id: "tile-2", q: 1, r: 0, typeId: "forest", label: "3" },
38
+ { id: "tile-3", q: 0, r: 1, typeId: "fields", label: "8" },
39
+ { id: "tile-4", q: -1, r: 1, typeId: "pasture", label: "5" },
40
+ { id: "tile-5", q: -1, r: 0, typeId: "mountains", label: "10" },
41
+ { id: "tile-6", q: 0, r: -1, typeId: "forest", label: "9" },
42
+ // Ring 2
43
+ { id: "tile-7", q: 2, r: -2, typeId: "fields", label: "4" },
44
+ { id: "tile-8", q: 2, r: -1, typeId: "pasture", label: "11" },
45
+ { id: "tile-9", q: 2, r: 0, typeId: "hills", label: "12" },
46
+ { id: "tile-10", q: 1, r: 1, typeId: "forest", label: "2" },
47
+ { id: "tile-11", q: 0, r: 2, typeId: "mountains", label: "6" },
48
+ { id: "tile-12", q: -1, r: 2, typeId: "fields", label: "8" },
49
+ { id: "tile-13", q: -2, r: 2, typeId: "pasture", label: "4" },
50
+ { id: "tile-14", q: -2, r: 1, typeId: "hills", label: "3" },
51
+ { id: "tile-15", q: -2, r: 0, typeId: "forest", label: "10" },
52
+ { id: "tile-16", q: -1, r: -1, typeId: "fields", label: "5" },
53
+ { id: "tile-17", q: 0, r: -2, typeId: "pasture", label: "9" },
54
+ { id: "tile-18", q: 1, r: -2, typeId: "mountains", label: "11" },
55
+ ];
56
+
57
+ // Sample edges (roads)
58
+ const sampleEdges: HexEdgeState[] = [
59
+ { id: "road-1", hex1: "center", hex2: "tile-1", owner: "player1" },
60
+ { id: "road-2", hex1: "tile-1", hex2: "tile-7", owner: "player1" },
61
+ { id: "road-3", hex1: "center", hex2: "tile-4", owner: "player2" },
62
+ { id: "road-4", hex1: "tile-4", hex2: "tile-13", owner: "player2" },
63
+ { id: "road-5", hex1: "center", hex2: "tile-2" },
64
+ { id: "road-6", hex1: "center", hex2: "tile-3" },
65
+ ];
66
+
67
+ // Sample vertices (settlements)
68
+ const sampleVertices: HexVertexState[] = [
69
+ {
70
+ id: "settle-1",
71
+ hexes: ["center", "tile-1", "tile-6"] as const,
72
+ owner: "player1",
73
+ typeId: "settlement",
74
+ },
75
+ {
76
+ id: "settle-2",
77
+ hexes: ["tile-1", "tile-7", "tile-8"] as const,
78
+ owner: "player1",
79
+ typeId: "city",
80
+ },
81
+ {
82
+ id: "settle-3",
83
+ hexes: ["center", "tile-4", "tile-5"] as const,
84
+ owner: "player2",
85
+ typeId: "settlement",
86
+ },
87
+ {
88
+ id: "settle-4",
89
+ hexes: ["tile-4", "tile-13", "tile-14"] as const,
90
+ owner: "player2",
91
+ typeId: "settlement",
92
+ },
93
+ ];
94
+
95
+ // Wargame hex grid (larger)
96
+ const wargameTiles: HexTileState[] = [];
97
+ const wargameTypes = ["plains", "forest", "hills", "mountains", "water"];
98
+ for (let q = -4; q <= 4; q++) {
99
+ for (let r = -4; r <= 4; r++) {
100
+ if (Math.abs(q + r) <= 4) {
101
+ const typeId = wargameTypes[(q + r + 8) % wargameTypes.length];
102
+ wargameTiles.push({
103
+ id: `hex-${q}-${r}`,
104
+ q,
105
+ r,
106
+ typeId,
107
+ });
108
+ }
109
+ }
110
+ }
111
+
112
+ // ============================================================================
113
+ // Color Definitions
114
+ // ============================================================================
115
+
116
+ const catanTileColors: Record<string, string> = {
117
+ desert: "#d4a574",
118
+ hills: "#b45309",
119
+ forest: "#15803d",
120
+ fields: "#ca8a04",
121
+ pasture: "#65a30d",
122
+ mountains: "#57534e",
123
+ ocean: "#0369a1",
124
+ };
125
+
126
+ const wargameTileColors: Record<string, string> = {
127
+ plains: "#84cc16",
128
+ forest: "#15803d",
129
+ hills: "#a16207",
130
+ mountains: "#57534e",
131
+ water: "#0284c7",
132
+ };
133
+
134
+ const playerColors: Record<string, string> = {
135
+ player1: "#ef4444",
136
+ player2: "#3b82f6",
137
+ player3: "#22c55e",
138
+ player4: "#f97316",
139
+ };
140
+
141
+ // ============================================================================
142
+ // Fixtures
143
+ // ============================================================================
144
+
145
+ export default {
146
+ /**
147
+ * Basic Catan Board
148
+ * Standard Catan-style hex board with resource tiles using pre-built components
149
+ */
150
+ "Basic Catan Board": () => {
151
+ const [selectedTile, setSelectedTile] = useState<string | null>(null);
152
+ const [hoveredTile, setHoveredTile] = useState<string | null>(null);
153
+
154
+ return (
155
+ <div className="p-6 bg-slate-900 min-h-screen">
156
+ <h2 className="text-xl font-bold text-white mb-4">Catan-Style Board</h2>
157
+ <p className="text-slate-400 mb-4">
158
+ Click tiles to select, hover to highlight. Uses DefaultHexTile
159
+ component.
160
+ </p>
161
+
162
+ <HexGrid
163
+ tiles={catanTiles}
164
+ edges={sampleEdges}
165
+ vertices={sampleVertices}
166
+ hexSize={55}
167
+ orientation="pointy-top"
168
+ width={700}
169
+ height={600}
170
+ enablePanZoom={false}
171
+ renderTile={(tile) => (
172
+ <DefaultHexTile
173
+ size={55}
174
+ fill={catanTileColors[tile.typeId ?? ""] ?? "#475569"}
175
+ label={tile.label}
176
+ isSelected={selectedTile === tile.id}
177
+ isHighlighted={hoveredTile === tile.id}
178
+ onClick={() => setSelectedTile(tile.id)}
179
+ onPointerEnter={() => setHoveredTile(tile.id)}
180
+ onPointerLeave={() => setHoveredTile(null)}
181
+ />
182
+ )}
183
+ renderEdge={(edge, pos) => (
184
+ <DefaultHexEdge
185
+ position={pos}
186
+ color={playerColors[edge.owner ?? ""] ?? "#94a3b8"}
187
+ hasOwner={!!edge.owner}
188
+ />
189
+ )}
190
+ renderVertex={(vertex, pos) => (
191
+ <DefaultHexVertex
192
+ position={pos}
193
+ color={playerColors[vertex.owner ?? ""] ?? "#94a3b8"}
194
+ hasOwner={!!vertex.owner}
195
+ />
196
+ )}
197
+ />
198
+
199
+ {selectedTile && (
200
+ <div className="mt-4 p-3 bg-slate-800 rounded-lg text-white">
201
+ Selected: {catanTiles.find((t) => t.id === selectedTile)?.typeId} (#
202
+ {catanTiles.find((t) => t.id === selectedTile)?.label})
203
+ </div>
204
+ )}
205
+ </div>
206
+ );
207
+ },
208
+
209
+ /**
210
+ * With Roads and Settlements
211
+ * Full Catan experience with edges (roads) and vertices (settlements)
212
+ */
213
+ "With Roads and Settlements": () => {
214
+ const [selectedEdge, setSelectedEdge] = useState<string | null>(null);
215
+ const [selectedVertex, setSelectedVertex] = useState<string | null>(null);
216
+
217
+ return (
218
+ <div className="p-6 bg-slate-900 min-h-screen">
219
+ <h2 className="text-xl font-bold text-white mb-4">
220
+ Roads & Settlements
221
+ </h2>
222
+ <p className="text-slate-400 mb-4">
223
+ Click edges (roads) or vertices (settlements). Cities shown as
224
+ squares.
225
+ </p>
226
+
227
+ <HexGrid
228
+ tiles={catanTiles}
229
+ edges={sampleEdges}
230
+ vertices={sampleVertices}
231
+ hexSize={55}
232
+ orientation="pointy-top"
233
+ width={700}
234
+ height={600}
235
+ enablePanZoom={false}
236
+ renderTile={(tile) => (
237
+ <DefaultHexTile
238
+ size={55}
239
+ fill={catanTileColors[tile.typeId ?? ""] ?? "#475569"}
240
+ label={tile.label}
241
+ />
242
+ )}
243
+ renderEdge={(edge, pos) => (
244
+ <DefaultHexEdge
245
+ position={pos}
246
+ color={playerColors[edge.owner ?? ""] ?? "#94a3b8"}
247
+ hasOwner={!!edge.owner}
248
+ onClick={() => setSelectedEdge(edge.id)}
249
+ />
250
+ )}
251
+ renderVertex={(vertex, pos) => (
252
+ <DefaultHexVertex
253
+ position={pos}
254
+ color={playerColors[vertex.owner ?? ""] ?? "#94a3b8"}
255
+ hasOwner={!!vertex.owner}
256
+ shape={vertex.typeId === "city" ? "square" : "circle"}
257
+ onClick={() => setSelectedVertex(vertex.id)}
258
+ />
259
+ )}
260
+ />
261
+
262
+ <div className="mt-4 flex gap-4">
263
+ {selectedEdge && (
264
+ <div className="p-3 bg-slate-800 rounded-lg text-white">
265
+ Edge: {selectedEdge}
266
+ </div>
267
+ )}
268
+ {selectedVertex && (
269
+ <div className="p-3 bg-slate-800 rounded-lg text-white">
270
+ Vertex: {selectedVertex}
271
+ </div>
272
+ )}
273
+ </div>
274
+ </div>
275
+ );
276
+ },
277
+
278
+ /**
279
+ * Wargame Grid with Pan & Zoom
280
+ * Large hex grid demonstrating pan and zoom functionality
281
+ */
282
+ "Wargame Grid (Pan & Zoom)": () => {
283
+ const [selectedTile, setSelectedTile] = useState<string | null>(null);
284
+
285
+ return (
286
+ <div className="p-6 bg-slate-900 min-h-screen">
287
+ <h2 className="text-xl font-bold text-white mb-4">Wargame Hex Grid</h2>
288
+ <p className="text-slate-400 mb-4">
289
+ Drag to pan, scroll/pinch to zoom. Click tiles to select.
290
+ </p>
291
+
292
+ <HexGrid
293
+ tiles={wargameTiles}
294
+ edges={[]}
295
+ vertices={[]}
296
+ hexSize={40}
297
+ orientation="flat-top"
298
+ width={800}
299
+ height={500}
300
+ enablePanZoom={true}
301
+ initialZoom={0.8}
302
+ minZoom={0.4}
303
+ maxZoom={2}
304
+ renderTile={(tile) => (
305
+ <DefaultHexTile
306
+ size={40}
307
+ fill={wargameTileColors[tile.typeId ?? ""] ?? "#475569"}
308
+ isSelected={selectedTile === tile.id}
309
+ showCoordinates={true}
310
+ coordinates={{ q: tile.q, r: tile.r }}
311
+ orientation="flat-top"
312
+ onClick={() => setSelectedTile(tile.id)}
313
+ />
314
+ )}
315
+ renderEdge={(edge, pos) => (
316
+ <DefaultHexEdge
317
+ position={pos}
318
+ color="#94a3b8"
319
+ hasOwner={!!edge.owner}
320
+ />
321
+ )}
322
+ renderVertex={(vertex, pos) => (
323
+ <DefaultHexVertex
324
+ position={pos}
325
+ color="#94a3b8"
326
+ hasOwner={!!vertex.owner}
327
+ />
328
+ )}
329
+ />
330
+ </div>
331
+ );
332
+ },
333
+
334
+ /**
335
+ * Flat-Top Orientation
336
+ * Same data but with flat-top hex orientation
337
+ */
338
+ "Flat-Top Orientation": () => (
339
+ <div className="p-6 bg-slate-900 min-h-screen">
340
+ <h2 className="text-xl font-bold text-white mb-4">Flat-Top Hexagons</h2>
341
+ <p className="text-slate-400 mb-4">Alternative hex orientation</p>
342
+
343
+ <HexGrid
344
+ tiles={catanTiles}
345
+ edges={[]}
346
+ vertices={[]}
347
+ hexSize={50}
348
+ orientation="flat-top"
349
+ width={700}
350
+ height={600}
351
+ enablePanZoom={false}
352
+ renderTile={(tile) => (
353
+ <DefaultHexTile
354
+ size={50}
355
+ fill={catanTileColors[tile.typeId ?? ""] ?? "#475569"}
356
+ label={tile.label}
357
+ showCoordinates={true}
358
+ coordinates={{ q: tile.q, r: tile.r }}
359
+ orientation="flat-top"
360
+ />
361
+ )}
362
+ renderEdge={(edge, pos) => (
363
+ <DefaultHexEdge
364
+ position={pos}
365
+ color="#94a3b8"
366
+ hasOwner={!!edge.owner}
367
+ />
368
+ )}
369
+ renderVertex={(vertex, pos) => (
370
+ <DefaultHexVertex
371
+ position={pos}
372
+ color="#94a3b8"
373
+ hasOwner={!!vertex.owner}
374
+ />
375
+ )}
376
+ />
377
+ </div>
378
+ ),
379
+
380
+ /**
381
+ * Interactive Highlighting
382
+ * Shows valid move highlighting for a strategy game
383
+ */
384
+ "Interactive Highlighting": () => {
385
+ const [selectedTile, setSelectedTile] = useState<string>("center");
386
+
387
+ // Find adjacent tiles to highlight as "valid"
388
+ const adjacentIds = new Set(
389
+ catanTiles
390
+ .filter((t) => {
391
+ const selected = catanTiles.find((s) => s.id === selectedTile);
392
+ if (!selected) return false;
393
+ const dist =
394
+ (Math.abs(t.q - selected.q) +
395
+ Math.abs(t.q + t.r - selected.q - selected.r) +
396
+ Math.abs(t.r - selected.r)) /
397
+ 2;
398
+ return dist === 1;
399
+ })
400
+ .map((t) => t.id),
401
+ );
402
+
403
+ return (
404
+ <div className="p-6 bg-slate-900 min-h-screen">
405
+ <h2 className="text-xl font-bold text-white mb-4">
406
+ Movement Highlighting
407
+ </h2>
408
+ <p className="text-slate-400 mb-4">
409
+ Selected tile shows adjacent tiles as valid moves (green)
410
+ </p>
411
+
412
+ <HexGrid
413
+ tiles={catanTiles}
414
+ edges={[]}
415
+ vertices={[]}
416
+ hexSize={55}
417
+ orientation="pointy-top"
418
+ width={700}
419
+ height={600}
420
+ enablePanZoom={false}
421
+ renderTile={(tile) => (
422
+ <DefaultHexTile
423
+ size={55}
424
+ fill={catanTileColors[tile.typeId ?? ""] ?? "#475569"}
425
+ label={tile.label}
426
+ isSelected={selectedTile === tile.id}
427
+ isHighlighted={adjacentIds.has(tile.id)}
428
+ onClick={() => setSelectedTile(tile.id)}
429
+ />
430
+ )}
431
+ renderEdge={(edge, pos) => (
432
+ <DefaultHexEdge
433
+ position={pos}
434
+ color="#94a3b8"
435
+ hasOwner={!!edge.owner}
436
+ />
437
+ )}
438
+ renderVertex={(vertex, pos) => (
439
+ <DefaultHexVertex
440
+ position={pos}
441
+ color="#94a3b8"
442
+ hasOwner={!!vertex.owner}
443
+ />
444
+ )}
445
+ />
446
+ </div>
447
+ );
448
+ },
449
+
450
+ /**
451
+ * Custom Tile Rendering
452
+ * Using fully custom renderTile for emoji icons
453
+ */
454
+ "Custom Tile Rendering": () => {
455
+ const resourceIcons: Record<string, string> = {
456
+ hills: "⛏️",
457
+ forest: "🌲",
458
+ fields: "🌾",
459
+ pasture: "🐑",
460
+ mountains: "⛰️",
461
+ desert: "🏜️",
462
+ };
463
+
464
+ const [selectedTile, setSelectedTile] = useState<string | null>(null);
465
+
466
+ return (
467
+ <div className="p-6 bg-slate-900 min-h-screen">
468
+ <h2 className="text-xl font-bold text-white mb-4">
469
+ Custom Tile Rendering
470
+ </h2>
471
+ <p className="text-slate-400 mb-4">
472
+ Using fully custom renderTile for emoji icons (no DefaultHexTile)
473
+ </p>
474
+
475
+ <HexGrid
476
+ tiles={catanTiles}
477
+ edges={[]}
478
+ vertices={[]}
479
+ hexSize={55}
480
+ orientation="pointy-top"
481
+ width={700}
482
+ height={600}
483
+ enablePanZoom={false}
484
+ renderTile={(tile, geometry) => {
485
+ const isSelected = selectedTile === tile.id;
486
+ return (
487
+ <g
488
+ onClick={() => setSelectedTile(tile.id)}
489
+ className="cursor-pointer"
490
+ >
491
+ <polygon
492
+ points={geometry.points({ inset: 5 })}
493
+ fill={isSelected ? "#3b82f6" : "#1e293b"}
494
+ stroke={isSelected ? "#60a5fa" : "#475569"}
495
+ strokeWidth={isSelected ? 3 : 2}
496
+ />
497
+ <text
498
+ textAnchor="middle"
499
+ dominantBaseline="middle"
500
+ fontSize={28}
501
+ y={-5}
502
+ >
503
+ {resourceIcons[tile.typeId ?? ""] ?? "❓"}
504
+ </text>
505
+ {tile.label && (
506
+ <text
507
+ textAnchor="middle"
508
+ dominantBaseline="middle"
509
+ fill="white"
510
+ fontSize={16}
511
+ fontWeight="bold"
512
+ y={22}
513
+ >
514
+ {tile.label}
515
+ </text>
516
+ )}
517
+ </g>
518
+ );
519
+ }}
520
+ renderEdge={(edge, pos) => (
521
+ <DefaultHexEdge
522
+ position={pos}
523
+ color={playerColors[edge.owner ?? ""] ?? "#94a3b8"}
524
+ hasOwner={!!edge.owner}
525
+ />
526
+ )}
527
+ renderVertex={(vertex, pos) => (
528
+ <DefaultHexVertex
529
+ position={pos}
530
+ color={playerColors[vertex.owner ?? ""] ?? "#94a3b8"}
531
+ hasOwner={!!vertex.owner}
532
+ />
533
+ )}
534
+ />
535
+ </div>
536
+ );
537
+ },
538
+
539
+ /**
540
+ * Responsive Container
541
+ * Using percentage-based sizing for responsive layouts
542
+ */
543
+ "Responsive Container": () => (
544
+ <div className="p-6 bg-slate-900 min-h-screen">
545
+ <h2 className="text-xl font-bold text-white mb-4">Responsive Hex Grid</h2>
546
+ <p className="text-slate-400 mb-4">Grid scales with container width</p>
547
+
548
+ <div className="w-full max-w-4xl mx-auto bg-slate-800 p-4 rounded-lg">
549
+ <HexGrid
550
+ tiles={catanTiles}
551
+ edges={[]}
552
+ vertices={[]}
553
+ hexSize={45}
554
+ orientation="pointy-top"
555
+ width="100%"
556
+ height={500}
557
+ enablePanZoom={true}
558
+ renderTile={(tile) => (
559
+ <DefaultHexTile
560
+ size={45}
561
+ fill={catanTileColors[tile.typeId ?? ""] ?? "#475569"}
562
+ label={tile.label}
563
+ />
564
+ )}
565
+ renderEdge={(edge, pos) => (
566
+ <DefaultHexEdge
567
+ position={pos}
568
+ color="#94a3b8"
569
+ hasOwner={!!edge.owner}
570
+ />
571
+ )}
572
+ renderVertex={(vertex, pos) => (
573
+ <DefaultHexVertex
574
+ position={pos}
575
+ color="#94a3b8"
576
+ hasOwner={!!vertex.owner}
577
+ />
578
+ )}
579
+ />
580
+ </div>
581
+ </div>
582
+ ),
583
+
584
+ /**
585
+ * @use-gesture Gestures Demo
586
+ * Showcases all gesture capabilities
587
+ */
588
+ "Gestures Demo": () => (
589
+ <div className="p-6 bg-slate-900 min-h-screen">
590
+ <h2 className="text-xl font-bold text-white mb-2">
591
+ @use-gesture Powered Gestures
592
+ </h2>
593
+
594
+ <div className="bg-slate-700/50 rounded-lg p-4 mb-4 max-w-md">
595
+ <ul className="text-slate-300 text-sm space-y-1">
596
+ <li>
597
+ 🖱️ <strong>Mouse drag</strong> - Pan the board
598
+ </li>
599
+ <li>
600
+ 📱 <strong>Touch drag</strong> - Pan with one finger
601
+ </li>
602
+ <li>
603
+ 🤌 <strong>Pinch to zoom</strong> - Two-finger zoom on mobile
604
+ </li>
605
+ <li>
606
+ 🖲️ <strong>Mouse wheel</strong> - Scroll to zoom
607
+ </li>
608
+ </ul>
609
+ </div>
610
+
611
+ <div className="bg-slate-800 rounded-lg overflow-hidden border-2 border-dashed border-slate-600 max-w-3xl">
612
+ <HexGrid
613
+ tiles={catanTiles}
614
+ edges={[]}
615
+ vertices={[]}
616
+ hexSize={50}
617
+ orientation="pointy-top"
618
+ width="100%"
619
+ height={450}
620
+ enablePanZoom={true}
621
+ initialZoom={0.9}
622
+ minZoom={0.5}
623
+ maxZoom={2.5}
624
+ renderTile={(tile) => (
625
+ <DefaultHexTile
626
+ size={50}
627
+ fill={catanTileColors[tile.typeId ?? ""] ?? "#475569"}
628
+ label={tile.label}
629
+ />
630
+ )}
631
+ renderEdge={(edge, pos) => (
632
+ <DefaultHexEdge
633
+ position={pos}
634
+ color="#94a3b8"
635
+ hasOwner={!!edge.owner}
636
+ />
637
+ )}
638
+ renderVertex={(vertex, pos) => (
639
+ <DefaultHexVertex
640
+ position={pos}
641
+ color="#94a3b8"
642
+ hasOwner={!!vertex.owner}
643
+ />
644
+ )}
645
+ />
646
+ </div>
647
+
648
+ <p className="text-slate-500 text-xs mt-2">
649
+ Powered by @use-gesture/react
650
+ </p>
651
+ </div>
652
+ ),
653
+
654
+ /**
655
+ * Interactive Vertex Placement
656
+ * Click corners to place settlements - Catan style!
657
+ */
658
+ "Interactive Vertex Placement": () => {
659
+ const [placedVertices, setPlacedVertices] = useState<Set<string>>(
660
+ new Set(),
661
+ );
662
+ const [lastPlacedId, setLastPlacedId] = useState<string | null>(null);
663
+
664
+ const handleVertexClick = (vertexId: string) => {
665
+ setPlacedVertices((prev) => {
666
+ const next = new Set(prev);
667
+ if (next.has(vertexId)) {
668
+ next.delete(vertexId);
669
+ } else {
670
+ next.add(vertexId);
671
+ }
672
+ return next;
673
+ });
674
+ setLastPlacedId(vertexId);
675
+ };
676
+
677
+ return (
678
+ <div className="p-6 bg-slate-900 min-h-screen">
679
+ <h2 className="text-xl font-bold text-white mb-4">
680
+ 🏠 Settlement Placement
681
+ </h2>
682
+ <p className="text-slate-400 mb-4">
683
+ Hover over corners to see placement points. Click to place/remove
684
+ settlements.
685
+ </p>
686
+
687
+ <HexGrid
688
+ tiles={catanTiles}
689
+ edges={sampleEdges}
690
+ vertices={sampleVertices}
691
+ hexSize={55}
692
+ orientation="pointy-top"
693
+ width={700}
694
+ height={600}
695
+ enablePanZoom={false}
696
+ renderTile={(tile) => (
697
+ <DefaultHexTile
698
+ size={55}
699
+ fill={catanTileColors[tile.typeId ?? ""] ?? "#475569"}
700
+ label={tile.label}
701
+ />
702
+ )}
703
+ renderEdge={(edge, pos) => (
704
+ <DefaultHexEdge
705
+ position={pos}
706
+ color="#94a3b8"
707
+ hasOwner={!!edge.owner}
708
+ />
709
+ )}
710
+ renderVertex={(vertex, pos) => (
711
+ <DefaultHexVertex
712
+ position={pos}
713
+ color="#94a3b8"
714
+ hasOwner={!!vertex.owner}
715
+ />
716
+ )}
717
+ interactiveVertices={{
718
+ selectTargetId: handleVertexClick,
719
+ }}
720
+ renderInteractiveVertex={(vertex, position, state) => {
721
+ const isPlaced = placedVertices.has(vertex.id);
722
+ if (isPlaced) {
723
+ return (
724
+ <circle
725
+ cx={position.x}
726
+ cy={position.y}
727
+ r={12}
728
+ fill="#ef4444"
729
+ stroke="#1e293b"
730
+ strokeWidth={2}
731
+ />
732
+ );
733
+ }
734
+ return (
735
+ <DefaultInteractiveVertex
736
+ position={position}
737
+ isHovered={state.isHovered}
738
+ color="rgba(255, 255, 255, 0.15)"
739
+ hoverColor="rgba(34, 197, 94, 0.7)"
740
+ />
741
+ );
742
+ }}
743
+ />
744
+
745
+ <div className="mt-4 flex gap-4 flex-wrap">
746
+ <div className="p-3 bg-slate-800 rounded-lg text-white">
747
+ Settlements placed: {placedVertices.size}
748
+ </div>
749
+ {lastPlacedId && (
750
+ <div className="p-3 bg-slate-800 rounded-lg text-slate-300">
751
+ Last changed: {lastPlacedId}
752
+ </div>
753
+ )}
754
+ </div>
755
+ </div>
756
+ );
757
+ },
758
+
759
+ /**
760
+ * Interactive Edge Placement
761
+ * Click edges to place roads - Catan style!
762
+ */
763
+ "Interactive Edge Placement": () => {
764
+ const [placedEdges, setPlacedEdges] = useState<Set<string>>(new Set());
765
+ const [lastPlacedId, setLastPlacedId] = useState<string | null>(null);
766
+
767
+ const handleEdgeClick = (edgeId: string) => {
768
+ setPlacedEdges((prev) => {
769
+ const next = new Set(prev);
770
+ if (next.has(edgeId)) {
771
+ next.delete(edgeId);
772
+ } else {
773
+ next.add(edgeId);
774
+ }
775
+ return next;
776
+ });
777
+ setLastPlacedId(edgeId);
778
+ };
779
+
780
+ return (
781
+ <div className="p-6 bg-slate-900 min-h-screen">
782
+ <h2 className="text-xl font-bold text-white mb-4">🛤️ Road Placement</h2>
783
+ <p className="text-slate-400 mb-4">
784
+ Hover over edges to see placement areas. Click to place/remove roads.
785
+ </p>
786
+
787
+ <HexGrid
788
+ tiles={catanTiles}
789
+ edges={sampleEdges}
790
+ vertices={sampleVertices}
791
+ hexSize={55}
792
+ orientation="pointy-top"
793
+ width={700}
794
+ height={600}
795
+ enablePanZoom={false}
796
+ renderTile={(tile) => (
797
+ <DefaultHexTile
798
+ size={55}
799
+ fill={catanTileColors[tile.typeId ?? ""] ?? "#475569"}
800
+ label={tile.label}
801
+ />
802
+ )}
803
+ renderEdge={(edge, pos) => (
804
+ <DefaultHexEdge
805
+ position={pos}
806
+ color="#94a3b8"
807
+ hasOwner={!!edge.owner}
808
+ />
809
+ )}
810
+ renderVertex={(vertex, pos) => (
811
+ <DefaultHexVertex
812
+ position={pos}
813
+ color="#94a3b8"
814
+ hasOwner={!!vertex.owner}
815
+ />
816
+ )}
817
+ interactiveEdges={{
818
+ selectTargetId: handleEdgeClick,
819
+ }}
820
+ renderInteractiveEdge={(edge, position, state) => {
821
+ const isPlaced = placedEdges.has(edge.id);
822
+ if (isPlaced) {
823
+ return (
824
+ <line
825
+ x1={position.x1}
826
+ y1={position.y1}
827
+ x2={position.x2}
828
+ y2={position.y2}
829
+ stroke="#3b82f6"
830
+ strokeWidth={8}
831
+ strokeLinecap="round"
832
+ />
833
+ );
834
+ }
835
+ return (
836
+ <DefaultInteractiveEdge
837
+ position={position}
838
+ isHovered={state.isHovered}
839
+ color="rgba(255, 255, 255, 0.1)"
840
+ hoverColor="rgba(251, 146, 60, 0.7)"
841
+ />
842
+ );
843
+ }}
844
+ />
845
+
846
+ <div className="mt-4 flex gap-4 flex-wrap">
847
+ <div className="p-3 bg-slate-800 rounded-lg text-white">
848
+ Roads placed: {placedEdges.size}
849
+ </div>
850
+ {lastPlacedId && (
851
+ <div className="p-3 bg-slate-800 rounded-lg text-slate-300">
852
+ Last changed: {lastPlacedId}
853
+ </div>
854
+ )}
855
+ </div>
856
+ </div>
857
+ );
858
+ },
859
+
860
+ /**
861
+ * Combined Placement Mode
862
+ * Toggle between settlement and road placement - full Catan experience!
863
+ */
864
+ "Catan Placement Mode": () => {
865
+ type PlacementMode = "settlement" | "road" | null;
866
+ const [placementMode, setPlacementMode] = useState<PlacementMode>(null);
867
+ const [placedVertices, setPlacedVertices] = useState<
868
+ Map<string, { player: string }>
869
+ >(new Map());
870
+ const [placedEdges, setPlacedEdges] = useState<
871
+ Map<string, { player: string }>
872
+ >(new Map());
873
+ const [currentPlayer, setCurrentPlayer] = useState<string>("player1");
874
+
875
+ const handleVertexClick = (vertexId: string) => {
876
+ if (placementMode !== "settlement") return;
877
+ setPlacedVertices((prev) => {
878
+ const next = new Map(prev);
879
+ if (next.has(vertexId)) {
880
+ next.delete(vertexId);
881
+ } else {
882
+ next.set(vertexId, { player: currentPlayer });
883
+ }
884
+ return next;
885
+ });
886
+ };
887
+
888
+ const handleEdgeClick = (edgeId: string) => {
889
+ if (placementMode !== "road") return;
890
+ setPlacedEdges((prev) => {
891
+ const next = new Map(prev);
892
+ if (next.has(edgeId)) {
893
+ next.delete(edgeId);
894
+ } else {
895
+ next.set(edgeId, { player: currentPlayer });
896
+ }
897
+ return next;
898
+ });
899
+ };
900
+
901
+ return (
902
+ <div className="p-6 bg-slate-900 min-h-screen">
903
+ <h2 className="text-xl font-bold text-white mb-4">
904
+ 🎮 Catan Placement Mode
905
+ </h2>
906
+
907
+ {/* Controls */}
908
+ <div className="flex gap-4 mb-4 flex-wrap">
909
+ <div className="flex gap-2">
910
+ <button
911
+ onClick={() =>
912
+ setPlacementMode(
913
+ placementMode === "settlement" ? null : "settlement",
914
+ )
915
+ }
916
+ className={`px-4 py-2 rounded-lg font-medium transition-colors ${
917
+ placementMode === "settlement"
918
+ ? "bg-green-600 text-white"
919
+ : "bg-slate-700 text-slate-300 hover:bg-slate-600"
920
+ }`}
921
+ >
922
+ 🏠 Place Settlement
923
+ </button>
924
+ <button
925
+ onClick={() =>
926
+ setPlacementMode(placementMode === "road" ? null : "road")
927
+ }
928
+ className={`px-4 py-2 rounded-lg font-medium transition-colors ${
929
+ placementMode === "road"
930
+ ? "bg-orange-600 text-white"
931
+ : "bg-slate-700 text-slate-300 hover:bg-slate-600"
932
+ }`}
933
+ >
934
+ 🛤️ Place Road
935
+ </button>
936
+ </div>
937
+
938
+ <div className="flex gap-2 items-center">
939
+ <span className="text-slate-400">Player:</span>
940
+ {(["player1", "player2", "player3", "player4"] as const).map(
941
+ (player) => (
942
+ <button
943
+ key={player}
944
+ onClick={() => setCurrentPlayer(player)}
945
+ className={`w-8 h-8 rounded-full border-2 transition-transform ${
946
+ currentPlayer === player
947
+ ? "scale-125 border-white"
948
+ : "border-transparent hover:scale-110"
949
+ }`}
950
+ style={{ backgroundColor: playerColors[player] }}
951
+ />
952
+ ),
953
+ )}
954
+ </div>
955
+ </div>
956
+
957
+ <HexGrid
958
+ tiles={catanTiles}
959
+ edges={[]}
960
+ vertices={[]}
961
+ hexSize={55}
962
+ orientation="pointy-top"
963
+ width={700}
964
+ height={600}
965
+ enablePanZoom={false}
966
+ renderTile={(tile) => (
967
+ <DefaultHexTile
968
+ size={55}
969
+ fill={catanTileColors[tile.typeId ?? ""] ?? "#475569"}
970
+ label={tile.label}
971
+ />
972
+ )}
973
+ renderEdge={(edge, pos) => (
974
+ <DefaultHexEdge
975
+ position={pos}
976
+ color="#94a3b8"
977
+ hasOwner={!!edge.owner}
978
+ />
979
+ )}
980
+ renderVertex={(vertex, pos) => (
981
+ <DefaultHexVertex
982
+ position={pos}
983
+ color="#94a3b8"
984
+ hasOwner={!!vertex.owner}
985
+ />
986
+ )}
987
+ interactiveVertices={{
988
+ enabled: placementMode === "settlement",
989
+ selectTargetId: handleVertexClick,
990
+ }}
991
+ interactiveEdges={{
992
+ enabled: placementMode === "road",
993
+ selectTargetId: handleEdgeClick,
994
+ }}
995
+ renderInteractiveVertex={(vertex, position, state) => {
996
+ if (!state.isEnabled) return null;
997
+ const placed = placedVertices.get(vertex.id);
998
+ if (placed) {
999
+ return (
1000
+ <circle
1001
+ cx={position.x}
1002
+ cy={position.y}
1003
+ r={12}
1004
+ fill={playerColors[placed.player]}
1005
+ stroke="#1e293b"
1006
+ strokeWidth={2}
1007
+ />
1008
+ );
1009
+ }
1010
+ return (
1011
+ <DefaultInteractiveVertex
1012
+ position={position}
1013
+ isHovered={state.isHovered}
1014
+ hoverColor={playerColors[currentPlayer]}
1015
+ />
1016
+ );
1017
+ }}
1018
+ renderInteractiveEdge={(edge, position, state) => {
1019
+ if (!state.isEnabled) return null;
1020
+ const placed = placedEdges.get(edge.id);
1021
+ if (placed) {
1022
+ return (
1023
+ <line
1024
+ x1={position.x1}
1025
+ y1={position.y1}
1026
+ x2={position.x2}
1027
+ y2={position.y2}
1028
+ stroke={playerColors[placed.player]}
1029
+ strokeWidth={8}
1030
+ strokeLinecap="round"
1031
+ />
1032
+ );
1033
+ }
1034
+ return (
1035
+ <DefaultInteractiveEdge
1036
+ position={position}
1037
+ isHovered={state.isHovered}
1038
+ hoverColor={playerColors[currentPlayer]}
1039
+ />
1040
+ );
1041
+ }}
1042
+ />
1043
+
1044
+ <div className="mt-4 grid grid-cols-2 gap-4 max-w-md">
1045
+ <div className="p-3 bg-slate-800 rounded-lg text-white">
1046
+ <div className="text-sm text-slate-400 mb-1">Settlements</div>
1047
+ <div className="text-2xl font-bold">{placedVertices.size}</div>
1048
+ </div>
1049
+ <div className="p-3 bg-slate-800 rounded-lg text-white">
1050
+ <div className="text-sm text-slate-400 mb-1">Roads</div>
1051
+ <div className="text-2xl font-bold">{placedEdges.size}</div>
1052
+ </div>
1053
+ </div>
1054
+
1055
+ {placementMode && (
1056
+ <p className="mt-4 text-slate-400 text-sm">
1057
+ 💡 Click on {placementMode === "settlement" ? "corners" : "edges"}{" "}
1058
+ to place. Click again to remove.
1059
+ </p>
1060
+ )}
1061
+ </div>
1062
+ );
1063
+ },
1064
+
1065
+ /**
1066
+ * Interactive with Custom Styling
1067
+ * Demonstrates custom renderers for interactive elements
1068
+ */
1069
+ "Custom Interactive Styling": () => (
1070
+ <div className="p-6 bg-slate-900 min-h-screen">
1071
+ <h2 className="text-xl font-bold text-white mb-4">
1072
+ ✨ Custom Interactive Styling
1073
+ </h2>
1074
+ <p className="text-slate-400 mb-4">
1075
+ Eligible vertices and edges visible with custom purple/cyan theme.
1076
+ </p>
1077
+
1078
+ <HexGrid
1079
+ tiles={catanTiles}
1080
+ edges={sampleEdges}
1081
+ vertices={sampleVertices}
1082
+ hexSize={55}
1083
+ orientation="pointy-top"
1084
+ width={700}
1085
+ height={600}
1086
+ enablePanZoom={false}
1087
+ renderTile={(tile) => (
1088
+ <DefaultHexTile
1089
+ size={55}
1090
+ fill={catanTileColors[tile.typeId ?? ""] ?? "#475569"}
1091
+ label={tile.label}
1092
+ />
1093
+ )}
1094
+ renderEdge={(edge, pos) => (
1095
+ <DefaultHexEdge
1096
+ position={pos}
1097
+ color="#94a3b8"
1098
+ hasOwner={!!edge.owner}
1099
+ />
1100
+ )}
1101
+ renderVertex={(vertex, pos) => (
1102
+ <DefaultHexVertex
1103
+ position={pos}
1104
+ color="#94a3b8"
1105
+ hasOwner={!!vertex.owner}
1106
+ />
1107
+ )}
1108
+ interactiveVertices={{
1109
+ eligible: new Set(["settle-1", "settle-2"]),
1110
+ selectTargetId: () => undefined,
1111
+ }}
1112
+ interactiveEdges={{
1113
+ eligible: new Set(["road-5", "road-6"]),
1114
+ selectTargetId: () => undefined,
1115
+ }}
1116
+ interactiveVertexSize={10}
1117
+ interactiveEdgeSize={8}
1118
+ renderInteractiveVertex={(vertex, position, state) =>
1119
+ state.isEligible ? (
1120
+ <g>
1121
+ <circle
1122
+ cx={position.x}
1123
+ cy={position.y}
1124
+ r={state.isHovered ? 10 : 6}
1125
+ fill={state.isHovered ? "#a855f7" : "rgba(168, 85, 247, 0.3)"}
1126
+ stroke={state.isHovered ? "#c084fc" : "rgba(168, 85, 247, 0.5)"}
1127
+ strokeWidth={state.isHovered ? 2 : 1}
1128
+ className="transition-all duration-150"
1129
+ />
1130
+ {state.isHovered && (
1131
+ <circle
1132
+ cx={position.x}
1133
+ cy={position.y}
1134
+ r={16}
1135
+ fill="none"
1136
+ stroke="#c084fc"
1137
+ strokeWidth={1}
1138
+ strokeDasharray="3 3"
1139
+ className="animate-spin"
1140
+ style={{ animationDuration: "3s" }}
1141
+ />
1142
+ )}
1143
+ </g>
1144
+ ) : null
1145
+ }
1146
+ renderInteractiveEdge={(edge, position, state) =>
1147
+ state.isEligible ? (
1148
+ <g>
1149
+ <line
1150
+ x1={position.x1}
1151
+ y1={position.y1}
1152
+ x2={position.x2}
1153
+ y2={position.y2}
1154
+ stroke={state.isHovered ? "#06b6d4" : "rgba(6, 182, 212, 0.25)"}
1155
+ strokeWidth={state.isHovered ? 6 : 3}
1156
+ strokeLinecap="round"
1157
+ className="transition-all duration-150"
1158
+ />
1159
+ {state.isHovered && (
1160
+ <>
1161
+ <circle
1162
+ cx={position.x1}
1163
+ cy={position.y1}
1164
+ r={4}
1165
+ fill="#06b6d4"
1166
+ />
1167
+ <circle
1168
+ cx={position.x2}
1169
+ cy={position.y2}
1170
+ r={4}
1171
+ fill="#06b6d4"
1172
+ />
1173
+ </>
1174
+ )}
1175
+ </g>
1176
+ ) : null
1177
+ }
1178
+ />
1179
+ </div>
1180
+ ),
1181
+ };