@dreamboard-games/sdk 0.5.0-alpha.2 → 0.5.0-alpha.4

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 (151) hide show
  1. package/README.md +186 -45
  2. package/dist/board-target-B0QG6OYx.d.ts +8 -0
  3. package/dist/chunk-3JXUL4VN.js +139 -0
  4. package/dist/chunk-3JXUL4VN.js.map +1 -0
  5. package/dist/{chunk-S2JEDVWH.js → chunk-C77SYIVT.js} +6102 -6505
  6. package/dist/chunk-C77SYIVT.js.map +1 -0
  7. package/dist/chunk-DMFVAZI2.js +16 -0
  8. package/dist/chunk-DMFVAZI2.js.map +1 -0
  9. package/dist/chunk-OOFMBCFF.js +1338 -0
  10. package/dist/chunk-OOFMBCFF.js.map +1 -0
  11. package/dist/chunk-UG2Q4NLG.js +590 -0
  12. package/dist/chunk-UG2Q4NLG.js.map +1 -0
  13. package/dist/chunk-XU6WJW6K.js +847 -0
  14. package/dist/chunk-XU6WJW6K.js.map +1 -0
  15. package/dist/diagnostics-B8d5aLad.d.ts +65 -0
  16. package/dist/extract-gVKcB8Og.d.ts +4736 -0
  17. package/dist/index.d.ts +3782 -1
  18. package/dist/index.js +1096 -6
  19. package/dist/index.js.map +1 -1
  20. package/dist/model-vdpLHrxh.d.ts +432 -0
  21. package/dist/react.d.ts +60 -0
  22. package/dist/react.js +208 -0
  23. package/dist/react.js.map +1 -0
  24. package/dist/reducer.d.ts +1244 -78
  25. package/dist/reducer.js +4204 -121
  26. package/dist/reducer.js.map +1 -1
  27. package/dist/testing.d.ts +520 -564
  28. package/dist/testing.js +2451 -915
  29. package/dist/testing.js.map +1 -1
  30. package/dist/types-Bj5ZLTUV.d.ts +114 -0
  31. package/package.json +40 -142
  32. package/dist/HandView-Bi2lxzQP.d.ts +0 -79
  33. package/dist/ResourceCounter-BFxJknqp.d.ts +0 -98
  34. package/dist/ThemeProvider-CElaL2ik.d.ts +0 -99
  35. package/dist/attributes-DbvyMbXw.d.ts +0 -68
  36. package/dist/browser-interaction.d.ts +0 -910
  37. package/dist/browser-interaction.js +0 -119
  38. package/dist/browser-interaction.js.map +0 -1
  39. package/dist/chunk-32PFKDV7.js +0 -11705
  40. package/dist/chunk-32PFKDV7.js.map +0 -1
  41. package/dist/chunk-3BAD4VC2.js +0 -263
  42. package/dist/chunk-3BAD4VC2.js.map +0 -1
  43. package/dist/chunk-3SKDNDPG.js +0 -104
  44. package/dist/chunk-3SKDNDPG.js.map +0 -1
  45. package/dist/chunk-5YV2WHJ4.js +0 -25
  46. package/dist/chunk-5YV2WHJ4.js.map +0 -1
  47. package/dist/chunk-ANRB4XKA.js +0 -90
  48. package/dist/chunk-ANRB4XKA.js.map +0 -1
  49. package/dist/chunk-BI4G3PM2.js +0 -1864
  50. package/dist/chunk-BI4G3PM2.js.map +0 -1
  51. package/dist/chunk-EVCLTY4Q.js +0 -935
  52. package/dist/chunk-EVCLTY4Q.js.map +0 -1
  53. package/dist/chunk-GDPKTBUR.js +0 -564
  54. package/dist/chunk-GDPKTBUR.js.map +0 -1
  55. package/dist/chunk-H6VDGFL5.js +0 -2903
  56. package/dist/chunk-H6VDGFL5.js.map +0 -1
  57. package/dist/chunk-LR3ZTWQF.js +0 -5960
  58. package/dist/chunk-LR3ZTWQF.js.map +0 -1
  59. package/dist/chunk-PF7L4BMG.js +0 -117
  60. package/dist/chunk-PF7L4BMG.js.map +0 -1
  61. package/dist/chunk-PN5O6GG2.js +0 -32
  62. package/dist/chunk-PN5O6GG2.js.map +0 -1
  63. package/dist/chunk-PZ5AY32C.js +0 -10
  64. package/dist/chunk-PZ5AY32C.js.map +0 -1
  65. package/dist/chunk-S2JEDVWH.js.map +0 -1
  66. package/dist/chunk-T3ZKNUZ7.js +0 -1
  67. package/dist/chunk-T3ZKNUZ7.js.map +0 -1
  68. package/dist/chunk-UGH54WZ2.js +0 -223
  69. package/dist/chunk-UGH54WZ2.js.map +0 -1
  70. package/dist/chunk-VCDWP5VL.js +0 -746
  71. package/dist/chunk-VCDWP5VL.js.map +0 -1
  72. package/dist/chunk-VDXOF4FW.js +0 -69
  73. package/dist/chunk-VDXOF4FW.js.map +0 -1
  74. package/dist/chunk-WHR5UW3F.js +0 -1988
  75. package/dist/chunk-WHR5UW3F.js.map +0 -1
  76. package/dist/chunk-Y75CFE77.js +0 -17
  77. package/dist/chunk-Y75CFE77.js.map +0 -1
  78. package/dist/chunk-Z7QCREWI.js +0 -3435
  79. package/dist/chunk-Z7QCREWI.js.map +0 -1
  80. package/dist/chunk-ZZGN2ATS.js +0 -1941
  81. package/dist/chunk-ZZGN2ATS.js.map +0 -1
  82. package/dist/components-gAbIz7hK.d.ts +0 -1284
  83. package/dist/definitions-BgmV_GhC.d.ts +0 -300
  84. package/dist/diagnostics-DIi3ina7.d.ts +0 -50
  85. package/dist/digest.d.ts +0 -8
  86. package/dist/game-CsZScpcn.d.ts +0 -763
  87. package/dist/hex-board-view-aKkblDp6.d.ts +0 -1228
  88. package/dist/index-CcT9Q7N7.d.ts +0 -193
  89. package/dist/index.d-DjzoK7zn.d.ts +0 -2101
  90. package/dist/package-set.d.ts +0 -13
  91. package/dist/package-set.js +0 -12
  92. package/dist/package-set.js.map +0 -1
  93. package/dist/player-state-Cqpyeql0.d.ts +0 -371
  94. package/dist/plugin-runtime-contract.d.ts +0 -17
  95. package/dist/plugin-runtime-contract.js +0 -92
  96. package/dist/plugin-runtime-contract.js.map +0 -1
  97. package/dist/primitive-props-BNHDkgd7.d.ts +0 -16
  98. package/dist/protocol-dYgafTYY.d.ts +0 -314
  99. package/dist/reducer/advanced.d.ts +0 -82
  100. package/dist/reducer/advanced.js +0 -51
  101. package/dist/reducer/advanced.js.map +0 -1
  102. package/dist/reducer-contract.d.ts +0 -11
  103. package/dist/reducer-contract.js +0 -16
  104. package/dist/reducer-contract.js.map +0 -1
  105. package/dist/reference-games/index.d.ts +0 -31
  106. package/dist/reference-games/index.js +0 -48
  107. package/dist/reference-games/index.js.map +0 -1
  108. package/dist/runtime/primitives.d.ts +0 -250
  109. package/dist/runtime/primitives.js +0 -189
  110. package/dist/runtime/primitives.js.map +0 -1
  111. package/dist/runtime/runtime-api.d.ts +0 -2
  112. package/dist/runtime/runtime-api.js +0 -1
  113. package/dist/runtime/runtime-api.js.map +0 -1
  114. package/dist/runtime/workspace-contract.d.ts +0 -392
  115. package/dist/runtime/workspace-contract.js +0 -28
  116. package/dist/runtime/workspace-contract.js.map +0 -1
  117. package/dist/runtime-RJ5orDJU.d.ts +0 -1699
  118. package/dist/runtime-api-Bz5pwNU_.d.ts +0 -296
  119. package/dist/runtime-json-CQ9QbLZ5.d.ts +0 -5
  120. package/dist/runtime.d.ts +0 -83
  121. package/dist/runtime.js +0 -242
  122. package/dist/runtime.js.map +0 -1
  123. package/dist/schema.d.ts +0 -379
  124. package/dist/stale-contract-artifact-error-XLaweZtF.d.ts +0 -18
  125. package/dist/testing-compiler.d.ts +0 -37
  126. package/dist/testing-compiler.js +0 -272
  127. package/dist/testing-compiler.js.map +0 -1
  128. package/dist/testing-runtime.d.ts +0 -119
  129. package/dist/testing-runtime.js +0 -212
  130. package/dist/testing-runtime.js.map +0 -1
  131. package/dist/types-DJj5MJkl.d.ts +0 -256
  132. package/dist/types-DR7DoB1x.d.ts +0 -28
  133. package/dist/types-JWCYHmu7.d.ts +0 -122
  134. package/dist/types.d.ts +0 -1958
  135. package/dist/types.js +0 -14
  136. package/dist/types.js.map +0 -1
  137. package/dist/ui/components.d.ts +0 -17
  138. package/dist/ui/components.js +0 -216
  139. package/dist/ui/components.js.map +0 -1
  140. package/dist/ui/defaults.d.ts +0 -19
  141. package/dist/ui/defaults.js +0 -104
  142. package/dist/ui/defaults.js.map +0 -1
  143. package/dist/ui/player-state.d.ts +0 -2
  144. package/dist/ui/player-state.js +0 -1
  145. package/dist/ui/player-state.js.map +0 -1
  146. package/dist/ui/plugin-styles.css +0 -2
  147. package/dist/ui-contract-GTUPkf8z.d.ts +0 -1168
  148. package/dist/ui.d.ts +0 -317
  149. package/dist/ui.js +0 -277
  150. package/dist/ui.js.map +0 -1
  151. package/dist/views-BpuWcOyN.d.ts +0 -1641
@@ -0,0 +1,60 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+ import { p as Features, x as InstanceOptions, m as CoreInstance, o as FeatureContext, G as GameInstance, r as GameSnapshot, I as IdOf, U as TargetOptions, n as DropTarget } from './model-vdpLHrxh.js';
4
+ import { G as GameSource } from './types-Bj5ZLTUV.js';
5
+ import './extract-gVKcB8Og.js';
6
+ import 'zod';
7
+ import 'honeycomb-grid';
8
+ import './board-target-B0QG6OYx.js';
9
+ import '@tanstack/store';
10
+
11
+ /** Browser-only hit testing receives client pixels; SVG renderers invert their screen CTM. */
12
+ interface BoardDropOptions {
13
+ containsPoint?(point: {
14
+ x: number;
15
+ y: number;
16
+ }): boolean;
17
+ }
18
+
19
+ interface SelectionOptions<Value> {
20
+ readonly compare?: (previous: Value, next: Value) => boolean;
21
+ }
22
+ /** Bind erased game types and features; each provider owns its source lifetime. */
23
+ declare function createGameHook<Game, Source extends GameSource = GameSource>(): <const Enabled extends Features = Record<never, never>>(defaults: Omit<InstanceOptions<Game, Source>, "source"> & {
24
+ features?: (core: CoreInstance<Game>, context: FeatureContext<Game>) => Enabled;
25
+ }) => {
26
+ GameProvider: ({ children, ...overrides }: Partial<Omit<InstanceOptions<Game, Source>, "source">> & {
27
+ source: Source;
28
+ children?: ReactNode;
29
+ }) => react.JSX.Element | null;
30
+ useGame: {
31
+ (): GameInstance<Game, Enabled, Source>;
32
+ <Value>(selector: (snapshot: GameSnapshot<Game, Enabled>) => Value, options?: SelectionOptions<Value>): Value;
33
+ };
34
+ Subscribe: <Value>({ selector, compare, children, }: {
35
+ selector: (snapshot: GameSnapshot<Game, Enabled>) => Value;
36
+ compare?: SelectionOptions<Value>["compare"];
37
+ children: (value: Value) => ReactNode;
38
+ }) => ReactNode;
39
+ useCardDrag: (cardId: IdOf<Game, "cardId">, options?: TargetOptions<Game>) => {
40
+ ref: ((element: Element | null) => void) | undefined;
41
+ handleRef: (element: Element | null) => void;
42
+ isDragging: boolean;
43
+ canDrag: boolean;
44
+ handleProps: {
45
+ type: "button";
46
+ disabled: boolean;
47
+ "aria-label": string;
48
+ "data-drag-card": string;
49
+ style: {
50
+ touchAction: "none";
51
+ };
52
+ };
53
+ };
54
+ useBoardDrop: (target: DropTarget<Game> | null, options?: BoardDropOptions) => {
55
+ ref: (element: Element | null) => void;
56
+ isDropTarget: boolean;
57
+ };
58
+ };
59
+
60
+ export { type BoardDropOptions, type SelectionOptions, createGameHook };
package/dist/react.js ADDED
@@ -0,0 +1,208 @@
1
+ import {
2
+ createGameInstance
3
+ } from "./chunk-OOFMBCFF.js";
4
+ import "./chunk-DMFVAZI2.js";
5
+ import "./chunk-3JXUL4VN.js";
6
+
7
+ // src/react/drag.tsx
8
+ import { useId, useLayoutEffect } from "react";
9
+ import {
10
+ DragDropProvider,
11
+ KeyboardSensor,
12
+ PointerSensor,
13
+ useDragDropManager,
14
+ useDraggable,
15
+ useDroppable
16
+ } from "@dnd-kit/react";
17
+ import {
18
+ Accessibility,
19
+ defaultPreset,
20
+ PointerActivationConstraints
21
+ } from "@dnd-kit/dom";
22
+ import { pointerIntersection } from "@dnd-kit/collision";
23
+ import { jsx, jsxs } from "react/jsx-runtime";
24
+ var sensors = [
25
+ PointerSensor.configure({
26
+ activationConstraints: [
27
+ new PointerActivationConstraints.Distance({ value: 5 })
28
+ ]
29
+ }),
30
+ KeyboardSensor
31
+ ];
32
+ var plugins = defaultPreset.plugins.map(
33
+ (plugin) => plugin === Accessibility ? Accessibility.configure({
34
+ announcements: {
35
+ dragstart: () => "Card picked up. Use arrow keys to move and Space or Enter to drop. Escape cancels.",
36
+ dragover: ({ operation }) => {
37
+ const element = operation.target?.element;
38
+ if (!element) return "No drop destination.";
39
+ const label = element.getAttribute("aria-label") ?? "board destination";
40
+ const board = element.getAttribute("data-board");
41
+ return `Over ${label}${board ? ` on ${board}` : ""}.`;
42
+ },
43
+ dragend: ({ canceled, operation }) => canceled ? "Drag cancelled." : operation.target ? "Card placement selected." : "No placement selected."
44
+ }
45
+ }) : plugin
46
+ );
47
+ function DragLifetime({ game }) {
48
+ const manager = useDragDropManager();
49
+ useLayoutEffect(
50
+ () => game.subscribe(() => {
51
+ if (!game.drag?.active && manager && !manager.dragOperation.status.idle && !manager.dragOperation.status.dropped)
52
+ manager.actions.stop({ canceled: true });
53
+ }),
54
+ [game, manager]
55
+ );
56
+ return null;
57
+ }
58
+ function GameDragProvider({
59
+ game,
60
+ children
61
+ }) {
62
+ return /* @__PURE__ */ jsxs(
63
+ DragDropProvider,
64
+ {
65
+ sensors,
66
+ plugins,
67
+ onBeforeDragStart: (event) => {
68
+ if (!event.operation.source?.data.begin?.()) event.preventDefault();
69
+ },
70
+ onDragOver: ({ operation }) => game.drag?.setDropTarget(operation.target?.data.target ?? null),
71
+ onDragEnd: ({ canceled, operation }) => {
72
+ if (canceled) game.drag?.cancel();
73
+ else {
74
+ game.drag?.setDropTarget(operation.target?.data.target ?? null);
75
+ game.drag?.drop();
76
+ }
77
+ },
78
+ children: [
79
+ /* @__PURE__ */ jsx(DragLifetime, { game }),
80
+ children
81
+ ]
82
+ }
83
+ );
84
+ }
85
+ function useCardDraggable(game, cardId, options) {
86
+ const id = useId();
87
+ const canDrag = game.drag?.getCanDrag(cardId, options) ?? false;
88
+ const { ref, handleRef, isDragging } = useDraggable({
89
+ id,
90
+ disabled: !canDrag,
91
+ data: { begin: () => game.drag?.begin(cardId, options) ?? false }
92
+ });
93
+ return {
94
+ // Disabled draggables must not mark ordinary nested card controls aria-disabled.
95
+ ref: canDrag ? ref : void 0,
96
+ handleRef,
97
+ isDragging,
98
+ canDrag,
99
+ handleProps: {
100
+ type: "button",
101
+ disabled: !canDrag,
102
+ "aria-label": `Drag ${cardId}`,
103
+ "data-drag-card": cardId,
104
+ style: { touchAction: "none" }
105
+ }
106
+ };
107
+ }
108
+ function useBoardDroppable(target, options) {
109
+ const id = useId();
110
+ const collisionDetector = (input) => {
111
+ const collision = pointerIntersection(input);
112
+ if (!collision) return null;
113
+ return !options?.containsPoint || options.containsPoint(input.dragOperation.position.current) ? collision : null;
114
+ };
115
+ const { ref, isDropTarget } = useDroppable({
116
+ id,
117
+ disabled: target === null,
118
+ data: { target },
119
+ collisionDetector
120
+ });
121
+ return { ref, isDropTarget };
122
+ }
123
+
124
+ // src/react/create-game-hook.tsx
125
+ import {
126
+ createContext,
127
+ useContext,
128
+ useLayoutEffect as useLayoutEffect2,
129
+ useRef,
130
+ useState
131
+ } from "react";
132
+ import { useSelector } from "@tanstack/react-store";
133
+ import { jsx as jsx2 } from "react/jsx-runtime";
134
+ function createGameHook() {
135
+ return function(defaults) {
136
+ const Context = createContext(null);
137
+ function GameProvider({ children, ...overrides }) {
138
+ const [instance, setInstance] = useState(null);
139
+ const lifetime = useRef(null);
140
+ const options = {
141
+ ...defaults,
142
+ ...overrides
143
+ };
144
+ useLayoutEffect2(() => {
145
+ let current = lifetime.current;
146
+ if (!current) {
147
+ current = {
148
+ instance: createGameInstance()(options),
149
+ generation: 0
150
+ };
151
+ lifetime.current = current;
152
+ setInstance(current.instance);
153
+ }
154
+ const owned = current;
155
+ ++owned.generation;
156
+ return () => {
157
+ const generation = ++owned.generation;
158
+ queueMicrotask(() => {
159
+ if (owned.generation !== generation) return;
160
+ owned.instance.dispose();
161
+ if (lifetime.current === owned) lifetime.current = null;
162
+ });
163
+ };
164
+ }, []);
165
+ useLayoutEffect2(() => {
166
+ lifetime.current?.instance.setOptions(options);
167
+ });
168
+ return instance ? /* @__PURE__ */ jsx2(Context.Provider, { value: instance, children: "drag" in instance ? /* @__PURE__ */ jsx2(GameDragProvider, { game: instance, children }) : children }) : null;
169
+ }
170
+ function useInstance() {
171
+ const instance = useContext(Context);
172
+ if (!instance) throw new Error("useGame requires its GameProvider.");
173
+ return instance;
174
+ }
175
+ function useGame(selector, options) {
176
+ const instance = useInstance();
177
+ const selected = useSelector(
178
+ instance.store,
179
+ selector ?? ((snapshot) => snapshot),
180
+ options
181
+ );
182
+ return selector ? selected : instance;
183
+ }
184
+ function Subscribe({
185
+ selector,
186
+ compare,
187
+ children
188
+ }) {
189
+ return children(useGame(selector, { compare }));
190
+ }
191
+ function useCardDrag(cardId, options) {
192
+ const game = useGame();
193
+ return useCardDraggable(
194
+ game,
195
+ cardId,
196
+ options
197
+ );
198
+ }
199
+ function useBoardDrop(target, options) {
200
+ return useBoardDroppable(target, options);
201
+ }
202
+ return { GameProvider, useGame, Subscribe, useCardDrag, useBoardDrop };
203
+ };
204
+ }
205
+ export {
206
+ createGameHook
207
+ };
208
+ //# sourceMappingURL=react.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/react/drag.tsx","../src/react/create-game-hook.tsx"],"sourcesContent":["import { useId, useLayoutEffect, type ReactNode } from \"react\";\nimport {\n DragDropProvider,\n KeyboardSensor,\n PointerSensor,\n useDragDropManager,\n useDraggable,\n useDroppable,\n type UseDroppableInput,\n type DragOverEvent,\n type DragEndEvent,\n} from \"@dnd-kit/react\";\nimport {\n Accessibility,\n defaultPreset,\n PointerActivationConstraints,\n} from \"@dnd-kit/dom\";\nimport { pointerIntersection } from \"@dnd-kit/collision\";\nimport type { CoreInstance } from \"../headless/model.js\";\nimport type { DragController } from \"../headless/features/drag.js\";\nimport type { DropTarget, TargetOptions } from \"../headless/targets.js\";\n\n/** Browser-only hit testing receives client pixels; SVG renderers invert their screen CTM. */\nexport interface BoardDropOptions {\n containsPoint?(point: { x: number; y: number }): boolean;\n}\nexport type DragGame = CoreInstance<unknown> & {\n readonly drag?: DragController<unknown>;\n};\ntype DragData = { begin?: () => boolean; target?: DropTarget<unknown> | null };\nconst sensors = [\n PointerSensor.configure({\n activationConstraints: [\n new PointerActivationConstraints.Distance({ value: 5 }),\n ],\n }),\n KeyboardSensor,\n];\n\nconst plugins = defaultPreset.plugins.map((plugin) =>\n plugin === Accessibility\n ? Accessibility.configure({\n announcements: {\n dragstart: () =>\n \"Card picked up. Use arrow keys to move and Space or Enter to drop. Escape cancels.\",\n dragover: ({ operation }: DragOverEvent) => {\n const element = operation.target?.element;\n if (!element) return \"No drop destination.\";\n const label =\n element.getAttribute(\"aria-label\") ?? \"board destination\";\n const board = element.getAttribute(\"data-board\");\n return `Over ${label}${board ? ` on ${board}` : \"\"}.`;\n },\n dragend: ({ canceled, operation }: DragEndEvent) =>\n canceled\n ? \"Drag cancelled.\"\n : operation.target\n ? \"Card placement selected.\"\n : \"No placement selected.\",\n },\n })\n : plugin,\n);\n\nfunction DragLifetime({ game }: { game: DragGame }) {\n const manager = useDragDropManager();\n useLayoutEffect(\n () =>\n game.subscribe(() => {\n if (\n !game.drag?.active &&\n manager &&\n !manager.dragOperation.status.idle &&\n !manager.dragOperation.status.dropped\n )\n manager.actions.stop({ canceled: true });\n }),\n [game, manager],\n );\n return null;\n}\n/** Installed by GameProvider when dragFeature is enabled. */\nexport function GameDragProvider({\n game,\n children,\n}: {\n game: DragGame;\n children?: ReactNode;\n}) {\n return (\n <DragDropProvider<DragData>\n sensors={sensors}\n plugins={plugins}\n onBeforeDragStart={(event) => {\n if (!event.operation.source?.data.begin?.()) event.preventDefault();\n }}\n onDragOver={({ operation }) =>\n game.drag?.setDropTarget(operation.target?.data.target ?? null)\n }\n onDragEnd={({ canceled, operation }) => {\n if (canceled) game.drag?.cancel();\n else {\n game.drag?.setDropTarget(operation.target?.data.target ?? null);\n game.drag?.drop();\n }\n }}\n >\n <DragLifetime game={game} />\n {children}\n </DragDropProvider>\n );\n}\n\n/** Bound by createGameHook so card and interaction IDs use the authored game. */\nexport function useCardDraggable(\n game: DragGame,\n cardId: string,\n options?: TargetOptions<unknown>,\n) {\n const id = useId();\n const canDrag = game.drag?.getCanDrag(cardId, options) ?? false;\n const { ref, handleRef, isDragging } = useDraggable<DragData>({\n id,\n disabled: !canDrag,\n data: { begin: () => game.drag?.begin(cardId, options) ?? false },\n });\n return {\n // Disabled draggables must not mark ordinary nested card controls aria-disabled.\n ref: canDrag ? ref : undefined,\n handleRef,\n isDragging,\n canDrag,\n handleProps: {\n type: \"button\" as const,\n disabled: !canDrag,\n \"aria-label\": `Drag ${cardId}`,\n \"data-drag-card\": cardId,\n style: { touchAction: \"none\" as const },\n },\n };\n}\n\n/** Targets retain the exact interaction, card-input and destination-input route. */\nexport function useBoardDroppable(\n target: DropTarget<unknown> | null,\n options?: BoardDropOptions,\n) {\n const id = useId();\n const collisionDetector: UseDroppableInput[\"collisionDetector\"] = (input) => {\n const collision = pointerIntersection(input);\n if (!collision) return null;\n return !options?.containsPoint ||\n options.containsPoint(input.dragOperation.position.current)\n ? collision\n : null;\n };\n const { ref, isDropTarget } = useDroppable<DragData>({\n id,\n disabled: target === null,\n data: { target },\n collisionDetector,\n });\n return { ref, isDropTarget };\n}\n","import {\n GameDragProvider,\n useCardDraggable,\n useBoardDroppable,\n type BoardDropOptions,\n type DragGame,\n} from \"./drag.js\";\nimport type { IdOf } from \"../headless/model.js\";\nimport type { DropTarget, TargetOptions } from \"../headless/targets.js\";\nimport {\n createContext,\n useContext,\n useLayoutEffect,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\nimport { useSelector } from \"@tanstack/react-store\";\nimport { createGameInstance } from \"../headless/instance.js\";\nimport type {\n CoreInstance,\n FeatureContext,\n Features,\n GameInstance,\n GameSnapshot,\n GameSource,\n InstanceOptions,\n} from \"../headless/model.js\";\n\nexport interface SelectionOptions<Value> {\n readonly compare?: (previous: Value, next: Value) => boolean;\n}\n\n/** Bind erased game types and features; each provider owns its source lifetime. */\nexport function createGameHook<Game, Source extends GameSource = GameSource>() {\n return function <const Enabled extends Features = Record<never, never>>(\n defaults: Omit<InstanceOptions<Game, Source>, \"source\"> & {\n features?: (\n core: CoreInstance<Game>,\n context: FeatureContext<Game>,\n ) => Enabled;\n },\n ) {\n type Instance = GameInstance<Game, Enabled, Source>;\n type Snapshot = GameSnapshot<Game, Enabled>;\n type ProviderProps = Partial<\n Omit<InstanceOptions<Game, Source>, \"source\">\n > & {\n source: Source;\n children?: ReactNode;\n };\n const Context = createContext<Instance | null>(null);\n\n /** Owns the source. Do not share it between independently mounted providers. */\n function GameProvider({ children, ...overrides }: ProviderProps) {\n const [instance, setInstance] = useState<Instance | null>(null);\n const lifetime = useRef<{\n instance: Instance;\n generation: number;\n } | null>(null);\n const options = {\n ...defaults,\n ...overrides,\n };\n useLayoutEffect(() => {\n let current = lifetime.current;\n if (!current) {\n current = {\n instance: createGameInstance<Game>()(options),\n generation: 0,\n };\n lifetime.current = current;\n setInstance(current.instance);\n }\n const owned = current;\n ++owned.generation;\n return () => {\n const generation = ++owned.generation;\n queueMicrotask(() => {\n if (owned.generation !== generation) return;\n owned.instance.dispose();\n if (lifetime.current === owned) lifetime.current = null;\n });\n };\n // Construction belongs to this committed provider lifetime. Current\n // options are installed separately without recreating features.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n useLayoutEffect(() => {\n lifetime.current?.instance.setOptions(options);\n });\n return instance ? (\n <Context.Provider value={instance}>\n {\"drag\" in instance ? (\n <GameDragProvider game={instance as unknown as DragGame}>\n {children}\n </GameDragProvider>\n ) : (\n children\n )}\n </Context.Provider>\n ) : null;\n }\n\n function useInstance(): Instance {\n const instance = useContext(Context);\n if (!instance) throw new Error(\"useGame requires its GameProvider.\");\n return instance;\n }\n\n function useGame(): Instance;\n function useGame<Value>(\n selector: (snapshot: Snapshot) => Value,\n options?: SelectionOptions<Value>,\n ): Value;\n function useGame<Value>(\n selector?: (snapshot: Snapshot) => Value,\n options?: SelectionOptions<Value>,\n ): Instance | Value {\n const instance = useInstance();\n const selected = useSelector(\n instance.store,\n selector ?? ((snapshot) => snapshot as unknown as Value),\n options,\n );\n return selector ? selected : instance;\n }\n\n function Subscribe<Value>({\n selector,\n compare,\n children,\n }: {\n selector: (snapshot: Snapshot) => Value;\n compare?: SelectionOptions<Value>[\"compare\"];\n children: (value: Value) => ReactNode;\n }) {\n return children(useGame(selector, { compare }));\n }\n\n function useCardDrag(\n cardId: IdOf<Game, \"cardId\">,\n options?: TargetOptions<Game>,\n ) {\n const game = useGame();\n return useCardDraggable(\n game as unknown as DragGame,\n cardId,\n options as TargetOptions<unknown> | undefined,\n );\n }\n function useBoardDrop(\n target: DropTarget<Game> | null,\n options?: BoardDropOptions,\n ) {\n return useBoardDroppable(target as DropTarget<unknown> | null, options);\n }\n return { GameProvider, useGame, Subscribe, useCardDrag, useBoardDrop };\n };\n}\n"],"mappings":"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","names":["useLayoutEffect","jsx","useLayoutEffect"]}