@dreamboard-games/sdk 0.2.1-alpha.0 → 0.3.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (363) hide show
  1. package/dist/{HandView-ncJIVLhN.d.ts → HandView-PYDmehVD.d.ts} +1 -1
  2. package/dist/{ResourceCounter-CTREyF73.d.ts → ResourceCounter-BTXklCEZ.d.ts} +1 -1
  3. package/dist/{ThemeProvider-fy0_QzgO.d.ts → ThemeProvider-DvMl17Ny.d.ts} +1 -1
  4. package/dist/attributes-BeRyboMS.d.ts +279 -0
  5. package/dist/browser-interaction.d.ts +708 -0
  6. package/dist/browser-interaction.js +106 -0
  7. package/dist/{chunk-KAELH4KC.js → chunk-3RQEICD3.js} +2 -2
  8. package/dist/{chunk-TDSWKVZ4.js → chunk-ADYH6PVT.js} +2457 -1265
  9. package/dist/chunk-ADYH6PVT.js.map +1 -0
  10. package/dist/{chunk-YFY3RL52.js → chunk-P7VMTJ5D.js} +2 -2
  11. package/dist/{chunk-YFY3RL52.js.map → chunk-P7VMTJ5D.js.map} +1 -1
  12. package/dist/chunk-QLG6VEMW.js +1691 -0
  13. package/dist/chunk-QLG6VEMW.js.map +1 -0
  14. package/dist/{chunk-WYPQ3GG5.js → chunk-QZ6X4B5P.js} +1177 -1106
  15. package/dist/chunk-QZ6X4B5P.js.map +1 -0
  16. package/dist/{chunk-GKKBPPSW.js → chunk-TXXLS3OI.js} +405 -309
  17. package/dist/chunk-TXXLS3OI.js.map +1 -0
  18. package/dist/{chunk-FDNZTDD6.js → chunk-W6SGFWXH.js} +48 -44
  19. package/dist/chunk-W6SGFWXH.js.map +1 -0
  20. package/dist/{chunk-MKXPVOUT.js → chunk-Y2AFYBMB.js} +48 -4
  21. package/dist/chunk-Y2AFYBMB.js.map +1 -0
  22. package/dist/{index-BwqPQtBu.d.ts → codegen.d.ts} +57 -565
  23. package/dist/{infrastructure/workspace-codegen.js → codegen.js} +3 -3
  24. package/dist/{components-D5ZRE2Hl.d.ts → components-DaIqi9QM.d.ts} +48 -37
  25. package/dist/{hex-board-view-D_07hO6O.d.ts → hex-board-view-UCUjMALd.d.ts} +3 -2
  26. package/dist/{infrastructure/reducer-bundle-abi.d.ts → index.d-BGqVifr_.d.ts} +289 -10
  27. package/dist/index.js +1 -1
  28. package/dist/package-set.d.ts +2 -2
  29. package/dist/package-set.js +1 -1
  30. package/dist/primitive-props-BNHDkgd7.d.ts +16 -0
  31. package/dist/reducer-contract.d.ts +3 -0
  32. package/dist/{infrastructure/reducer-bundle-abi.js → reducer-contract.js} +4 -4
  33. package/dist/reducer.d.ts +185 -179
  34. package/dist/reducer.js +1637 -1353
  35. package/dist/reducer.js.map +1 -1
  36. package/dist/runtime/primitives.d.ts +10 -9
  37. package/dist/runtime/primitives.js +8 -7
  38. package/dist/runtime/runtime-api.d.ts +2 -0
  39. package/dist/runtime/runtime-api.js +1 -0
  40. package/dist/runtime/workspace-contract.d.ts +63 -17
  41. package/dist/runtime/workspace-contract.js +5 -5
  42. package/dist/{runtime-api-DWxvTr-O.d.ts → runtime-api-tGL3ArsB.d.ts} +109 -4
  43. package/dist/runtime.d.ts +12 -57
  44. package/dist/runtime.js +487 -7
  45. package/dist/runtime.js.map +1 -1
  46. package/dist/testing.d.ts +4 -4
  47. package/dist/testing.js.map +1 -1
  48. package/dist/types.d.ts +1531 -85
  49. package/dist/types.js +1 -3
  50. package/dist/types.js.map +1 -1
  51. package/dist/ui/components.d.ts +7 -8
  52. package/dist/ui/components.js +1 -1
  53. package/dist/ui/defaults.d.ts +1 -1
  54. package/dist/ui/{types/player-state.d.ts → player-state.d.ts} +2 -2
  55. package/dist/{ui-contract-iQfTtUSL.d.ts → ui-contract-SctM4ibF.d.ts} +157 -150
  56. package/dist/ui.d.ts +10 -11
  57. package/dist/ui.js +2 -2
  58. package/package.json +35 -74
  59. package/dist/bundle-CDd5FKeD.d.ts +0 -283
  60. package/dist/cards-Sl3b40Mv.d.ts +0 -13
  61. package/dist/chunk-7YAHLYBR.js +0 -481
  62. package/dist/chunk-7YAHLYBR.js.map +0 -1
  63. package/dist/chunk-FDNZTDD6.js.map +0 -1
  64. package/dist/chunk-GKKBPPSW.js.map +0 -1
  65. package/dist/chunk-I46YJSOD.js +0 -1
  66. package/dist/chunk-MKXPVOUT.js.map +0 -1
  67. package/dist/chunk-T52J5RMF.js +0 -1
  68. package/dist/chunk-TDSWKVZ4.js.map +0 -1
  69. package/dist/chunk-U5C6BONG.js +0 -34
  70. package/dist/chunk-U5C6BONG.js.map +0 -1
  71. package/dist/chunk-WYPQ3GG5.js.map +0 -1
  72. package/dist/generated/runtime/primitives.d.ts +0 -12
  73. package/dist/generated/runtime/primitives.js +0 -180
  74. package/dist/generated/runtime/primitives.js.map +0 -1
  75. package/dist/generated/runtime-api.d.ts +0 -3
  76. package/dist/generated/runtime-api.js +0 -2
  77. package/dist/generated/runtime.d.ts +0 -14
  78. package/dist/generated/runtime.js +0 -18
  79. package/dist/generated/runtime.js.map +0 -1
  80. package/dist/generated/workspace-contract.d.ts +0 -14
  81. package/dist/generated/workspace-contract.js +0 -14
  82. package/dist/generated/workspace-contract.js.map +0 -1
  83. package/dist/infrastructure/reducer-bundle-abi.js.map +0 -1
  84. package/dist/infrastructure/workspace-codegen.d.ts +0 -53
  85. package/dist/infrastructure/workspace-codegen.js.map +0 -1
  86. package/dist/manifest-contract-BNHVGFtU.d.ts +0 -9
  87. package/dist/primitive-props-DpKs-GCr.d.ts +0 -11
  88. package/dist/runtime/types/runtime-api.d.ts +0 -1
  89. package/dist/runtime/types/runtime-api.js +0 -2
  90. package/dist/runtime-api-3dshj6kK.d.ts +0 -101
  91. package/dist/slots-1GPGihk8.d.ts +0 -8
  92. package/src/generated/reducer-contract/builders.ts +0 -90
  93. package/src/generated/reducer-contract/version.ts +0 -9
  94. package/src/generated/reducer-contract/wire.ts +0 -100
  95. package/src/generated/reducer-contract/zod.ts +0 -103
  96. package/src/generated/runtime/primitives.ts +0 -2
  97. package/src/generated/runtime-api.ts +0 -5
  98. package/src/generated/runtime.ts +0 -35
  99. package/src/generated/workspace-contract.ts +0 -2
  100. package/src/index.ts +0 -7
  101. package/src/infrastructure/reducer-bundle-abi.ts +0 -8
  102. package/src/infrastructure/reducer-contract/bundle.ts +0 -37
  103. package/src/infrastructure/workspace-codegen/hex-geometry.ts +0 -69
  104. package/src/infrastructure/workspace-codegen/index.ts +0 -64
  105. package/src/infrastructure/workspace-codegen/manifest-contract.ts +0 -6632
  106. package/src/infrastructure/workspace-codegen/manifest-validation.ts +0 -795
  107. package/src/infrastructure/workspace-codegen/ownership.ts +0 -131
  108. package/src/infrastructure/workspace-codegen/preset-card-sets.ts +0 -169
  109. package/src/infrastructure/workspace-codegen/seeds.ts +0 -1705
  110. package/src/infrastructure/workspace-codegen.ts +0 -1
  111. package/src/package-set.ts +0 -19
  112. package/src/reducer/authoring/contract.ts +0 -157
  113. package/src/reducer/authoring/effect.ts +0 -224
  114. package/src/reducer/authoring/game.ts +0 -23
  115. package/src/reducer/authoring/interaction.ts +0 -98
  116. package/src/reducer/authoring/phase.ts +0 -300
  117. package/src/reducer/authoring/types.ts +0 -70
  118. package/src/reducer/authoring/validation.ts +0 -382
  119. package/src/reducer/authoring/view-stage.ts +0 -68
  120. package/src/reducer/authoring.ts +0 -29
  121. package/src/reducer/bundle/ingress-bundle.ts +0 -491
  122. package/src/reducer/bundle/trusted/engine-instruction-resolver.ts +0 -254
  123. package/src/reducer/bundle/trusted/flow-instruction-resolver.ts +0 -73
  124. package/src/reducer/bundle/trusted/instruction-runner.ts +0 -414
  125. package/src/reducer/bundle/trusted/interaction-authorization.ts +0 -137
  126. package/src/reducer/bundle/trusted/interaction-collectors.ts +0 -859
  127. package/src/reducer/bundle/trusted/interaction-decision.ts +0 -747
  128. package/src/reducer/bundle/trusted/interaction-resolver.ts +0 -95
  129. package/src/reducer/bundle/trusted/interaction-types.ts +0 -171
  130. package/src/reducer/bundle/trusted/lifecycle-runner.ts +0 -427
  131. package/src/reducer/bundle/trusted/projection-builder.ts +0 -356
  132. package/src/reducer/bundle/trusted/projection-context.ts +0 -39
  133. package/src/reducer/bundle/trusted/rng-sampler.ts +0 -150
  134. package/src/reducer/bundle/trusted/runtime-registry.ts +0 -120
  135. package/src/reducer/bundle/trusted/runtime-scope.ts +0 -336
  136. package/src/reducer/bundle/trusted/simultaneous-player.ts +0 -97
  137. package/src/reducer/bundle/trusted/stage-resolver.ts +0 -87
  138. package/src/reducer/bundle/trusted/static-projection.ts +0 -116
  139. package/src/reducer/bundle/trusted/trusted-runtime-args.ts +0 -97
  140. package/src/reducer/bundle/trusted/trusted-runtime-result.ts +0 -39
  141. package/src/reducer/bundle/trusted/trusted-setup-profiles.ts +0 -43
  142. package/src/reducer/bundle/trusted/trusted-state-codec.ts +0 -48
  143. package/src/reducer/bundle/trusted-bundle.ts +0 -97
  144. package/src/reducer/bundle/types.ts +0 -171
  145. package/src/reducer/bundle.ts +0 -2
  146. package/src/reducer/client-param-schemas.ts +0 -57
  147. package/src/reducer/compose.ts +0 -34
  148. package/src/reducer/core/runtime-input.ts +0 -30
  149. package/src/reducer/core/runtime-instruction.ts +0 -59
  150. package/src/reducer/core/types.ts +0 -62
  151. package/src/reducer/definition-index.ts +0 -277
  152. package/src/reducer/derived.ts +0 -106
  153. package/src/reducer/effects.ts +0 -92
  154. package/src/reducer/engine/runtime-instruction-engine.ts +0 -155
  155. package/src/reducer/ingress/decode-runtime-input.ts +0 -7
  156. package/src/reducer/ingress/decode-session-state.ts +0 -9
  157. package/src/reducer/ingress/encode-session-state.ts +0 -6
  158. package/src/reducer/ingress/input-codec.ts +0 -18
  159. package/src/reducer/ingress/phase-schemas.ts +0 -62
  160. package/src/reducer/ingress/raw-types.ts +0 -107
  161. package/src/reducer/ingress/runtime-codec.ts +0 -14
  162. package/src/reducer/ingress/runtime-payload.ts +0 -13
  163. package/src/reducer/ingress/session-codec.ts +0 -392
  164. package/src/reducer/ingress/types.ts +0 -6
  165. package/src/reducer/inputs/boardInput.ts +0 -217
  166. package/src/reducer/inputs/boardTarget.ts +0 -190
  167. package/src/reducer/inputs/cardInput.ts +0 -86
  168. package/src/reducer/inputs/cardTarget.ts +0 -101
  169. package/src/reducer/inputs/choiceTarget.ts +0 -104
  170. package/src/reducer/inputs/defineInputs.ts +0 -71
  171. package/src/reducer/inputs/formInput.ts +0 -809
  172. package/src/reducer/inputs/many.ts +0 -120
  173. package/src/reducer/inputs/promptInput.ts +0 -87
  174. package/src/reducer/inputs/rngInput.ts +0 -58
  175. package/src/reducer/inputs/targetRule.ts +0 -123
  176. package/src/reducer/inputs.ts +0 -41
  177. package/src/reducer/model/definition.ts +0 -1072
  178. package/src/reducer/model/extract.ts +0 -745
  179. package/src/reducer/model/manifest.ts +0 -570
  180. package/src/reducer/model/queries.ts +0 -641
  181. package/src/reducer/model/runtime.ts +0 -264
  182. package/src/reducer/model/spec.ts +0 -1386
  183. package/src/reducer/model/table.ts +0 -260
  184. package/src/reducer/model.ts +0 -7
  185. package/src/reducer/ops.ts +0 -1034
  186. package/src/reducer/parse-utils.ts +0 -28
  187. package/src/reducer/per-player.ts +0 -422
  188. package/src/reducer/rng.ts +0 -69
  189. package/src/reducer/schema-helpers.ts +0 -185
  190. package/src/reducer/setup-bootstrap-helpers.ts +0 -171
  191. package/src/reducer/setup-bootstrap.ts +0 -481
  192. package/src/reducer/table-ops.ts +0 -2671
  193. package/src/reducer/table-queries.ts +0 -372
  194. package/src/reducer/transaction.ts +0 -120
  195. package/src/reducer.ts +0 -314
  196. package/src/runtime/primitives.ts +0 -1
  197. package/src/runtime/types/runtime-api.ts +0 -1
  198. package/src/runtime/workspace-contract.ts +0 -32
  199. package/src/runtime-internal/components/InteractionForm.tsx +0 -1309
  200. package/src/runtime-internal/components/PluginRuntime.tsx +0 -103
  201. package/src/runtime-internal/components/board/target-layer.ts +0 -70
  202. package/src/runtime-internal/context/ClientParamSchemaContext.tsx +0 -44
  203. package/src/runtime-internal/context/InteractionDraftContext.tsx +0 -279
  204. package/src/runtime-internal/context/PluginSessionContext.tsx +0 -47
  205. package/src/runtime-internal/context/PluginStateContext.tsx +0 -262
  206. package/src/runtime-internal/context/RuntimeContext.tsx +0 -96
  207. package/src/runtime-internal/defaults/components.tsx +0 -409
  208. package/src/runtime-internal/defaults/index.ts +0 -11
  209. package/src/runtime-internal/errors/ValidationError.ts +0 -29
  210. package/src/runtime-internal/hooks/useActivePlayers.ts +0 -33
  211. package/src/runtime-internal/hooks/useBoardInteractions.ts +0 -665
  212. package/src/runtime-internal/hooks/useGameSelector.ts +0 -105
  213. package/src/runtime-internal/hooks/useGameView.ts +0 -9
  214. package/src/runtime-internal/hooks/useInteractionByKey.ts +0 -354
  215. package/src/runtime-internal/hooks/useInteractionHandle.ts +0 -438
  216. package/src/runtime-internal/hooks/useIsMyTurn.ts +0 -20
  217. package/src/runtime-internal/hooks/useLobby.ts +0 -76
  218. package/src/runtime-internal/hooks/useMe.ts +0 -48
  219. package/src/runtime-internal/hooks/usePlayerInfo.ts +0 -28
  220. package/src/runtime-internal/hooks/usePlayerTurnOrder.ts +0 -23
  221. package/src/runtime-internal/hooks/usePluginRuntime.ts +0 -147
  222. package/src/runtime-internal/hooks/useSeatInbox.ts +0 -61
  223. package/src/runtime-internal/hooks/useSimultaneousPhase.ts +0 -10
  224. package/src/runtime-internal/index.ts +0 -42
  225. package/src/runtime-internal/internal.ts +0 -43
  226. package/src/runtime-internal/plugin-styles.css +0 -250
  227. package/src/runtime-internal/primitives/board.tsx +0 -459
  228. package/src/runtime-internal/primitives/dialog-lifecycle.ts +0 -58
  229. package/src/runtime-internal/primitives/dice.tsx +0 -79
  230. package/src/runtime-internal/primitives/game-ui-provider.tsx +0 -35
  231. package/src/runtime-internal/primitives/game.tsx +0 -387
  232. package/src/runtime-internal/primitives/hand-intent-adapter.ts +0 -147
  233. package/src/runtime-internal/primitives/hand-surface.tsx +0 -594
  234. package/src/runtime-internal/primitives/index.ts +0 -196
  235. package/src/runtime-internal/primitives/interaction-form-binding.tsx +0 -56
  236. package/src/runtime-internal/primitives/interaction-submit.ts +0 -90
  237. package/src/runtime-internal/primitives/interaction.tsx +0 -987
  238. package/src/runtime-internal/primitives/phase.tsx +0 -43
  239. package/src/runtime-internal/primitives/player-roster.tsx +0 -302
  240. package/src/runtime-internal/primitives/primitive-props.tsx +0 -101
  241. package/src/runtime-internal/primitives/prompt.tsx +0 -255
  242. package/src/runtime-internal/primitives/ui.tsx +0 -60
  243. package/src/runtime-internal/primitives/zone.tsx +0 -791
  244. package/src/runtime-internal/reducer.ts +0 -30
  245. package/src/runtime-internal/runtime/createPluginRuntimeAPI.ts +0 -605
  246. package/src/runtime-internal/types/plugin-state.ts +0 -508
  247. package/src/runtime-internal/types/reducer-state.ts +0 -24
  248. package/src/runtime-internal/types/runtime-api.ts +0 -114
  249. package/src/runtime-internal/ui-contract.ts +0 -519
  250. package/src/runtime-internal/utils/card-intent-adapter.ts +0 -546
  251. package/src/runtime-internal/utils/interaction-inputs.ts +0 -492
  252. package/src/runtime-internal/utils/interaction-labels.ts +0 -23
  253. package/src/runtime-internal/utils/interaction-router.ts +0 -273
  254. package/src/runtime-internal/utils/interaction-status.ts +0 -74
  255. package/src/runtime-internal/workspace-contract.ts +0 -1170
  256. package/src/runtime.ts +0 -34
  257. package/src/testing/create-expect-api.ts +0 -352
  258. package/src/testing/create-test-runtime.ts +0 -381
  259. package/src/testing/definitions.ts +0 -127
  260. package/src/testing/index.ts +0 -3
  261. package/src/testing.ts +0 -1
  262. package/src/type-stubs/manifest-contract.d.ts +0 -42
  263. package/src/type-stubs/manifest-contract.js +0 -72
  264. package/src/type-stubs/ui-contract.d.ts +0 -5
  265. package/src/type-stubs/ui-contract.js +0 -1
  266. package/src/types/authoring-card-properties.type-test.ts +0 -266
  267. package/src/types/authoring.ts +0 -1282
  268. package/src/types/cards.ts +0 -19
  269. package/src/types/contracts.ts +0 -1550
  270. package/src/types/generated-helpers.ts +0 -35
  271. package/src/types/index.ts +0 -147
  272. package/src/types/slots.ts +0 -11
  273. package/src/types.ts +0 -1
  274. package/src/ui/components/ActionButton.tsx +0 -97
  275. package/src/ui/components/ActionPanel.tsx +0 -315
  276. package/src/ui/components/Card.tsx +0 -378
  277. package/src/ui/components/CardDragSurface.tsx +0 -1076
  278. package/src/ui/components/ChromeSuppressionContext.tsx +0 -70
  279. package/src/ui/components/CostDisplay.tsx +0 -145
  280. package/src/ui/components/DiceRoller.tsx +0 -581
  281. package/src/ui/components/Drawer.tsx +0 -180
  282. package/src/ui/components/ErrorBoundary.tsx +0 -275
  283. package/src/ui/components/GameEndDisplay.tsx +0 -398
  284. package/src/ui/components/GameSkeleton.tsx +0 -260
  285. package/src/ui/components/Hand.tsx +0 -468
  286. package/src/ui/components/HandDock.tsx +0 -299
  287. package/src/ui/components/HandView.tsx +0 -441
  288. package/src/ui/components/MobileHandTray.tsx +0 -381
  289. package/src/ui/components/MoreActions.tsx +0 -143
  290. package/src/ui/components/PhaseIndicator.tsx +0 -341
  291. package/src/ui/components/PlayArea.tsx +0 -146
  292. package/src/ui/components/PrimaryActionButton.tsx +0 -336
  293. package/src/ui/components/PrimaryButton.tsx +0 -45
  294. package/src/ui/components/ResourceCounter.tsx +0 -270
  295. package/src/ui/components/StagingZone.tsx +0 -134
  296. package/src/ui/components/ThemedButton.tsx +0 -113
  297. package/src/ui/components/Toast.tsx +0 -264
  298. package/src/ui/components/board/HexGrid.tsx +0 -1294
  299. package/src/ui/components/board/NetworkGraph.tsx +0 -476
  300. package/src/ui/components/board/SlotSystem.tsx +0 -388
  301. package/src/ui/components/board/SquareGrid.tsx +0 -1165
  302. package/src/ui/components/board/TrackBoard.tsx +0 -496
  303. package/src/ui/components/board/ZoneMap.tsx +0 -448
  304. package/src/ui/components/board/hex-board-view.ts +0 -123
  305. package/src/ui/components/board/index.ts +0 -142
  306. package/src/ui/components/board/interaction-accessibility.ts +0 -21
  307. package/src/ui/components/board/target-layer.ts +0 -66
  308. package/src/ui/components/card-render-content.type-test.ts +0 -27
  309. package/src/ui/components/hand-layout-math.ts +0 -163
  310. package/src/ui/components/hand-pointer-engine.ts +0 -413
  311. package/src/ui/components/index.ts +0 -245
  312. package/src/ui/components.ts +0 -1
  313. package/src/ui/defaults/components.tsx +0 -106
  314. package/src/ui/defaults/index.ts +0 -8
  315. package/src/ui/defaults.ts +0 -1
  316. package/src/ui/errors/ValidationError.ts +0 -29
  317. package/src/ui/helpers/cards.ts +0 -19
  318. package/src/ui/helpers/track-board.ts +0 -211
  319. package/src/ui/hooks/useBoardTopology.ts +0 -316
  320. package/src/ui/hooks/useCards.ts +0 -10
  321. package/src/ui/hooks/useHandCardPointer.ts +0 -381
  322. package/src/ui/hooks/useHandLayout.ts +0 -378
  323. package/src/ui/hooks/useHandPresentation.ts +0 -121
  324. package/src/ui/hooks/useHexBoard.ts +0 -74
  325. package/src/ui/hooks/useHexGrid.ts +0 -185
  326. package/src/ui/hooks/useIsMobile.ts +0 -35
  327. package/src/ui/hooks/usePanZoom.ts +0 -278
  328. package/src/ui/hooks/useSquareBoard.ts +0 -124
  329. package/src/ui/hooks/useSquareGrid.ts +0 -328
  330. package/src/ui/index.ts +0 -98
  331. package/src/ui/internal/ui/alert.tsx +0 -51
  332. package/src/ui/internal/ui/button.tsx +0 -58
  333. package/src/ui/internal/ui/dialog.tsx +0 -134
  334. package/src/ui/internal/ui/input.tsx +0 -21
  335. package/src/ui/internal/ui/label.tsx +0 -21
  336. package/src/ui/internal/ui/select.tsx +0 -129
  337. package/src/ui/internal/ui/tooltip.tsx +0 -54
  338. package/src/ui/internal/ui/utils.ts +0 -5
  339. package/src/ui/plugin-styles.css +0 -250
  340. package/src/ui/primitives/dialog-lifecycle.ts +0 -58
  341. package/src/ui/primitives/dice.tsx +0 -79
  342. package/src/ui/primitives/primitive-props.tsx +0 -101
  343. package/src/ui/theme/ThemeProvider.tsx +0 -252
  344. package/src/ui/theme/board.ts +0 -61
  345. package/src/ui/theme/css-vars.ts +0 -105
  346. package/src/ui/theme/derive.ts +0 -240
  347. package/src/ui/theme/index.ts +0 -61
  348. package/src/ui/theme/presets/arcade.ts +0 -261
  349. package/src/ui/theme/presets/studio.ts +0 -261
  350. package/src/ui/theme/presets/tabletop.ts +0 -266
  351. package/src/ui/theme/tokens.ts +0 -392
  352. package/src/ui/types/hex-color.ts +0 -20
  353. package/src/ui/types/player-state.ts +0 -463
  354. package/src/ui/types/tiled-board.ts +0 -785
  355. package/src/ui/types/visual-state.ts +0 -137
  356. package/src/ui.ts +0 -1
  357. /package/dist/{chunk-I46YJSOD.js.map → browser-interaction.js.map} +0 -0
  358. /package/dist/{chunk-KAELH4KC.js.map → chunk-3RQEICD3.js.map} +0 -0
  359. /package/dist/{chunk-T52J5RMF.js.map → codegen.js.map} +0 -0
  360. /package/dist/{generated/runtime-api.js.map → reducer-contract.js.map} +0 -0
  361. /package/dist/runtime/{types/runtime-api.js.map → runtime-api.js.map} +0 -0
  362. /package/dist/ui/{types/player-state.js → player-state.js} +0 -0
  363. /package/dist/ui/{types/player-state.js.map → player-state.js.map} +0 -0
@@ -1,1076 +0,0 @@
1
- /**
2
- * Controlled drag-to-target surface for the SDK hand and drop-target views.
3
- *
4
- * `CardDragSurface` is the single owner of:
5
- *
6
- * - the drag-lifecycle phase (`idle`/`inspecting`/`dragging`/`settling`/
7
- * `returning`)
8
- * - the registry of drop targets, including their eligibility
9
- * - all committed `CardIntent` emission (`activate`, `previewStart`,
10
- * `previewEnd`, and `drop`)
11
- * - the lifted-card overlay, settle/snap-back animation and live
12
- * announcement
13
- *
14
- * `CardDropTargetView` is a generic controlled drop-target wrapper. It
15
- * registers the underlying DOM element (and its eligible/disabled state) so
16
- * the lifted pointer can be matched without exposing geometry to the
17
- * caller. Its registration is stable: only the `targetId` triggers register/
18
- * unregister; eligibility and label changes flow through `updateTarget`.
19
- *
20
- * `HandView` (and the hook it uses) drives this surface through the
21
- * controller exposed by `useCardDragSurface()`. Pointer events come from
22
- * `HandPointerEngine`'s lift callbacks. Keyboard pickup, target traversal
23
- * and Escape are handled here so that the drag-lifecycle has exactly one
24
- * authoritative owner.
25
- */
26
-
27
- import {
28
- createContext,
29
- useCallback,
30
- useContext,
31
- useEffect,
32
- useId,
33
- useMemo,
34
- useRef,
35
- useState,
36
- type CSSProperties,
37
- type KeyboardEvent,
38
- type ReactNode,
39
- } from "react";
40
- import { createPortal } from "react-dom";
41
- import { clsx } from "clsx";
42
- import { AnimatePresence, motion, type Transition } from "framer-motion";
43
- import { useTheme } from "../theme/ThemeProvider.js";
44
- import {
45
- dropTargetVisualStateDataAttributes,
46
- type CardDropTargetVisualState,
47
- type CardIntent,
48
- } from "../types/visual-state.js";
49
-
50
- interface RegisteredDropTarget {
51
- targetId: string;
52
- disabled: boolean;
53
- eligible: boolean;
54
- element: HTMLElement;
55
- /** Plain-text label used for the live a11y announcement. */
56
- label: string | null;
57
- /** Order hint for keyboard target traversal (lower numbers focus first). */
58
- order: number;
59
- }
60
-
61
- export type DragPhase =
62
- | "idle"
63
- | "inspecting"
64
- | "dragging"
65
- | "settling"
66
- | "returning";
67
-
68
- interface ActiveDragState {
69
- cardId: string;
70
- cardLabel: string | null;
71
- source: "pointer" | "keyboard";
72
- pointerId: number | null;
73
- pointerX: number;
74
- pointerY: number;
75
- grabOffsetX: number;
76
- grabOffsetY: number;
77
- /**
78
- * Source rectangle captured at lift time, used as the snap-back/origin
79
- * geometry for animated returns.
80
- */
81
- sourceRect: { left: number; top: number; width: number; height: number };
82
- content: ReactNode;
83
- overTargetId: string | null;
84
- keyboardFocusedTargetId: string | null;
85
- /** DOM node we should focus when the lifecycle ends. */
86
- sourceFocus: HTMLElement | null;
87
- }
88
-
89
- interface SettlingState {
90
- cardId: string;
91
- source: "pointer" | "keyboard";
92
- pointerX: number;
93
- pointerY: number;
94
- grabOffsetX: number;
95
- grabOffsetY: number;
96
- sourceRect: { left: number; top: number; width: number; height: number };
97
- targetRect: { left: number; top: number; width: number; height: number };
98
- content: ReactNode;
99
- }
100
-
101
- interface ReturningState {
102
- cardId: string;
103
- source: "pointer" | "keyboard";
104
- pointerX: number;
105
- pointerY: number;
106
- grabOffsetX: number;
107
- grabOffsetY: number;
108
- sourceRect: { left: number; top: number; width: number; height: number };
109
- content: ReactNode;
110
- }
111
-
112
- export interface CardDragSurfaceController {
113
- /** Identity of the card currently in the drag-lifecycle, if any. */
114
- activeCardId: string | null;
115
- /** Source of the active drag, if any. */
116
- activeSource: "pointer" | "keyboard" | null;
117
- /** Drag-lifecycle phase. */
118
- phase: DragPhase;
119
- /** Currently highlighted drop target id, if any. */
120
- overTargetId: string | null;
121
- /** Currently keyboard-focused target id, if any. */
122
- keyboardFocusedTargetId: string | null;
123
- /**
124
- * Begin a pointer drag session. Returns `true` if the session started.
125
- */
126
- startPointerDrag: (input: PointerDragInput) => boolean;
127
- /** Update the pointer coordinates of an active pointer drag. */
128
- updatePointer: (point: { x: number; y: number }) => void;
129
- /**
130
- * Commit the active pointer drag at the supplied release position. Emits
131
- * a `drop` intent if the pointer is over an eligible target, or schedules
132
- * a snap back otherwise.
133
- */
134
- releasePointer: (point: { x: number; y: number }) => void;
135
- /** Cancel the active drag (pointer or keyboard) without committing. */
136
- cancelDrag: () => void;
137
- /**
138
- * Begin a keyboard drag session. The first eligible registered target is
139
- * focused automatically.
140
- */
141
- startKeyboardDrag: (input: KeyboardDragInput) => boolean;
142
- /** Move keyboard focus across registered eligible targets. */
143
- moveKeyboardFocus: (direction: "next" | "prev") => void;
144
- /** Commit the active keyboard drag on the focused target. */
145
- commitKeyboardDrop: () => void;
146
- /**
147
- * Record a tap that did not produce a lift. The surface holds the
148
- * `inspecting` phase until another lift, drop, or external dismissal.
149
- */
150
- recordTap: (input: TapInput) => void;
151
- /**
152
- * Record a `previewStart` intent. Surface owns canonical intent emission
153
- * so consumers subscribe in one place.
154
- */
155
- recordPreviewStart: (cardId: string) => void;
156
- /** Record a `previewEnd` intent. */
157
- recordPreviewEnd: (cardId: string) => void;
158
- /**
159
- * Record a desktop/keyboard `activate` intent under the `direct-activate`
160
- * policy. Drag-to-target policy never calls this; it is centralized here
161
- * so that the surface remains the only ingress for `CardIntent`.
162
- */
163
- recordActivate: (cardId: string, source: "tap" | "keyboard") => void;
164
- /** Clear the `inspecting` phase. */
165
- clearInspect: () => void;
166
- /** Total number of currently registered eligible targets. */
167
- eligibleTargetCount: number;
168
- }
169
-
170
- interface TapInput {
171
- cardId: string;
172
- cardEligible: boolean;
173
- cardDisabled: boolean;
174
- sourceFocus?: HTMLElement | null;
175
- }
176
-
177
- interface PointerDragInput {
178
- cardId: string;
179
- cardLabel?: string | null;
180
- pointerId: number;
181
- startX: number;
182
- startY: number;
183
- pointerX: number;
184
- pointerY: number;
185
- grabOffsetX: number;
186
- grabOffsetY: number;
187
- sourceRect: { left: number; top: number; width: number; height: number };
188
- content: ReactNode;
189
- sourceFocus?: HTMLElement | null;
190
- }
191
-
192
- interface KeyboardDragInput {
193
- cardId: string;
194
- cardLabel?: string | null;
195
- cardEligible: boolean;
196
- sourceRect: { left: number; top: number; width: number; height: number };
197
- content: ReactNode;
198
- sourceFocus?: HTMLElement | null;
199
- }
200
-
201
- export interface CardDragSurfaceContextValue {
202
- registerTarget: (target: RegisteredDropTarget) => () => void;
203
- updateTarget: (
204
- targetId: string,
205
- patch: Partial<Omit<RegisteredDropTarget, "targetId" | "element">>,
206
- ) => void;
207
- controller: CardDragSurfaceController;
208
- /** Expose the most recent active card id for visual-state computation. */
209
- activeCardId: string | null;
210
- /** Expose the highlighted target id for visual-state computation. */
211
- overTargetId: string | null;
212
- /** Expose whether any drag is in progress. */
213
- dragActive: boolean;
214
- keyboardFocusedTargetId: string | null;
215
- }
216
-
217
- const CardDragSurfaceContext =
218
- createContext<CardDragSurfaceContextValue | null>(null);
219
-
220
- export function useCardDragSurface(): CardDragSurfaceContextValue | null {
221
- return useContext(CardDragSurfaceContext);
222
- }
223
-
224
- export interface CardDragSurfaceProps {
225
- onCardIntent?: (intent: CardIntent) => void;
226
- /**
227
- * Approximate inset (px) used for the deterministic hit test. Defaults to
228
- * `8`. Lowering this lets edges register more aggressively; raising it
229
- * makes overlapping targets less ambiguous.
230
- */
231
- hitTestInsetPx?: number;
232
- /** Suppress the live a11y announcement (for environments providing their own). */
233
- suppressLiveAnnouncement?: boolean;
234
- /** Animation transition tunable for settle/return. */
235
- motionTransition?: Transition;
236
- className?: string;
237
- style?: CSSProperties;
238
- children: ReactNode;
239
- }
240
-
241
- const SETTLE_TRANSITION: Transition = {
242
- type: "spring",
243
- stiffness: 380,
244
- damping: 32,
245
- mass: 0.9,
246
- };
247
- const RETURN_TRANSITION: Transition = {
248
- type: "spring",
249
- stiffness: 320,
250
- damping: 26,
251
- mass: 0.8,
252
- };
253
- const REDUCED_TRANSITION: Transition = { duration: 0 };
254
-
255
- export function CardDragSurface({
256
- onCardIntent,
257
- hitTestInsetPx = 8,
258
- suppressLiveAnnouncement = false,
259
- motionTransition,
260
- className,
261
- style,
262
- children,
263
- }: CardDragSurfaceProps) {
264
- const theme = useTheme();
265
- const reducedMotion = theme.motion.reducedMotion === "true";
266
- const liveRegionId = useId();
267
- const targetsRef = useRef(new Map<string, RegisteredDropTarget>());
268
- const orderCounterRef = useRef(0);
269
- const activeDragRef = useRef<ActiveDragState | null>(null);
270
- const inspectingRef = useRef<string | null>(null);
271
- const [activeDrag, setActiveDrag] = useState<ActiveDragState | null>(null);
272
- const [inspectingCardId, setInspectingCardId] = useState<string | null>(null);
273
- const [settlingState, setSettlingState] = useState<SettlingState | null>(
274
- null,
275
- );
276
- const [returningState, setReturningState] = useState<ReturningState | null>(
277
- null,
278
- );
279
- const onIntentRef = useRef(onCardIntent);
280
- onIntentRef.current = onCardIntent;
281
-
282
- const settleTransition = motionTransition ?? SETTLE_TRANSITION;
283
- const returnTransition = motionTransition ?? RETURN_TRANSITION;
284
-
285
- const setActive = useCallback((next: ActiveDragState | null) => {
286
- activeDragRef.current = next;
287
- setActiveDrag(next);
288
- }, []);
289
-
290
- const setInspecting = useCallback((next: string | null) => {
291
- inspectingRef.current = next;
292
- setInspectingCardId(next);
293
- }, []);
294
-
295
- const finalizeInteraction = useCallback((sourceFocus: HTMLElement | null) => {
296
- if (sourceFocus) {
297
- // Defer focus restoration so animation can complete first frame.
298
- queueMicrotask(() => {
299
- try {
300
- sourceFocus.focus({ preventScroll: true });
301
- } catch {
302
- // Source may have been unmounted; ignore.
303
- }
304
- });
305
- }
306
- }, []);
307
-
308
- const registerTarget = useCallback(
309
- (target: RegisteredDropTarget) => {
310
- targetsRef.current.set(target.targetId, {
311
- ...target,
312
- order: target.order || ++orderCounterRef.current,
313
- });
314
- return () => {
315
- targetsRef.current.delete(target.targetId);
316
- const active = activeDragRef.current;
317
- if (active && active.keyboardFocusedTargetId === target.targetId) {
318
- setActive({ ...active, keyboardFocusedTargetId: null });
319
- }
320
- };
321
- },
322
- [setActive],
323
- );
324
-
325
- const updateTarget = useCallback(
326
- (
327
- targetId: string,
328
- patch: Partial<Omit<RegisteredDropTarget, "targetId" | "element">>,
329
- ) => {
330
- const existing = targetsRef.current.get(targetId);
331
- if (!existing) return;
332
- targetsRef.current.set(targetId, { ...existing, ...patch });
333
- },
334
- [],
335
- );
336
-
337
- const isTargetUsable = useCallback((target: RegisteredDropTarget) => {
338
- return !target.disabled && target.eligible !== false;
339
- }, []);
340
-
341
- const resolveDropTarget = useCallback(
342
- (point: { x: number; y: number }): string | null => {
343
- for (const target of targetsRef.current.values()) {
344
- if (!isTargetUsable(target)) continue;
345
- const rect = target.element.getBoundingClientRect();
346
- if (
347
- point.x >= rect.left + hitTestInsetPx &&
348
- point.x <= rect.right - hitTestInsetPx &&
349
- point.y >= rect.top + hitTestInsetPx &&
350
- point.y <= rect.bottom - hitTestInsetPx
351
- ) {
352
- return target.targetId;
353
- }
354
- }
355
- return null;
356
- },
357
- [hitTestInsetPx, isTargetUsable],
358
- );
359
-
360
- const sortedUsableTargetIds = useCallback((): string[] => {
361
- const entries = Array.from(targetsRef.current.values()).filter((t) =>
362
- isTargetUsable(t),
363
- );
364
- entries.sort((a, b) => a.order - b.order);
365
- return entries.map((t) => t.targetId);
366
- }, [isTargetUsable]);
367
-
368
- const recordTap = useCallback(
369
- (input: TapInput) => {
370
- if (input.cardDisabled || !input.cardEligible) return;
371
- // Tap is non-committing in drag-to-target mode regardless of whether
372
- // any usable target is currently registered. A missing target is a
373
- // composition/availability problem, not a reason to silently fall
374
- // back to tap-to-play. Hold the card in `inspecting` so the user can
375
- // see what they tapped while the runtime decides what to do next.
376
- setInspecting(input.cardId);
377
- },
378
- [setInspecting],
379
- );
380
-
381
- const recordPreviewStart = useCallback((cardId: string) => {
382
- onIntentRef.current?.({ type: "previewStart", cardId });
383
- }, []);
384
-
385
- const recordPreviewEnd = useCallback((cardId: string) => {
386
- onIntentRef.current?.({ type: "previewEnd", cardId });
387
- }, []);
388
-
389
- const recordActivate = useCallback(
390
- (cardId: string, source: "tap" | "keyboard") => {
391
- onIntentRef.current?.({ type: "activate", cardId, source });
392
- },
393
- [],
394
- );
395
-
396
- const clearInspect = useCallback(() => {
397
- if (inspectingRef.current !== null) setInspecting(null);
398
- }, [setInspecting]);
399
-
400
- const startPointerDrag = useCallback(
401
- (input: PointerDragInput): boolean => {
402
- if (activeDragRef.current) return false;
403
- if (inspectingRef.current) setInspecting(null);
404
- const overTargetId = resolveDropTarget({
405
- x: input.pointerX,
406
- y: input.pointerY,
407
- });
408
- const next: ActiveDragState = {
409
- cardId: input.cardId,
410
- cardLabel: input.cardLabel ?? null,
411
- source: "pointer",
412
- pointerId: input.pointerId,
413
- pointerX: input.pointerX,
414
- pointerY: input.pointerY,
415
- grabOffsetX: input.grabOffsetX,
416
- grabOffsetY: input.grabOffsetY,
417
- sourceRect: input.sourceRect,
418
- content: input.content,
419
- overTargetId,
420
- keyboardFocusedTargetId: null,
421
- sourceFocus: input.sourceFocus ?? null,
422
- };
423
- setActive(next);
424
- return true;
425
- },
426
- [resolveDropTarget, setActive, setInspecting],
427
- );
428
-
429
- const updatePointer = useCallback(
430
- (point: { x: number; y: number }) => {
431
- const active = activeDragRef.current;
432
- if (!active || active.source !== "pointer") return;
433
- const overTargetId = resolveDropTarget(point);
434
- setActive({
435
- ...active,
436
- pointerX: point.x,
437
- pointerY: point.y,
438
- overTargetId,
439
- });
440
- },
441
- [resolveDropTarget, setActive],
442
- );
443
-
444
- const completeWithDrop = useCallback(
445
- (
446
- active: ActiveDragState,
447
- targetId: string,
448
- releaseX: number,
449
- releaseY: number,
450
- ) => {
451
- const target = targetsRef.current.get(targetId);
452
- const targetRect = target?.element.getBoundingClientRect();
453
- const settling: SettlingState = {
454
- cardId: active.cardId,
455
- source: active.source,
456
- pointerX: releaseX,
457
- pointerY: releaseY,
458
- grabOffsetX: active.grabOffsetX,
459
- grabOffsetY: active.grabOffsetY,
460
- sourceRect: active.sourceRect,
461
- targetRect: targetRect
462
- ? {
463
- left: targetRect.left,
464
- top: targetRect.top,
465
- width: targetRect.width,
466
- height: targetRect.height,
467
- }
468
- : active.sourceRect,
469
- content: active.content,
470
- };
471
- onIntentRef.current?.({
472
- type: "drop",
473
- cardId: active.cardId,
474
- targetId,
475
- source: active.source === "keyboard" ? "keyboard" : "pointer",
476
- });
477
- setSettlingState(settling);
478
- setReturningState(null);
479
- setActive(null);
480
- finalizeInteraction(active.sourceFocus);
481
- },
482
- [finalizeInteraction, setActive],
483
- );
484
-
485
- const completeWithReturn = useCallback(
486
- (active: ActiveDragState, releaseX: number, releaseY: number) => {
487
- const returning: ReturningState = {
488
- cardId: active.cardId,
489
- source: active.source,
490
- pointerX: releaseX,
491
- pointerY: releaseY,
492
- grabOffsetX: active.grabOffsetX,
493
- grabOffsetY: active.grabOffsetY,
494
- sourceRect: active.sourceRect,
495
- content: active.content,
496
- };
497
- setReturningState(returning);
498
- setSettlingState(null);
499
- setActive(null);
500
- finalizeInteraction(active.sourceFocus);
501
- },
502
- [finalizeInteraction, setActive],
503
- );
504
-
505
- const releasePointer = useCallback(
506
- (point: { x: number; y: number }) => {
507
- const active = activeDragRef.current;
508
- if (!active || active.source !== "pointer") return;
509
- const overTargetId = resolveDropTarget(point);
510
- if (overTargetId) {
511
- completeWithDrop(active, overTargetId, point.x, point.y);
512
- return;
513
- }
514
- completeWithReturn(active, point.x, point.y);
515
- },
516
- [completeWithDrop, completeWithReturn, resolveDropTarget],
517
- );
518
-
519
- const cancelDrag = useCallback(() => {
520
- const active = activeDragRef.current;
521
- if (!active) return;
522
- completeWithReturn(active, active.pointerX, active.pointerY);
523
- }, [completeWithReturn]);
524
-
525
- const startKeyboardDrag = useCallback(
526
- (input: KeyboardDragInput): boolean => {
527
- if (activeDragRef.current) return false;
528
- if (!input.cardEligible) return false;
529
- if (inspectingRef.current) setInspecting(null);
530
- const ids = sortedUsableTargetIds();
531
- if (ids.length === 0) return false;
532
- const firstFocus = ids[0]!;
533
- const firstTarget = targetsRef.current.get(firstFocus);
534
- const firstRect = firstTarget?.element.getBoundingClientRect();
535
- const next: ActiveDragState = {
536
- cardId: input.cardId,
537
- cardLabel: input.cardLabel ?? null,
538
- source: "keyboard",
539
- pointerId: null,
540
- pointerX: firstRect ? firstRect.left + firstRect.width / 2 : 0,
541
- pointerY: firstRect ? firstRect.top + firstRect.height / 2 : 0,
542
- grabOffsetX: 0,
543
- grabOffsetY: 0,
544
- sourceRect: input.sourceRect,
545
- content: input.content,
546
- overTargetId: firstFocus,
547
- keyboardFocusedTargetId: firstFocus,
548
- sourceFocus: input.sourceFocus ?? null,
549
- };
550
- setActive(next);
551
- // Focus is moved by `CardDropTargetView` in an effect that watches
552
- // `keyboardFocusedTargetId`, ensuring focus transfer happens after
553
- // React commits the render that promotes this target.
554
- return true;
555
- },
556
- [setActive, setInspecting, sortedUsableTargetIds],
557
- );
558
-
559
- const moveKeyboardFocus = useCallback(
560
- (direction: "next" | "prev") => {
561
- const active = activeDragRef.current;
562
- if (!active || active.source !== "keyboard") return;
563
- const ids = sortedUsableTargetIds();
564
- if (ids.length === 0) return;
565
- const currentIdx = active.keyboardFocusedTargetId
566
- ? ids.indexOf(active.keyboardFocusedTargetId)
567
- : -1;
568
- const nextIdx =
569
- direction === "next"
570
- ? (currentIdx + 1) % ids.length
571
- : (currentIdx - 1 + ids.length) % ids.length;
572
- const nextId = ids[nextIdx]!;
573
- const target = targetsRef.current.get(nextId);
574
- const rect = target?.element.getBoundingClientRect();
575
- setActive({
576
- ...active,
577
- keyboardFocusedTargetId: nextId,
578
- overTargetId: nextId,
579
- pointerX: rect ? rect.left + rect.width / 2 : active.pointerX,
580
- pointerY: rect ? rect.top + rect.height / 2 : active.pointerY,
581
- });
582
- // Focus moved by the target's keyboardFocused effect.
583
- },
584
- [setActive, sortedUsableTargetIds],
585
- );
586
-
587
- const commitKeyboardDrop = useCallback(() => {
588
- const active = activeDragRef.current;
589
- if (!active || active.source !== "keyboard") return;
590
- const targetId = active.keyboardFocusedTargetId;
591
- if (!targetId) return;
592
- const target = targetsRef.current.get(targetId);
593
- if (!target || !isTargetUsable(target)) return;
594
- const rect = target.element.getBoundingClientRect();
595
- completeWithDrop(
596
- active,
597
- targetId,
598
- rect.left + rect.width / 2,
599
- rect.top + rect.height / 2,
600
- );
601
- }, [completeWithDrop, isTargetUsable]);
602
-
603
- const phase: DragPhase = useMemo(() => {
604
- if (activeDrag) return "dragging";
605
- if (settlingState) return "settling";
606
- if (returningState) return "returning";
607
- if (inspectingCardId) return "inspecting";
608
- return "idle";
609
- }, [activeDrag, inspectingCardId, returningState, settlingState]);
610
-
611
- const eligibleTargetCount = useMemo(
612
- () => sortedUsableTargetIds().length,
613
- // Recompute whenever any target patch changes — `activeDrag` is a cheap
614
- // proxy: target updates also bump the surface re-render via parent state.
615
- // eslint-disable-next-line react-hooks/exhaustive-deps
616
- [activeDrag, sortedUsableTargetIds],
617
- );
618
-
619
- const controller: CardDragSurfaceController = useMemo(
620
- () => ({
621
- activeCardId:
622
- activeDrag?.cardId ??
623
- settlingState?.cardId ??
624
- returningState?.cardId ??
625
- (inspectingCardId ? inspectingCardId : null),
626
- activeSource: activeDrag?.source ?? null,
627
- phase,
628
- overTargetId: activeDrag?.overTargetId ?? null,
629
- keyboardFocusedTargetId: activeDrag?.keyboardFocusedTargetId ?? null,
630
- startPointerDrag,
631
- updatePointer,
632
- releasePointer,
633
- cancelDrag,
634
- startKeyboardDrag,
635
- moveKeyboardFocus,
636
- commitKeyboardDrop,
637
- recordTap,
638
- recordPreviewStart,
639
- recordPreviewEnd,
640
- recordActivate,
641
- clearInspect,
642
- eligibleTargetCount,
643
- }),
644
- [
645
- activeDrag,
646
- cancelDrag,
647
- clearInspect,
648
- commitKeyboardDrop,
649
- eligibleTargetCount,
650
- inspectingCardId,
651
- moveKeyboardFocus,
652
- phase,
653
- recordActivate,
654
- recordPreviewEnd,
655
- recordPreviewStart,
656
- recordTap,
657
- releasePointer,
658
- returningState,
659
- settlingState,
660
- startKeyboardDrag,
661
- startPointerDrag,
662
- updatePointer,
663
- ],
664
- );
665
-
666
- const contextValue: CardDragSurfaceContextValue = useMemo(
667
- () => ({
668
- registerTarget,
669
- updateTarget,
670
- controller,
671
- activeCardId: controller.activeCardId,
672
- overTargetId: activeDrag?.overTargetId ?? null,
673
- dragActive: activeDrag !== null,
674
- keyboardFocusedTargetId: activeDrag?.keyboardFocusedTargetId ?? null,
675
- }),
676
- [activeDrag, controller, registerTarget, updateTarget],
677
- );
678
-
679
- useEffect(() => {
680
- if (!activeDrag) return;
681
- function onKeyDown(event: globalThis.KeyboardEvent) {
682
- if (event.key === "Escape") {
683
- event.preventDefault();
684
- cancelDrag();
685
- }
686
- }
687
- window.addEventListener("keydown", onKeyDown);
688
- return () => window.removeEventListener("keydown", onKeyDown);
689
- }, [activeDrag, cancelDrag]);
690
-
691
- const announcement = useMemo(() => {
692
- if (!activeDrag) return null;
693
- const overTarget = activeDrag.overTargetId
694
- ? targetsRef.current.get(activeDrag.overTargetId)
695
- : null;
696
- const overLabel = overTarget?.label ?? null;
697
- const cardLabel = activeDrag.cardLabel ?? "Card";
698
- if (overLabel) {
699
- return `${cardLabel} over ${overLabel}. Press Enter to drop or Escape to cancel.`;
700
- }
701
- return `${cardLabel} picked up. Move to a target or press Escape to cancel.`;
702
- }, [activeDrag]);
703
-
704
- const overlayContent = activeDrag ? (
705
- <DragOverlay session={activeDrag} reducedMotion={reducedMotion} />
706
- ) : null;
707
-
708
- const settleOverlay = settlingState ? (
709
- <SettleOverlay
710
- key={`settle-${settlingState.cardId}`}
711
- session={settlingState}
712
- transition={reducedMotion ? REDUCED_TRANSITION : settleTransition}
713
- onDone={() => setSettlingState(null)}
714
- />
715
- ) : null;
716
-
717
- const returnOverlay = returningState ? (
718
- <ReturnOverlay
719
- key={`return-${returningState.cardId}`}
720
- session={returningState}
721
- transition={reducedMotion ? REDUCED_TRANSITION : returnTransition}
722
- onDone={() => setReturningState(null)}
723
- />
724
- ) : null;
725
-
726
- const portalRoot = typeof document !== "undefined" ? document.body : null;
727
-
728
- return (
729
- <CardDragSurfaceContext.Provider value={contextValue}>
730
- <div
731
- data-dreamboard-card-drag-surface=""
732
- data-drag-active={activeDrag ? "true" : undefined}
733
- data-drag-source={activeDrag?.source}
734
- data-drag-phase={phase}
735
- className={clsx("relative", className)}
736
- style={style}
737
- >
738
- {children}
739
- </div>
740
- {portalRoot
741
- ? createPortal(
742
- <AnimatePresence initial={false}>
743
- {overlayContent}
744
- {settleOverlay}
745
- {returnOverlay}
746
- </AnimatePresence>,
747
- portalRoot,
748
- )
749
- : null}
750
- {!suppressLiveAnnouncement ? (
751
- <div
752
- id={liveRegionId}
753
- role="status"
754
- aria-live="polite"
755
- className="sr-only"
756
- data-dreamboard-card-drag-announcement=""
757
- style={{
758
- position: "absolute",
759
- width: 1,
760
- height: 1,
761
- padding: 0,
762
- margin: -1,
763
- overflow: "hidden",
764
- clip: "rect(0,0,0,0)",
765
- whiteSpace: "nowrap",
766
- border: 0,
767
- }}
768
- >
769
- {announcement ?? ""}
770
- </div>
771
- ) : null}
772
- </CardDragSurfaceContext.Provider>
773
- );
774
- }
775
-
776
- interface DragOverlayProps {
777
- session: ActiveDragState;
778
- reducedMotion: boolean;
779
- }
780
-
781
- function DragOverlay({ session, reducedMotion }: DragOverlayProps) {
782
- if (session.source === "keyboard") {
783
- return (
784
- <motion.div
785
- key="overlay-keyboard"
786
- data-dreamboard-card-drag-overlay=""
787
- data-source="keyboard"
788
- initial={{ opacity: 0 }}
789
- animate={{ opacity: 1 }}
790
- exit={{ opacity: 0 }}
791
- transition={reducedMotion ? REDUCED_TRANSITION : { duration: 0.12 }}
792
- style={{
793
- position: "fixed",
794
- left: session.sourceRect.left,
795
- top: session.sourceRect.top,
796
- width: session.sourceRect.width,
797
- height: session.sourceRect.height,
798
- pointerEvents: "none",
799
- zIndex: 1000,
800
- boxShadow: reducedMotion ? "none" : "0 12px 32px rgba(0,0,0,0.18)",
801
- borderRadius: 12,
802
- }}
803
- >
804
- {session.content}
805
- </motion.div>
806
- );
807
- }
808
-
809
- const liftedAnimate = reducedMotion
810
- ? {
811
- scale: 1,
812
- rotate: 0,
813
- filter: "drop-shadow(0 0 0 rgba(0,0,0,0))",
814
- }
815
- : {
816
- scale: [1, 1.42, 1.35],
817
- rotate: [0, -3, 3, -1.8, 1.8, 0],
818
- filter: [
819
- "drop-shadow(0 4px 8px rgba(0,0,0,0.18))",
820
- "drop-shadow(0 24px 36px rgba(0,0,0,0.36))",
821
- ],
822
- };
823
- const liftedTransition: Transition = reducedMotion
824
- ? REDUCED_TRANSITION
825
- : {
826
- scale: { type: "spring", stiffness: 420, damping: 22, mass: 0.7 },
827
- rotate: { duration: 0.55, ease: "easeInOut" },
828
- filter: { duration: 0.18, ease: "easeOut" },
829
- };
830
- return (
831
- <motion.div
832
- key="overlay-pointer"
833
- data-dreamboard-card-drag-overlay=""
834
- data-source="pointer"
835
- initial={{
836
- scale: 1,
837
- rotate: 0,
838
- opacity: 1,
839
- filter: "drop-shadow(0 2px 6px rgba(0,0,0,0.15))",
840
- }}
841
- animate={liftedAnimate}
842
- exit={{ opacity: 0 }}
843
- transition={liftedTransition}
844
- style={{
845
- position: "fixed",
846
- left: session.pointerX - session.sourceRect.width / 2,
847
- top: session.pointerY - session.sourceRect.height / 2,
848
- width: session.sourceRect.width,
849
- height: session.sourceRect.height,
850
- zIndex: 1000,
851
- pointerEvents: "none",
852
- touchAction: "none",
853
- transformOrigin: "center center",
854
- willChange: "transform, filter",
855
- }}
856
- >
857
- {session.content}
858
- </motion.div>
859
- );
860
- }
861
-
862
- interface SettleOverlayProps {
863
- session: SettlingState;
864
- transition: Transition;
865
- onDone: () => void;
866
- }
867
-
868
- function SettleOverlay({ session, transition, onDone }: SettleOverlayProps) {
869
- const startLeft = session.pointerX - session.sourceRect.width / 2;
870
- const startTop = session.pointerY - session.sourceRect.height / 2;
871
- const endLeft =
872
- session.targetRect.left +
873
- session.targetRect.width / 2 -
874
- session.sourceRect.width / 2;
875
- const endTop =
876
- session.targetRect.top +
877
- session.targetRect.height / 2 -
878
- session.sourceRect.height / 2;
879
- return (
880
- <motion.div
881
- data-dreamboard-card-drag-overlay=""
882
- data-source={session.source}
883
- data-drag-phase="settling"
884
- initial={{ left: startLeft, top: startTop, scale: 1.06, opacity: 1 }}
885
- animate={{ left: endLeft, top: endTop, scale: 0.92, opacity: 0 }}
886
- transition={transition}
887
- onAnimationComplete={onDone}
888
- style={{
889
- position: "fixed",
890
- width: session.sourceRect.width,
891
- height: session.sourceRect.height,
892
- zIndex: 1000,
893
- pointerEvents: "none",
894
- }}
895
- >
896
- {session.content}
897
- </motion.div>
898
- );
899
- }
900
-
901
- interface ReturnOverlayProps {
902
- session: ReturningState;
903
- transition: Transition;
904
- onDone: () => void;
905
- }
906
-
907
- function ReturnOverlay({ session, transition, onDone }: ReturnOverlayProps) {
908
- const startLeft = session.pointerX - session.sourceRect.width / 2;
909
- const startTop = session.pointerY - session.sourceRect.height / 2;
910
- return (
911
- <motion.div
912
- data-dreamboard-card-drag-overlay=""
913
- data-source={session.source}
914
- data-drag-phase="returning"
915
- initial={{ left: startLeft, top: startTop, scale: 1.06, opacity: 1 }}
916
- animate={{
917
- left: session.sourceRect.left,
918
- top: session.sourceRect.top,
919
- scale: 1,
920
- opacity: 1,
921
- }}
922
- exit={{ opacity: 0 }}
923
- transition={transition}
924
- onAnimationComplete={onDone}
925
- style={{
926
- position: "fixed",
927
- width: session.sourceRect.width,
928
- height: session.sourceRect.height,
929
- zIndex: 1000,
930
- pointerEvents: "none",
931
- }}
932
- >
933
- {session.content}
934
- </motion.div>
935
- );
936
- }
937
-
938
- export interface CardDropTargetViewProps {
939
- targetId: string;
940
- state?: CardDropTargetVisualState;
941
- /** Plain-text label used in live announcements ("Selected cards", etc.). */
942
- label?: string;
943
- renderTarget: (state: CardDropTargetVisualState) => ReactNode;
944
- className?: string;
945
- style?: CSSProperties;
946
- /** Tab order hint (lower numbers focus first). */
947
- order?: number;
948
- /** ARIA role override; defaults to `button`. */
949
- role?: string;
950
- }
951
-
952
- export function CardDropTargetView({
953
- targetId,
954
- state,
955
- label,
956
- renderTarget,
957
- className,
958
- style,
959
- order,
960
- role = "button",
961
- }: CardDropTargetViewProps) {
962
- const surface = useCardDragSurface();
963
- const ref = useRef<HTMLDivElement | null>(null);
964
- const disabled = state?.disabled ?? false;
965
- const baseEligible = state?.eligible ?? true;
966
- const baseLabel = label ?? null;
967
- const orderProp = order ?? 0;
968
-
969
- const registerTargetRef = useRef(surface?.registerTarget);
970
- registerTargetRef.current = surface?.registerTarget;
971
- const updateTargetRef = useRef(surface?.updateTarget);
972
- updateTargetRef.current = surface?.updateTarget;
973
-
974
- // Stable register/unregister keyed only on `targetId`. Eligibility, label
975
- // and disabled flow through `updateTarget` so changing surface context
976
- // values cannot tear down the registration mid-drag.
977
- useEffect(() => {
978
- const element = ref.current;
979
- const register = registerTargetRef.current;
980
- if (!register || !element) return;
981
- const unregister = register({
982
- targetId,
983
- disabled,
984
- eligible: baseEligible,
985
- element,
986
- label: baseLabel,
987
- order: orderProp,
988
- });
989
- return unregister;
990
- // eslint-disable-next-line react-hooks/exhaustive-deps
991
- }, [targetId]);
992
-
993
- useEffect(() => {
994
- updateTargetRef.current?.(targetId, {
995
- disabled,
996
- eligible: baseEligible,
997
- label: baseLabel,
998
- order: orderProp,
999
- });
1000
- }, [targetId, disabled, baseEligible, baseLabel, orderProp]);
1001
-
1002
- const dragActive = surface?.dragActive ?? false;
1003
- const overTargetId = surface?.overTargetId ?? null;
1004
- const keyboardFocused = surface?.keyboardFocusedTargetId === targetId;
1005
-
1006
- // Move focus into this target whenever the surface promotes it to the
1007
- // keyboard-focused id. Doing it here, after React commits, is more
1008
- // reliable than firing focus() from inside the surface's `setActive`
1009
- // callback (where React batching can race the source card's commit).
1010
- useEffect(() => {
1011
- if (!keyboardFocused) return;
1012
- const el = ref.current;
1013
- if (!el) return;
1014
- if (document.activeElement === el) return;
1015
- try {
1016
- el.focus({ preventScroll: true });
1017
- } catch {
1018
- // Element may have unmounted; ignore.
1019
- }
1020
- }, [keyboardFocused, targetId]);
1021
-
1022
- const computedState: CardDropTargetVisualState = {
1023
- ...state,
1024
- eligible: baseEligible,
1025
- active: dragActive ? true : state?.active,
1026
- over:
1027
- overTargetId === targetId && baseEligible ? true : (state?.over ?? false),
1028
- };
1029
-
1030
- const handleKeyDown = useCallback(
1031
- (event: KeyboardEvent<HTMLDivElement>) => {
1032
- if (!surface) return;
1033
- const controller = surface.controller;
1034
- if (controller.activeSource !== "keyboard") return;
1035
- if (event.key === "Enter" || event.key === " ") {
1036
- event.preventDefault();
1037
- controller.commitKeyboardDrop();
1038
- return;
1039
- }
1040
- if (event.key === "ArrowRight" || event.key === "ArrowDown") {
1041
- event.preventDefault();
1042
- controller.moveKeyboardFocus("next");
1043
- return;
1044
- }
1045
- if (event.key === "ArrowLeft" || event.key === "ArrowUp") {
1046
- event.preventDefault();
1047
- controller.moveKeyboardFocus("prev");
1048
- return;
1049
- }
1050
- if (event.key === "Escape") {
1051
- event.preventDefault();
1052
- controller.cancelDrag();
1053
- }
1054
- },
1055
- [surface],
1056
- );
1057
-
1058
- return (
1059
- <div
1060
- ref={ref}
1061
- data-dreamboard-card-drop-target=""
1062
- data-target-id={targetId}
1063
- data-keyboard-focused={keyboardFocused ? "true" : undefined}
1064
- role={role}
1065
- tabIndex={disabled || !baseEligible ? -1 : 0}
1066
- aria-disabled={disabled || !baseEligible || undefined}
1067
- aria-label={label}
1068
- onKeyDown={handleKeyDown}
1069
- className={className}
1070
- style={style}
1071
- {...dropTargetVisualStateDataAttributes(computedState)}
1072
- >
1073
- {renderTarget(computedState)}
1074
- </div>
1075
- );
1076
- }