@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.
- package/README.md +186 -45
- package/dist/board-target-B0QG6OYx.d.ts +8 -0
- package/dist/chunk-3JXUL4VN.js +139 -0
- package/dist/chunk-3JXUL4VN.js.map +1 -0
- package/dist/{chunk-S2JEDVWH.js → chunk-C77SYIVT.js} +6102 -6505
- package/dist/chunk-C77SYIVT.js.map +1 -0
- package/dist/chunk-DMFVAZI2.js +16 -0
- package/dist/chunk-DMFVAZI2.js.map +1 -0
- package/dist/chunk-OOFMBCFF.js +1338 -0
- package/dist/chunk-OOFMBCFF.js.map +1 -0
- package/dist/chunk-UG2Q4NLG.js +590 -0
- package/dist/chunk-UG2Q4NLG.js.map +1 -0
- package/dist/chunk-XU6WJW6K.js +847 -0
- package/dist/chunk-XU6WJW6K.js.map +1 -0
- package/dist/diagnostics-B8d5aLad.d.ts +65 -0
- package/dist/extract-gVKcB8Og.d.ts +4736 -0
- package/dist/index.d.ts +3782 -1
- package/dist/index.js +1096 -6
- package/dist/index.js.map +1 -1
- package/dist/model-vdpLHrxh.d.ts +432 -0
- package/dist/react.d.ts +60 -0
- package/dist/react.js +208 -0
- package/dist/react.js.map +1 -0
- package/dist/reducer.d.ts +1244 -78
- package/dist/reducer.js +4204 -121
- package/dist/reducer.js.map +1 -1
- package/dist/testing.d.ts +520 -564
- package/dist/testing.js +2451 -915
- package/dist/testing.js.map +1 -1
- package/dist/types-Bj5ZLTUV.d.ts +114 -0
- package/package.json +40 -142
- package/dist/HandView-Bi2lxzQP.d.ts +0 -79
- package/dist/ResourceCounter-BFxJknqp.d.ts +0 -98
- package/dist/ThemeProvider-CElaL2ik.d.ts +0 -99
- package/dist/attributes-DbvyMbXw.d.ts +0 -68
- package/dist/browser-interaction.d.ts +0 -910
- package/dist/browser-interaction.js +0 -119
- package/dist/browser-interaction.js.map +0 -1
- package/dist/chunk-32PFKDV7.js +0 -11705
- package/dist/chunk-32PFKDV7.js.map +0 -1
- package/dist/chunk-3BAD4VC2.js +0 -263
- package/dist/chunk-3BAD4VC2.js.map +0 -1
- package/dist/chunk-3SKDNDPG.js +0 -104
- package/dist/chunk-3SKDNDPG.js.map +0 -1
- package/dist/chunk-5YV2WHJ4.js +0 -25
- package/dist/chunk-5YV2WHJ4.js.map +0 -1
- package/dist/chunk-ANRB4XKA.js +0 -90
- package/dist/chunk-ANRB4XKA.js.map +0 -1
- package/dist/chunk-BI4G3PM2.js +0 -1864
- package/dist/chunk-BI4G3PM2.js.map +0 -1
- package/dist/chunk-EVCLTY4Q.js +0 -935
- package/dist/chunk-EVCLTY4Q.js.map +0 -1
- package/dist/chunk-GDPKTBUR.js +0 -564
- package/dist/chunk-GDPKTBUR.js.map +0 -1
- package/dist/chunk-H6VDGFL5.js +0 -2903
- package/dist/chunk-H6VDGFL5.js.map +0 -1
- package/dist/chunk-LR3ZTWQF.js +0 -5960
- package/dist/chunk-LR3ZTWQF.js.map +0 -1
- package/dist/chunk-PF7L4BMG.js +0 -117
- package/dist/chunk-PF7L4BMG.js.map +0 -1
- package/dist/chunk-PN5O6GG2.js +0 -32
- package/dist/chunk-PN5O6GG2.js.map +0 -1
- package/dist/chunk-PZ5AY32C.js +0 -10
- package/dist/chunk-PZ5AY32C.js.map +0 -1
- package/dist/chunk-S2JEDVWH.js.map +0 -1
- package/dist/chunk-T3ZKNUZ7.js +0 -1
- package/dist/chunk-T3ZKNUZ7.js.map +0 -1
- package/dist/chunk-UGH54WZ2.js +0 -223
- package/dist/chunk-UGH54WZ2.js.map +0 -1
- package/dist/chunk-VCDWP5VL.js +0 -746
- package/dist/chunk-VCDWP5VL.js.map +0 -1
- package/dist/chunk-VDXOF4FW.js +0 -69
- package/dist/chunk-VDXOF4FW.js.map +0 -1
- package/dist/chunk-WHR5UW3F.js +0 -1988
- package/dist/chunk-WHR5UW3F.js.map +0 -1
- package/dist/chunk-Y75CFE77.js +0 -17
- package/dist/chunk-Y75CFE77.js.map +0 -1
- package/dist/chunk-Z7QCREWI.js +0 -3435
- package/dist/chunk-Z7QCREWI.js.map +0 -1
- package/dist/chunk-ZZGN2ATS.js +0 -1941
- package/dist/chunk-ZZGN2ATS.js.map +0 -1
- package/dist/components-gAbIz7hK.d.ts +0 -1284
- package/dist/definitions-BgmV_GhC.d.ts +0 -300
- package/dist/diagnostics-DIi3ina7.d.ts +0 -50
- package/dist/digest.d.ts +0 -8
- package/dist/game-CsZScpcn.d.ts +0 -763
- package/dist/hex-board-view-aKkblDp6.d.ts +0 -1228
- package/dist/index-CcT9Q7N7.d.ts +0 -193
- package/dist/index.d-DjzoK7zn.d.ts +0 -2101
- package/dist/package-set.d.ts +0 -13
- package/dist/package-set.js +0 -12
- package/dist/package-set.js.map +0 -1
- package/dist/player-state-Cqpyeql0.d.ts +0 -371
- package/dist/plugin-runtime-contract.d.ts +0 -17
- package/dist/plugin-runtime-contract.js +0 -92
- package/dist/plugin-runtime-contract.js.map +0 -1
- package/dist/primitive-props-BNHDkgd7.d.ts +0 -16
- package/dist/protocol-dYgafTYY.d.ts +0 -314
- package/dist/reducer/advanced.d.ts +0 -82
- package/dist/reducer/advanced.js +0 -51
- package/dist/reducer/advanced.js.map +0 -1
- package/dist/reducer-contract.d.ts +0 -11
- package/dist/reducer-contract.js +0 -16
- package/dist/reducer-contract.js.map +0 -1
- package/dist/reference-games/index.d.ts +0 -31
- package/dist/reference-games/index.js +0 -48
- package/dist/reference-games/index.js.map +0 -1
- package/dist/runtime/primitives.d.ts +0 -250
- package/dist/runtime/primitives.js +0 -189
- package/dist/runtime/primitives.js.map +0 -1
- package/dist/runtime/runtime-api.d.ts +0 -2
- package/dist/runtime/runtime-api.js +0 -1
- package/dist/runtime/runtime-api.js.map +0 -1
- package/dist/runtime/workspace-contract.d.ts +0 -392
- package/dist/runtime/workspace-contract.js +0 -28
- package/dist/runtime/workspace-contract.js.map +0 -1
- package/dist/runtime-RJ5orDJU.d.ts +0 -1699
- package/dist/runtime-api-Bz5pwNU_.d.ts +0 -296
- package/dist/runtime-json-CQ9QbLZ5.d.ts +0 -5
- package/dist/runtime.d.ts +0 -83
- package/dist/runtime.js +0 -242
- package/dist/runtime.js.map +0 -1
- package/dist/schema.d.ts +0 -379
- package/dist/stale-contract-artifact-error-XLaweZtF.d.ts +0 -18
- package/dist/testing-compiler.d.ts +0 -37
- package/dist/testing-compiler.js +0 -272
- package/dist/testing-compiler.js.map +0 -1
- package/dist/testing-runtime.d.ts +0 -119
- package/dist/testing-runtime.js +0 -212
- package/dist/testing-runtime.js.map +0 -1
- package/dist/types-DJj5MJkl.d.ts +0 -256
- package/dist/types-DR7DoB1x.d.ts +0 -28
- package/dist/types-JWCYHmu7.d.ts +0 -122
- package/dist/types.d.ts +0 -1958
- package/dist/types.js +0 -14
- package/dist/types.js.map +0 -1
- package/dist/ui/components.d.ts +0 -17
- package/dist/ui/components.js +0 -216
- package/dist/ui/components.js.map +0 -1
- package/dist/ui/defaults.d.ts +0 -19
- package/dist/ui/defaults.js +0 -104
- package/dist/ui/defaults.js.map +0 -1
- package/dist/ui/player-state.d.ts +0 -2
- package/dist/ui/player-state.js +0 -1
- package/dist/ui/player-state.js.map +0 -1
- package/dist/ui/plugin-styles.css +0 -2
- package/dist/ui-contract-GTUPkf8z.d.ts +0 -1168
- package/dist/ui.d.ts +0 -317
- package/dist/ui.js +0 -277
- package/dist/ui.js.map +0 -1
- package/dist/views-BpuWcOyN.d.ts +0 -1641
package/dist/react.d.ts
ADDED
|
@@ -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"]}
|