@dreamboard-games/sdk 0.5.0-alpha.11 → 0.5.0-alpha.13
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 +68 -1
- package/dist/{chunk-2IGXAYW4.js → chunk-W5G6VMMQ.js} +18 -15
- package/dist/chunk-W5G6VMMQ.js.map +1 -0
- package/dist/{chunk-D3F3GCMF.js → chunk-YUEMDRIH.js} +14 -4
- package/dist/chunk-YUEMDRIH.js.map +1 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +100 -3
- package/dist/index.js.map +1 -1
- package/dist/react.d.ts +22 -6
- package/dist/react.js +282 -33
- package/dist/react.js.map +1 -1
- package/dist/{model-BeslAXVA.d.ts → shortcuts-Chlx8o7p.d.ts} +93 -8
- package/dist/testing.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-2IGXAYW4.js.map +0 -1
- package/dist/chunk-D3F3GCMF.js.map +0 -1
package/dist/react.d.ts
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { PointerEvent, MouseEvent, DragEvent, CSSProperties, ReactNode } from 'react';
|
|
3
|
-
import {
|
|
2
|
+
import { PointerEvent, FocusEvent, MouseEvent, DragEvent, CSSProperties, ReactNode } from 'react';
|
|
3
|
+
import { aa as TargetOptions, a1 as SeatCardId, q as Features, E as InstanceOptions, n as CoreInstance, p as FeatureContext, G as GameInstance, r as GameSnapshot, a5 as ShortcutOptions, a6 as ShortcutTarget, a4 as ShortcutHint, o as DropTarget } from './shortcuts-Chlx8o7p.js';
|
|
4
4
|
import { G as GameSource } from './types-Xg_RxV1n.js';
|
|
5
|
+
import '@tanstack/store';
|
|
5
6
|
import './extract-DgzrJ5el.js';
|
|
6
7
|
import 'zod';
|
|
7
8
|
import 'honeycomb-grid';
|
|
8
|
-
import '@tanstack/store';
|
|
9
9
|
|
|
10
10
|
interface CardGestureProps {
|
|
11
11
|
onPointerDown(event: PointerEvent<Element>): void;
|
|
12
|
-
|
|
13
|
-
onPointerLeave(event: PointerEvent<Element>): void;
|
|
14
|
-
onFocus(): void;
|
|
12
|
+
onFocus(event: FocusEvent<Element>): void;
|
|
15
13
|
onBlur(): void;
|
|
16
14
|
onContextMenu(event: MouseEvent<Element>): void;
|
|
17
15
|
onDragStart(event: DragEvent<Element>): void;
|
|
18
16
|
readonly style: CSSProperties;
|
|
19
17
|
readonly "data-dragging"?: true;
|
|
20
18
|
readonly "data-inspecting"?: "hold" | "hover";
|
|
19
|
+
readonly "data-card-gesture": string;
|
|
20
|
+
readonly "data-gesture-session": string;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
interface SelectionOptions<Value> {
|
|
@@ -36,6 +36,7 @@ interface CardGesture {
|
|
|
36
36
|
readonly props: CardGestureProps;
|
|
37
37
|
readonly canDrag: boolean;
|
|
38
38
|
readonly isDragging: boolean;
|
|
39
|
+
readonly isActive: boolean;
|
|
39
40
|
/** Held on touch, or rested on with a mouse. */
|
|
40
41
|
readonly inspecting: "hold" | "hover" | null;
|
|
41
42
|
}
|
|
@@ -73,6 +74,21 @@ declare function createGameHook<Game, Source extends GameSource = GameSource>():
|
|
|
73
74
|
children: (value: Value) => ReactNode;
|
|
74
75
|
}) => ReactNode;
|
|
75
76
|
useCardGesture: (cardId: SeatCardId<Game>, options: CardGestureOptions<Game>) => CardGesture;
|
|
77
|
+
useActiveCard: () => SeatCardId<Game> | null;
|
|
78
|
+
useGameShortcuts: (options: ShortcutOptions<Game>) => {
|
|
79
|
+
readonly target: ShortcutTarget<Game> | null;
|
|
80
|
+
readonly hints: readonly ShortcutHint<Game>[];
|
|
81
|
+
};
|
|
82
|
+
useShortcutTarget: (target: ShortcutTarget<Game>) => {
|
|
83
|
+
props: {
|
|
84
|
+
"data-shortcut-target": string;
|
|
85
|
+
"data-gesture-session": string;
|
|
86
|
+
onPointerDown(event: react.PointerEvent<Element>): void;
|
|
87
|
+
onFocus(event: react.FocusEvent<Element>): void;
|
|
88
|
+
onBlur(): void;
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
useShortcutHints: (target: ShortcutTarget<Game> | null) => readonly ShortcutHint<Game>[];
|
|
76
92
|
useDropArea: (binding: DropTarget<Game> | DropAreaBinding<Game> | null) => DropArea;
|
|
77
93
|
useDragOverlay: () => DragOverlay<Game> | null;
|
|
78
94
|
};
|
package/dist/react.js
CHANGED
|
@@ -3,8 +3,76 @@ import {
|
|
|
3
3
|
createGestureRecognizer,
|
|
4
4
|
isSameDropTarget,
|
|
5
5
|
magneticDropPoint
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import
|
|
6
|
+
} from "./chunk-W5G6VMMQ.js";
|
|
7
|
+
import {
|
|
8
|
+
inputValueKey
|
|
9
|
+
} from "./chunk-ES74C2IN.js";
|
|
10
|
+
|
|
11
|
+
// src/react/create-game-hook.tsx
|
|
12
|
+
import { createStore as createStore2 } from "@tanstack/store";
|
|
13
|
+
|
|
14
|
+
// src/react/shortcuts.ts
|
|
15
|
+
import { useLayoutEffect, useRef } from "react";
|
|
16
|
+
function canHandle(event) {
|
|
17
|
+
const target = event.target;
|
|
18
|
+
return !event.defaultPrevented && !event.repeat && !event.isComposing && !event.altKey && !event.ctrlKey && !event.metaKey && !event.shiftKey && !(target instanceof Element && target.closest(
|
|
19
|
+
"input, textarea, select, [contenteditable]:not([contenteditable='false']), [role='textbox']"
|
|
20
|
+
)) && !Array.from(
|
|
21
|
+
document.querySelectorAll(
|
|
22
|
+
"[aria-modal='true'], dialog[open], [role='dialog'], [role='menu'], [role='listbox']"
|
|
23
|
+
)
|
|
24
|
+
).some(
|
|
25
|
+
(surface) => !surface.closest("[aria-hidden='true']") && surface.checkVisibility({
|
|
26
|
+
visibilityProperty: true,
|
|
27
|
+
opacityProperty: true
|
|
28
|
+
})
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
function useShortcutsAdapter(game, controller, session, options, onResult) {
|
|
32
|
+
const latest = useRef({ options, onResult });
|
|
33
|
+
const registration = useRef(null);
|
|
34
|
+
useLayoutEffect(() => {
|
|
35
|
+
latest.current = { options, onResult };
|
|
36
|
+
});
|
|
37
|
+
useLayoutEffect(() => {
|
|
38
|
+
let mounted = true;
|
|
39
|
+
const owned = controller.register(latest.current.options);
|
|
40
|
+
registration.current = owned;
|
|
41
|
+
function keydown(event) {
|
|
42
|
+
if (!canHandle(event)) return;
|
|
43
|
+
const source = game.getOptions().source;
|
|
44
|
+
const seat = game.snapshot?.me;
|
|
45
|
+
const active = () => mounted && game.getOptions().source === source && game.snapshot?.me === seat;
|
|
46
|
+
const pending = controller.handle(
|
|
47
|
+
event.key,
|
|
48
|
+
session.getActiveTarget()
|
|
49
|
+
);
|
|
50
|
+
if (!pending) return;
|
|
51
|
+
event.preventDefault();
|
|
52
|
+
void pending.then(
|
|
53
|
+
(result) => {
|
|
54
|
+
if (active()) latest.current.onResult(result);
|
|
55
|
+
},
|
|
56
|
+
(cause) => {
|
|
57
|
+
if (active())
|
|
58
|
+
latest.current.onResult(
|
|
59
|
+
cause instanceof Error ? cause : new Error(String(cause))
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
window.addEventListener("keydown", keydown);
|
|
65
|
+
return () => {
|
|
66
|
+
mounted = false;
|
|
67
|
+
window.removeEventListener("keydown", keydown);
|
|
68
|
+
registration.current = null;
|
|
69
|
+
owned.dispose();
|
|
70
|
+
};
|
|
71
|
+
}, [game, controller, session]);
|
|
72
|
+
useLayoutEffect(() => {
|
|
73
|
+
registration.current?.update(options);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
8
76
|
|
|
9
77
|
// src/react/gesture.tsx
|
|
10
78
|
import { createStore } from "@tanstack/store";
|
|
@@ -38,11 +106,18 @@ var sameDropTarget = (left, right) => left === right || !!left && !!right && isS
|
|
|
38
106
|
var sessions = 0;
|
|
39
107
|
function createGestureSession(game) {
|
|
40
108
|
const id = `g${++sessions}`;
|
|
41
|
-
const store = createStore({
|
|
109
|
+
const store = createStore({
|
|
110
|
+
activeTarget: null,
|
|
111
|
+
drag: null,
|
|
112
|
+
inspect: null
|
|
113
|
+
});
|
|
114
|
+
const targets = /* @__PURE__ */ new Map();
|
|
115
|
+
const sameTarget = (left, right) => inputValueKey(left) === inputValueKey(right);
|
|
42
116
|
const areas = /* @__PURE__ */ new Map();
|
|
43
117
|
const followers = /* @__PURE__ */ new Set();
|
|
44
118
|
let press = null;
|
|
45
119
|
let hover = null;
|
|
120
|
+
let hoverPoint = null;
|
|
46
121
|
let focused = null;
|
|
47
122
|
let alt = false;
|
|
48
123
|
let pointer = { x: 0, y: 0 };
|
|
@@ -51,12 +126,34 @@ function createGestureSession(game) {
|
|
|
51
126
|
let disposed = false;
|
|
52
127
|
const set = (patch) => store.setState((previous) => ({ ...previous, ...patch }));
|
|
53
128
|
let snapshot = game.snapshot;
|
|
129
|
+
let source = game.getOptions().source;
|
|
130
|
+
function admitted(target) {
|
|
131
|
+
if (!target || !game.snapshot) return null;
|
|
132
|
+
const zones = game.snapshot.frame.zones;
|
|
133
|
+
if (target.kind === "zone")
|
|
134
|
+
return zones[target.zoneId]?.[target.hostId] ? target : null;
|
|
135
|
+
if (target.kind === "card")
|
|
136
|
+
return Object.entries(snapshot?.frame.zones ?? {}).some(
|
|
137
|
+
([zoneId, hosts]) => Object.entries(hosts).some(
|
|
138
|
+
([hostId, zone]) => zone.cardIds.includes(target.value) && zones[zoneId]?.[hostId]?.cardIds.includes(target.value)
|
|
139
|
+
)
|
|
140
|
+
) ? target : null;
|
|
141
|
+
return [...targets.values()].some((read) => sameTarget(read(), target)) ? target : null;
|
|
142
|
+
}
|
|
54
143
|
const unsubscribe = game.subscribe(() => {
|
|
55
|
-
|
|
144
|
+
const nextSource = game.getOptions().source;
|
|
145
|
+
const changedLifetime = source !== nextSource || snapshot?.me !== game.snapshot?.me;
|
|
146
|
+
if (changedLifetime || snapshot !== game.snapshot) {
|
|
147
|
+
const nextHover = changedLifetime ? null : admitted(hover);
|
|
148
|
+
const nextFocused = changedLifetime ? null : admitted(focused);
|
|
149
|
+
source = nextSource;
|
|
56
150
|
snapshot = game.snapshot;
|
|
57
151
|
press?.recognizer.cancel();
|
|
58
152
|
alt = false;
|
|
59
|
-
|
|
153
|
+
hover = nextHover;
|
|
154
|
+
focused = nextFocused;
|
|
155
|
+
if (changedLifetime) hoverPoint = null;
|
|
156
|
+
set({ activeTarget: hover ?? focused, inspect: null });
|
|
60
157
|
}
|
|
61
158
|
if (press?.dragging && !game.drag?.active) press.recognizer.cancel();
|
|
62
159
|
const drag = store.get().drag;
|
|
@@ -132,9 +229,33 @@ function createGestureSession(game) {
|
|
|
132
229
|
hover = null;
|
|
133
230
|
}
|
|
134
231
|
function inspectWithAlt() {
|
|
135
|
-
const
|
|
136
|
-
|
|
232
|
+
const target = hover ?? focused;
|
|
233
|
+
const cardId = target?.kind === "card" ? target.value : null;
|
|
234
|
+
set({
|
|
235
|
+
activeTarget: target,
|
|
236
|
+
inspect: alt && cardId && !press ? { cardId, via: "hover" } : null
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
function hoverAt(at) {
|
|
240
|
+
hoverPoint = at;
|
|
241
|
+
const wasFocused = focused !== null;
|
|
242
|
+
focused = null;
|
|
243
|
+
const hit = document.elementFromPoint(at.x, at.y)?.closest("[data-card-gesture], [data-shortcut-target]");
|
|
244
|
+
const control = hit?.getAttribute("data-gesture-session") === id ? hit : null;
|
|
245
|
+
const targetId = control?.getAttribute("data-shortcut-target");
|
|
246
|
+
const cardId = control?.getAttribute("data-card-gesture");
|
|
247
|
+
const next = targetId ? targets.get(targetId)?.() ?? null : cardId ? { kind: "card", value: cardId } : null;
|
|
248
|
+
if (sameTarget(hover, next) && !wasFocused) return;
|
|
249
|
+
hover = next;
|
|
250
|
+
inspectWithAlt();
|
|
137
251
|
}
|
|
252
|
+
const browse = (event) => {
|
|
253
|
+
if (event.pointerType !== "mouse" || press) return;
|
|
254
|
+
if (hoverPoint?.x === event.clientX && hoverPoint.y === event.clientY)
|
|
255
|
+
return;
|
|
256
|
+
alt = event.altKey;
|
|
257
|
+
hoverAt({ x: event.clientX, y: event.clientY });
|
|
258
|
+
};
|
|
138
259
|
const key = (event) => {
|
|
139
260
|
if (event.key === "Escape") {
|
|
140
261
|
press?.recognizer.cancel();
|
|
@@ -148,12 +269,23 @@ function createGestureSession(game) {
|
|
|
148
269
|
const loseFocus = () => {
|
|
149
270
|
alt = false;
|
|
150
271
|
clearHover();
|
|
272
|
+
hoverPoint = null;
|
|
151
273
|
focused = null;
|
|
152
|
-
set({ inspect: null });
|
|
274
|
+
set({ activeTarget: null, inspect: null });
|
|
153
275
|
};
|
|
154
276
|
addEventListener("keydown", key);
|
|
155
277
|
addEventListener("keyup", key);
|
|
156
278
|
addEventListener("blur", loseFocus);
|
|
279
|
+
const leaveWindow = (event) => {
|
|
280
|
+
if (event.pointerType !== "mouse" || event.relatedTarget !== null) return;
|
|
281
|
+
clearHover();
|
|
282
|
+
hoverPoint = null;
|
|
283
|
+
focused = null;
|
|
284
|
+
alt = false;
|
|
285
|
+
inspectWithAlt();
|
|
286
|
+
};
|
|
287
|
+
addEventListener("pointermove", browse);
|
|
288
|
+
addEventListener("pointerout", leaveWindow);
|
|
157
289
|
function release() {
|
|
158
290
|
press?.detach();
|
|
159
291
|
press = null;
|
|
@@ -169,6 +301,7 @@ function createGestureSession(game) {
|
|
|
169
301
|
return false;
|
|
170
302
|
const box = element.getBoundingClientRect();
|
|
171
303
|
clearHover();
|
|
304
|
+
focused = null;
|
|
172
305
|
press.dragging = true;
|
|
173
306
|
set({
|
|
174
307
|
drag: {
|
|
@@ -177,7 +310,8 @@ function createGestureSession(game) {
|
|
|
177
310
|
size: { width: box.width, height: box.height },
|
|
178
311
|
settling: false
|
|
179
312
|
},
|
|
180
|
-
inspect: null
|
|
313
|
+
inspect: null,
|
|
314
|
+
activeTarget: null
|
|
181
315
|
});
|
|
182
316
|
return true;
|
|
183
317
|
},
|
|
@@ -187,11 +321,15 @@ function createGestureSession(game) {
|
|
|
187
321
|
},
|
|
188
322
|
end(kind, at) {
|
|
189
323
|
const dragging = press.dragging;
|
|
324
|
+
const pointerType = press.pointerType;
|
|
190
325
|
if (dragging) retarget(at);
|
|
191
326
|
release();
|
|
192
327
|
if (kind !== "tap") swallowNextClick();
|
|
193
328
|
if (kind === "hold") set({ inspect: null });
|
|
194
|
-
if (!dragging)
|
|
329
|
+
if (!dragging) {
|
|
330
|
+
if (kind === "browse" && pointerType === "mouse") hoverAt(at);
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
195
333
|
const before = game.request;
|
|
196
334
|
game.drag?.drop();
|
|
197
335
|
const drag = store.get().drag;
|
|
@@ -240,24 +378,22 @@ function createGestureSession(game) {
|
|
|
240
378
|
}
|
|
241
379
|
function cardProps(cardId, options, flags) {
|
|
242
380
|
return {
|
|
243
|
-
onPointerDown
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
},
|
|
251
|
-
onPointerLeave() {
|
|
252
|
-
if (hover === cardId) clearHover();
|
|
253
|
-
inspectWithAlt();
|
|
381
|
+
onPointerDown(event) {
|
|
382
|
+
if (event.pointerType === "mouse") {
|
|
383
|
+
hover = { kind: "card", value: cardId };
|
|
384
|
+
alt = event.altKey;
|
|
385
|
+
inspectWithAlt();
|
|
386
|
+
}
|
|
387
|
+
start(event.nativeEvent, cardId, event.currentTarget, options);
|
|
254
388
|
},
|
|
255
|
-
onFocus() {
|
|
256
|
-
focused = cardId;
|
|
389
|
+
onFocus(event) {
|
|
390
|
+
focused = event.currentTarget.matches(":focus-visible") ? { kind: "card", value: cardId } : null;
|
|
391
|
+
if (focused) hover = null;
|
|
257
392
|
inspectWithAlt();
|
|
258
393
|
},
|
|
259
394
|
onBlur() {
|
|
260
|
-
if (focused ===
|
|
395
|
+
if (focused?.kind === "card" && focused.value === cardId)
|
|
396
|
+
focused = null;
|
|
261
397
|
inspectWithAlt();
|
|
262
398
|
},
|
|
263
399
|
onContextMenu(event) {
|
|
@@ -267,7 +403,9 @@ function createGestureSession(game) {
|
|
|
267
403
|
onDragStart: (event) => event.preventDefault(),
|
|
268
404
|
style: options === false ? INSPECTION_STYLE : CARD_STYLE,
|
|
269
405
|
"data-dragging": flags.dragging || void 0,
|
|
270
|
-
"data-inspecting": flags.inspecting ?? void 0
|
|
406
|
+
"data-inspecting": flags.inspecting ?? void 0,
|
|
407
|
+
"data-card-gesture": cardId,
|
|
408
|
+
"data-gesture-session": id
|
|
271
409
|
};
|
|
272
410
|
}
|
|
273
411
|
function overlayRef(element) {
|
|
@@ -295,6 +433,43 @@ function createGestureSession(game) {
|
|
|
295
433
|
id,
|
|
296
434
|
store,
|
|
297
435
|
cardProps,
|
|
436
|
+
getActiveCardId() {
|
|
437
|
+
const target = store.get().activeTarget;
|
|
438
|
+
return target?.kind === "card" ? target.value : null;
|
|
439
|
+
},
|
|
440
|
+
getActiveTarget: () => store.get().activeTarget,
|
|
441
|
+
registerTarget(targetId, read) {
|
|
442
|
+
const target = read();
|
|
443
|
+
targets.set(targetId, read);
|
|
444
|
+
return () => {
|
|
445
|
+
targets.delete(targetId);
|
|
446
|
+
if (sameTarget(hover, target)) hover = null;
|
|
447
|
+
if (sameTarget(focused, target)) focused = null;
|
|
448
|
+
inspectWithAlt();
|
|
449
|
+
};
|
|
450
|
+
},
|
|
451
|
+
targetProps(targetId) {
|
|
452
|
+
return {
|
|
453
|
+
"data-shortcut-target": targetId,
|
|
454
|
+
"data-gesture-session": id,
|
|
455
|
+
onPointerDown(event) {
|
|
456
|
+
if (event.pointerType !== "mouse") return;
|
|
457
|
+
hover = targets.get(targetId)?.() ?? null;
|
|
458
|
+
alt = event.altKey;
|
|
459
|
+
inspectWithAlt();
|
|
460
|
+
},
|
|
461
|
+
onFocus(event) {
|
|
462
|
+
focused = event.currentTarget.matches(":focus-visible") ? targets.get(targetId)?.() ?? null : null;
|
|
463
|
+
if (focused) hover = null;
|
|
464
|
+
inspectWithAlt();
|
|
465
|
+
},
|
|
466
|
+
onBlur() {
|
|
467
|
+
const target = targets.get(targetId)?.() ?? null;
|
|
468
|
+
if (sameTarget(focused, target)) focused = null;
|
|
469
|
+
inspectWithAlt();
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
},
|
|
298
473
|
overlayRef,
|
|
299
474
|
/** `read` returns the area's current binding; targets resolve when hit. */
|
|
300
475
|
registerArea(area, read) {
|
|
@@ -310,8 +485,11 @@ function createGestureSession(game) {
|
|
|
310
485
|
removeEventListener("keydown", key);
|
|
311
486
|
removeEventListener("keyup", key);
|
|
312
487
|
removeEventListener("blur", loseFocus);
|
|
488
|
+
removeEventListener("pointermove", browse);
|
|
489
|
+
removeEventListener("pointerout", leaveWindow);
|
|
313
490
|
unsubscribe();
|
|
314
491
|
followers.clear();
|
|
492
|
+
targets.clear();
|
|
315
493
|
if (!guardingClicks) return;
|
|
316
494
|
removeEventListener("click", swallow, true);
|
|
317
495
|
removeEventListener("pointerdown", newPress, true);
|
|
@@ -333,8 +511,8 @@ import {
|
|
|
333
511
|
createContext as createContext2,
|
|
334
512
|
useContext as useContext2,
|
|
335
513
|
useId,
|
|
336
|
-
useLayoutEffect,
|
|
337
|
-
useRef,
|
|
514
|
+
useLayoutEffect as useLayoutEffect2,
|
|
515
|
+
useRef as useRef2,
|
|
338
516
|
useState
|
|
339
517
|
} from "react";
|
|
340
518
|
import { useSelector as useSelector2 } from "@tanstack/react-store";
|
|
@@ -344,12 +522,12 @@ function createGameHook() {
|
|
|
344
522
|
const Context = createContext2(null);
|
|
345
523
|
function GameProvider({ children, ...overrides }) {
|
|
346
524
|
const [owned, setOwned] = useState(null);
|
|
347
|
-
const lifetime =
|
|
525
|
+
const lifetime = useRef2(null);
|
|
348
526
|
const options = {
|
|
349
527
|
...defaults,
|
|
350
528
|
...overrides
|
|
351
529
|
};
|
|
352
|
-
|
|
530
|
+
useLayoutEffect2(() => {
|
|
353
531
|
let current = lifetime.current;
|
|
354
532
|
if (!current) {
|
|
355
533
|
const instance = createGameInstance()(options);
|
|
@@ -373,7 +551,7 @@ function createGameHook() {
|
|
|
373
551
|
});
|
|
374
552
|
};
|
|
375
553
|
}, []);
|
|
376
|
-
|
|
554
|
+
useLayoutEffect2(() => {
|
|
377
555
|
lifetime.current?.instance.setOptions(options);
|
|
378
556
|
});
|
|
379
557
|
return owned ? /* @__PURE__ */ jsx(Context.Provider, { value: owned.instance, children: /* @__PURE__ */ jsx(GestureContext.Provider, { value: owned.session, children }) }) : null;
|
|
@@ -416,10 +594,15 @@ function createGameHook() {
|
|
|
416
594
|
session,
|
|
417
595
|
(state) => state.inspect?.cardId === cardId ? state.inspect.via : null
|
|
418
596
|
);
|
|
597
|
+
const isActive = useGestureState(
|
|
598
|
+
session,
|
|
599
|
+
(state) => state.activeTarget?.kind === "card" && state.activeTarget.value === cardId
|
|
600
|
+
);
|
|
419
601
|
return {
|
|
420
602
|
props: session.cardProps(cardId, routes, { dragging, inspecting }),
|
|
421
603
|
canDrag,
|
|
422
604
|
isDragging: dragging,
|
|
605
|
+
isActive,
|
|
423
606
|
inspecting
|
|
424
607
|
};
|
|
425
608
|
}
|
|
@@ -435,11 +618,11 @@ function createGameHook() {
|
|
|
435
618
|
const active = dragOf(snapshot)?.active?.target;
|
|
436
619
|
return !!target && sameDropTarget(active ?? null, target);
|
|
437
620
|
});
|
|
438
|
-
const latest =
|
|
439
|
-
|
|
621
|
+
const latest = useRef2(erased);
|
|
622
|
+
useLayoutEffect2(() => {
|
|
440
623
|
latest.current = erased;
|
|
441
624
|
});
|
|
442
|
-
|
|
625
|
+
useLayoutEffect2(
|
|
443
626
|
() => session.registerArea(id, () => latest.current),
|
|
444
627
|
[session, id]
|
|
445
628
|
);
|
|
@@ -453,6 +636,61 @@ function createGameHook() {
|
|
|
453
636
|
isOver
|
|
454
637
|
};
|
|
455
638
|
}
|
|
639
|
+
const shortcutsOf = (snapshot) => snapshot.shortcuts;
|
|
640
|
+
const noShortcutsConfiguration = createStore2(
|
|
641
|
+
null
|
|
642
|
+
);
|
|
643
|
+
const sameHints = (left, right) => left.length === right.length && left.every((hint, index) => {
|
|
644
|
+
const next = right[index];
|
|
645
|
+
return hint.label === next.label && hint.interaction === next.interaction && hint.keys.length === next.keys.length && hint.keys.every((key, at) => key === next.keys[at]);
|
|
646
|
+
});
|
|
647
|
+
function useShortcutHints(target) {
|
|
648
|
+
const controller = shortcutsOf(useInstance());
|
|
649
|
+
useGame((snapshot) => shortcutsOf(snapshot)?.getHints(target) ?? [], {
|
|
650
|
+
compare: sameHints
|
|
651
|
+
});
|
|
652
|
+
return useSelector2(
|
|
653
|
+
controller?.configuration ?? noShortcutsConfiguration,
|
|
654
|
+
() => controller?.getHints(target) ?? [],
|
|
655
|
+
{ compare: sameHints }
|
|
656
|
+
);
|
|
657
|
+
}
|
|
658
|
+
function useGameShortcuts(options) {
|
|
659
|
+
const instance = useInstance();
|
|
660
|
+
const session = useGestureSession();
|
|
661
|
+
const controller = shortcutsOf(instance);
|
|
662
|
+
if (!controller)
|
|
663
|
+
throw new Error(
|
|
664
|
+
"useGameShortcuts requires shortcutsFeature in the game binding."
|
|
665
|
+
);
|
|
666
|
+
useShortcutsAdapter(instance, controller, session, options, (result) => {
|
|
667
|
+
if (result instanceof Error) instance.getOptions().onError?.(result);
|
|
668
|
+
else if (!result.accepted)
|
|
669
|
+
instance.getOptions().onError?.(
|
|
670
|
+
new Error(result.message ?? result.errorCode, { cause: result })
|
|
671
|
+
);
|
|
672
|
+
});
|
|
673
|
+
const target = useGestureState(
|
|
674
|
+
session,
|
|
675
|
+
(state) => state.activeTarget
|
|
676
|
+
);
|
|
677
|
+
const hints = useShortcutHints(target);
|
|
678
|
+
return { target, hints };
|
|
679
|
+
}
|
|
680
|
+
function useShortcutTarget(target) {
|
|
681
|
+
const session = useGestureSession();
|
|
682
|
+
const id = `${session.id}${useId()}`;
|
|
683
|
+
const latest = useRef2(target);
|
|
684
|
+
const targetKey = inputValueKey(target);
|
|
685
|
+
useLayoutEffect2(() => {
|
|
686
|
+
latest.current = target;
|
|
687
|
+
});
|
|
688
|
+
useLayoutEffect2(
|
|
689
|
+
() => session.registerTarget(id, () => latest.current),
|
|
690
|
+
[session, id, targetKey]
|
|
691
|
+
);
|
|
692
|
+
return { props: session.targetProps(id) };
|
|
693
|
+
}
|
|
456
694
|
function useDragOverlay() {
|
|
457
695
|
const session = useGestureSession();
|
|
458
696
|
const drag = useGestureState(session, (state) => state.drag);
|
|
@@ -462,11 +700,22 @@ function createGameHook() {
|
|
|
462
700
|
ref: session.overlayRef
|
|
463
701
|
} : null;
|
|
464
702
|
}
|
|
703
|
+
function useActiveCard() {
|
|
704
|
+
const session = useGestureSession();
|
|
705
|
+
return useGestureState(
|
|
706
|
+
session,
|
|
707
|
+
(state) => state.activeTarget?.kind === "card" ? state.activeTarget.value : null
|
|
708
|
+
);
|
|
709
|
+
}
|
|
465
710
|
return {
|
|
466
711
|
GameProvider,
|
|
467
712
|
useGame,
|
|
468
713
|
Subscribe,
|
|
469
714
|
useCardGesture,
|
|
715
|
+
useActiveCard,
|
|
716
|
+
useGameShortcuts,
|
|
717
|
+
useShortcutTarget,
|
|
718
|
+
useShortcutHints,
|
|
470
719
|
useDropArea,
|
|
471
720
|
useDragOverlay
|
|
472
721
|
};
|