koin.js 1.0.12 → 1.0.14
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/dist/index.js +257 -130
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +257 -130
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2282,7 +2282,8 @@ var SIX_BUTTON_LAYOUT = {
|
|
|
2282
2282
|
{ type: "y", label: "A", x: 72, y: 64, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true },
|
|
2283
2283
|
{ type: "b", label: "B", x: 82, y: 60, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true },
|
|
2284
2284
|
{ type: "a", label: "C", x: 92, y: 56, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true },
|
|
2285
|
-
{ type: "
|
|
2285
|
+
{ type: "select", label: "SELECT", x: SELECT_X, y: START_SELECT_Y, size: BUTTON_SMALL, showInPortrait: true, showInLandscape: true, shape: "pill" },
|
|
2286
|
+
{ type: "start", label: "START", x: START_X, y: START_SELECT_Y, size: BUTTON_SMALL, showInPortrait: true, showInLandscape: true, shape: "pill" }
|
|
2286
2287
|
]
|
|
2287
2288
|
};
|
|
2288
2289
|
var SATURN_LAYOUT = {
|
|
@@ -2301,7 +2302,8 @@ var SATURN_LAYOUT = {
|
|
|
2301
2302
|
// Triggers (L2/R2 for Saturn L/R)
|
|
2302
2303
|
{ type: "l2", label: "L", x: 8, y: 20, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true, shape: "rect" },
|
|
2303
2304
|
{ type: "r2", label: "R", x: 92, y: 20, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true, shape: "rect" },
|
|
2304
|
-
{ type: "
|
|
2305
|
+
{ type: "select", label: "SELECT", x: SELECT_X, y: START_SELECT_Y, size: BUTTON_SMALL, showInPortrait: true, showInLandscape: true, shape: "pill" },
|
|
2306
|
+
{ type: "start", label: "START", x: START_X, y: START_SELECT_Y, size: BUTTON_SMALL, showInPortrait: true, showInLandscape: true, shape: "pill" }
|
|
2305
2307
|
]
|
|
2306
2308
|
};
|
|
2307
2309
|
var NEOGEO_LAYOUT = {
|
|
@@ -2350,9 +2352,10 @@ var N64_LAYOUT = {
|
|
|
2350
2352
|
// Shoulders
|
|
2351
2353
|
{ type: "l", label: "L", x: 8, y: 20, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true, shape: "rect" },
|
|
2352
2354
|
{ type: "r", label: "R", x: 92, y: 20, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true, shape: "rect" },
|
|
2353
|
-
// Z trigger (use
|
|
2354
|
-
{ type: "
|
|
2355
|
-
{ type: "
|
|
2355
|
+
// Z trigger (use l3 button for Z trigger)
|
|
2356
|
+
{ type: "l3", label: "Z", x: 8, y: 35, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true, shape: "rect" },
|
|
2357
|
+
{ type: "select", label: "SELECT", x: SELECT_X, y: START_SELECT_Y, size: BUTTON_SMALL, showInPortrait: true, showInLandscape: true, shape: "pill" },
|
|
2358
|
+
{ type: "start", label: "START", x: START_X, y: START_SELECT_Y, size: BUTTON_SMALL, showInPortrait: true, showInLandscape: true, shape: "pill" }
|
|
2356
2359
|
]
|
|
2357
2360
|
};
|
|
2358
2361
|
var DREAMCAST_LAYOUT = {
|
|
@@ -2367,7 +2370,8 @@ var DREAMCAST_LAYOUT = {
|
|
|
2367
2370
|
// Triggers
|
|
2368
2371
|
{ type: "l", label: "L", x: 8, y: 20, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true, shape: "rect" },
|
|
2369
2372
|
{ type: "r", label: "R", x: 92, y: 20, size: BUTTON_MEDIUM, showInPortrait: true, showInLandscape: true, shape: "rect" },
|
|
2370
|
-
{ type: "
|
|
2373
|
+
{ type: "select", label: "SELECT", x: SELECT_X, y: START_SELECT_Y, size: BUTTON_SMALL, showInPortrait: true, showInLandscape: true, shape: "pill" },
|
|
2374
|
+
{ type: "start", label: "START", x: START_X, y: START_SELECT_Y, size: BUTTON_SMALL, showInPortrait: true, showInLandscape: true, shape: "pill" }
|
|
2371
2375
|
]
|
|
2372
2376
|
};
|
|
2373
2377
|
function getLayoutForSystem(system) {
|
|
@@ -2389,26 +2393,49 @@ function getLayoutForSystem(system) {
|
|
|
2389
2393
|
if (s.includes("ATARI")) return TWO_BUTTON_LAYOUT;
|
|
2390
2394
|
return TWO_BUTTON_LAYOUT;
|
|
2391
2395
|
}
|
|
2392
|
-
|
|
2396
|
+
|
|
2397
|
+
// src/components/VirtualController/utils/dragConstraints.ts
|
|
2398
|
+
function constrainToViewport({
|
|
2399
|
+
newXPercent,
|
|
2400
|
+
newYPercent,
|
|
2401
|
+
elementSize,
|
|
2402
|
+
containerWidth,
|
|
2403
|
+
containerHeight
|
|
2404
|
+
}) {
|
|
2405
|
+
const xMargin = elementSize / 2 / containerWidth * 100;
|
|
2406
|
+
const yMargin = elementSize / 2 / containerHeight * 100;
|
|
2407
|
+
return {
|
|
2408
|
+
x: Math.max(xMargin, Math.min(100 - xMargin, newXPercent)),
|
|
2409
|
+
y: Math.max(yMargin, Math.min(100 - yMargin, newYPercent))
|
|
2410
|
+
};
|
|
2411
|
+
}
|
|
2412
|
+
|
|
2413
|
+
// src/components/VirtualController/hooks/useDrag.ts
|
|
2414
|
+
var DEFAULT_HOLD_DELAY = 350;
|
|
2415
|
+
var DEFAULT_CENTER_THRESHOLD = 0.4;
|
|
2393
2416
|
var DRAG_MOVE_THRESHOLD = 10;
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
buttonType,
|
|
2397
|
-
isSystemButton,
|
|
2398
|
-
buttonSize,
|
|
2417
|
+
function useDrag({
|
|
2418
|
+
elementSize,
|
|
2399
2419
|
displayX,
|
|
2400
2420
|
displayY,
|
|
2401
2421
|
containerWidth,
|
|
2402
2422
|
containerHeight,
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2423
|
+
onPositionChange,
|
|
2424
|
+
holdDelay = DEFAULT_HOLD_DELAY,
|
|
2425
|
+
centerThreshold = DEFAULT_CENTER_THRESHOLD,
|
|
2426
|
+
onDragStart,
|
|
2427
|
+
onDragEnd
|
|
2407
2428
|
}) {
|
|
2429
|
+
const [isDragging, setIsDragging] = React2.useState(false);
|
|
2408
2430
|
const isDraggingRef = React2.useRef(false);
|
|
2409
|
-
const dragStartRef = React2.useRef({ x: 0, y: 0 });
|
|
2410
2431
|
const dragTimerRef = React2.useRef(null);
|
|
2411
2432
|
const touchStartPosRef = React2.useRef({ x: 0, y: 0 });
|
|
2433
|
+
const dragStartRef = React2.useRef({
|
|
2434
|
+
elementX: 0,
|
|
2435
|
+
elementY: 0,
|
|
2436
|
+
touchX: 0,
|
|
2437
|
+
touchY: 0
|
|
2438
|
+
});
|
|
2412
2439
|
const clearDragTimer = React2.useCallback(() => {
|
|
2413
2440
|
if (dragTimerRef.current) {
|
|
2414
2441
|
clearTimeout(dragTimerRef.current);
|
|
@@ -2418,23 +2445,129 @@ function useTouchHandlers({
|
|
|
2418
2445
|
const startDragging = React2.useCallback(
|
|
2419
2446
|
(touchX, touchY) => {
|
|
2420
2447
|
isDraggingRef.current = true;
|
|
2448
|
+
setIsDragging(true);
|
|
2421
2449
|
dragStartRef.current = {
|
|
2422
|
-
|
|
2423
|
-
|
|
2450
|
+
elementX: displayX,
|
|
2451
|
+
elementY: displayY,
|
|
2452
|
+
touchX,
|
|
2453
|
+
touchY
|
|
2424
2454
|
};
|
|
2425
2455
|
if (navigator.vibrate) {
|
|
2426
2456
|
navigator.vibrate([10, 30, 10]);
|
|
2427
2457
|
}
|
|
2458
|
+
onDragStart?.();
|
|
2459
|
+
},
|
|
2460
|
+
[displayX, displayY, onDragStart]
|
|
2461
|
+
);
|
|
2462
|
+
const checkDragStart = React2.useCallback(
|
|
2463
|
+
(touchX, touchY, elementRect) => {
|
|
2464
|
+
if (!onPositionChange) return false;
|
|
2465
|
+
touchStartPosRef.current = { x: touchX, y: touchY };
|
|
2466
|
+
const centerX = elementRect.left + elementRect.width / 2;
|
|
2467
|
+
const centerY = elementRect.top + elementRect.height / 2;
|
|
2468
|
+
const distFromCenter = Math.sqrt(
|
|
2469
|
+
Math.pow(touchX - centerX, 2) + Math.pow(touchY - centerY, 2)
|
|
2470
|
+
);
|
|
2471
|
+
const centerRadius = elementSize * centerThreshold;
|
|
2472
|
+
if (distFromCenter < centerRadius) {
|
|
2473
|
+
dragTimerRef.current = setTimeout(() => {
|
|
2474
|
+
if (!isDraggingRef.current) {
|
|
2475
|
+
startDragging(touchX, touchY);
|
|
2476
|
+
}
|
|
2477
|
+
}, holdDelay);
|
|
2478
|
+
return true;
|
|
2479
|
+
}
|
|
2480
|
+
return false;
|
|
2481
|
+
},
|
|
2482
|
+
[onPositionChange, elementSize, centerThreshold, holdDelay, startDragging]
|
|
2483
|
+
);
|
|
2484
|
+
const checkMoveThreshold = React2.useCallback(
|
|
2485
|
+
(touchX, touchY) => {
|
|
2486
|
+
if (!onPositionChange || isDraggingRef.current) return false;
|
|
2487
|
+
const moveDistance = Math.sqrt(
|
|
2488
|
+
Math.pow(touchX - touchStartPosRef.current.x, 2) + Math.pow(touchY - touchStartPosRef.current.y, 2)
|
|
2489
|
+
);
|
|
2490
|
+
if (moveDistance > DRAG_MOVE_THRESHOLD) {
|
|
2491
|
+
clearDragTimer();
|
|
2492
|
+
startDragging(touchX, touchY);
|
|
2493
|
+
return true;
|
|
2494
|
+
}
|
|
2495
|
+
return false;
|
|
2496
|
+
},
|
|
2497
|
+
[onPositionChange, clearDragTimer, startDragging]
|
|
2498
|
+
);
|
|
2499
|
+
const handleDragMove = React2.useCallback(
|
|
2500
|
+
(touchX, touchY) => {
|
|
2501
|
+
if (!isDraggingRef.current || !onPositionChange) return;
|
|
2502
|
+
const deltaX = touchX - dragStartRef.current.touchX;
|
|
2503
|
+
const deltaY = touchY - dragStartRef.current.touchY;
|
|
2504
|
+
const newXPercent = dragStartRef.current.elementX + deltaX / containerWidth * 100;
|
|
2505
|
+
const newYPercent = dragStartRef.current.elementY + deltaY / containerHeight * 100;
|
|
2506
|
+
const constrained = constrainToViewport({
|
|
2507
|
+
newXPercent,
|
|
2508
|
+
newYPercent,
|
|
2509
|
+
elementSize,
|
|
2510
|
+
containerWidth,
|
|
2511
|
+
containerHeight
|
|
2512
|
+
});
|
|
2513
|
+
onPositionChange(constrained.x, constrained.y);
|
|
2514
|
+
},
|
|
2515
|
+
[onPositionChange, containerWidth, containerHeight, elementSize]
|
|
2516
|
+
);
|
|
2517
|
+
const handleDragEnd = React2.useCallback(() => {
|
|
2518
|
+
clearDragTimer();
|
|
2519
|
+
if (isDraggingRef.current) {
|
|
2520
|
+
isDraggingRef.current = false;
|
|
2521
|
+
setIsDragging(false);
|
|
2522
|
+
onDragEnd?.();
|
|
2523
|
+
}
|
|
2524
|
+
}, [clearDragTimer, onDragEnd]);
|
|
2525
|
+
return {
|
|
2526
|
+
isDragging,
|
|
2527
|
+
checkDragStart,
|
|
2528
|
+
handleDragMove,
|
|
2529
|
+
handleDragEnd,
|
|
2530
|
+
clearDragTimer,
|
|
2531
|
+
checkMoveThreshold
|
|
2532
|
+
};
|
|
2533
|
+
}
|
|
2534
|
+
|
|
2535
|
+
// src/components/VirtualController/hooks/useTouchHandlers.ts
|
|
2536
|
+
function useTouchHandlers({
|
|
2537
|
+
buttonType,
|
|
2538
|
+
isSystemButton,
|
|
2539
|
+
buttonSize,
|
|
2540
|
+
displayX,
|
|
2541
|
+
displayY,
|
|
2542
|
+
containerWidth,
|
|
2543
|
+
containerHeight,
|
|
2544
|
+
onPress,
|
|
2545
|
+
onPressDown,
|
|
2546
|
+
onRelease,
|
|
2547
|
+
onPositionChange
|
|
2548
|
+
}) {
|
|
2549
|
+
const isDraggingRef = React2.useRef(false);
|
|
2550
|
+
const drag = useDrag({
|
|
2551
|
+
elementSize: buttonSize,
|
|
2552
|
+
displayX,
|
|
2553
|
+
displayY,
|
|
2554
|
+
containerWidth,
|
|
2555
|
+
containerHeight,
|
|
2556
|
+
onPositionChange,
|
|
2557
|
+
centerThreshold: 0.4,
|
|
2558
|
+
onDragStart: () => {
|
|
2559
|
+
isDraggingRef.current = true;
|
|
2428
2560
|
if (!isSystemButton) {
|
|
2429
2561
|
onRelease(buttonType);
|
|
2430
2562
|
}
|
|
2431
2563
|
},
|
|
2432
|
-
|
|
2433
|
-
|
|
2564
|
+
onDragEnd: () => {
|
|
2565
|
+
isDraggingRef.current = false;
|
|
2566
|
+
}
|
|
2567
|
+
});
|
|
2434
2568
|
const handleTouchStart = React2.useCallback(
|
|
2435
2569
|
(e) => {
|
|
2436
2570
|
const touch = e.touches[0];
|
|
2437
|
-
touchStartPosRef.current = { x: touch.clientX, y: touch.clientY };
|
|
2438
2571
|
e.preventDefault();
|
|
2439
2572
|
e.stopPropagation();
|
|
2440
2573
|
if (navigator.vibrate) {
|
|
@@ -2449,57 +2582,32 @@ function useTouchHandlers({
|
|
|
2449
2582
|
const target = e.currentTarget;
|
|
2450
2583
|
if (!target) return;
|
|
2451
2584
|
const rect = target.getBoundingClientRect();
|
|
2452
|
-
|
|
2453
|
-
const centerY = rect.top + rect.height / 2;
|
|
2454
|
-
const distance = Math.sqrt(
|
|
2455
|
-
Math.pow(touch.clientX - centerX, 2) + Math.pow(touch.clientY - centerY, 2)
|
|
2456
|
-
);
|
|
2457
|
-
const dragThreshold = buttonSize * DRAG_CENTER_THRESHOLD;
|
|
2458
|
-
if (distance < dragThreshold) {
|
|
2459
|
-
dragTimerRef.current = setTimeout(() => {
|
|
2460
|
-
if (!isDraggingRef.current) {
|
|
2461
|
-
startDragging(touch.clientX, touch.clientY);
|
|
2462
|
-
}
|
|
2463
|
-
}, DRAG_HOLD_DELAY);
|
|
2464
|
-
}
|
|
2585
|
+
drag.checkDragStart(touch.clientX, touch.clientY, rect);
|
|
2465
2586
|
}
|
|
2466
2587
|
},
|
|
2467
|
-
[isSystemButton, buttonType, onPress, onPressDown, onPositionChange,
|
|
2588
|
+
[isSystemButton, buttonType, onPress, onPressDown, onPositionChange, drag]
|
|
2468
2589
|
);
|
|
2469
2590
|
const handleTouchMove = React2.useCallback(
|
|
2470
2591
|
(e) => {
|
|
2471
2592
|
const touch = e.touches[0];
|
|
2472
2593
|
if (onPositionChange && !isDraggingRef.current) {
|
|
2473
|
-
|
|
2474
|
-
Math.pow(touch.clientX - touchStartPosRef.current.x, 2) + Math.pow(touch.clientY - touchStartPosRef.current.y, 2)
|
|
2475
|
-
);
|
|
2476
|
-
if (moveDistance > DRAG_MOVE_THRESHOLD) {
|
|
2477
|
-
clearDragTimer();
|
|
2478
|
-
startDragging(touch.clientX, touch.clientY);
|
|
2479
|
-
}
|
|
2594
|
+
drag.checkMoveThreshold(touch.clientX, touch.clientY);
|
|
2480
2595
|
}
|
|
2481
|
-
if (isDraggingRef.current
|
|
2596
|
+
if (isDraggingRef.current) {
|
|
2482
2597
|
e.preventDefault();
|
|
2483
2598
|
e.stopPropagation();
|
|
2484
|
-
|
|
2485
|
-
const newY = touch.clientY - dragStartRef.current.y;
|
|
2486
|
-
const newXPercent = newX / containerWidth * 100;
|
|
2487
|
-
const newYPercent = newY / containerHeight * 100;
|
|
2488
|
-
const margin = buttonSize / 2 / Math.min(containerWidth, containerHeight) * 100;
|
|
2489
|
-
const constrainedX = Math.max(margin, Math.min(100 - margin, newXPercent));
|
|
2490
|
-
const constrainedY = Math.max(margin, Math.min(100 - margin, newYPercent));
|
|
2491
|
-
onPositionChange(constrainedX, constrainedY);
|
|
2599
|
+
drag.handleDragMove(touch.clientX, touch.clientY);
|
|
2492
2600
|
}
|
|
2493
2601
|
},
|
|
2494
|
-
[onPositionChange,
|
|
2602
|
+
[onPositionChange, drag]
|
|
2495
2603
|
);
|
|
2496
2604
|
const handleTouchEnd = React2.useCallback(
|
|
2497
2605
|
(e) => {
|
|
2498
|
-
clearDragTimer();
|
|
2606
|
+
drag.clearDragTimer();
|
|
2499
2607
|
if (isDraggingRef.current) {
|
|
2500
2608
|
e.preventDefault();
|
|
2501
2609
|
e.stopPropagation();
|
|
2502
|
-
|
|
2610
|
+
drag.handleDragEnd();
|
|
2503
2611
|
return;
|
|
2504
2612
|
}
|
|
2505
2613
|
e.preventDefault();
|
|
@@ -2508,15 +2616,15 @@ function useTouchHandlers({
|
|
|
2508
2616
|
onRelease(buttonType);
|
|
2509
2617
|
}
|
|
2510
2618
|
},
|
|
2511
|
-
[
|
|
2619
|
+
[drag, isSystemButton, buttonType, onRelease]
|
|
2512
2620
|
);
|
|
2513
2621
|
const handleTouchCancel = React2.useCallback(
|
|
2514
2622
|
(e) => {
|
|
2515
|
-
clearDragTimer();
|
|
2623
|
+
drag.clearDragTimer();
|
|
2516
2624
|
if (isDraggingRef.current) {
|
|
2517
2625
|
e.preventDefault();
|
|
2518
2626
|
e.stopPropagation();
|
|
2519
|
-
|
|
2627
|
+
drag.handleDragEnd();
|
|
2520
2628
|
return;
|
|
2521
2629
|
}
|
|
2522
2630
|
e.preventDefault();
|
|
@@ -2525,11 +2633,11 @@ function useTouchHandlers({
|
|
|
2525
2633
|
onRelease(buttonType);
|
|
2526
2634
|
}
|
|
2527
2635
|
},
|
|
2528
|
-
[
|
|
2636
|
+
[drag, isSystemButton, buttonType, onRelease]
|
|
2529
2637
|
);
|
|
2530
2638
|
const cleanup = React2.useCallback(() => {
|
|
2531
|
-
clearDragTimer();
|
|
2532
|
-
}, [
|
|
2639
|
+
drag.clearDragTimer();
|
|
2640
|
+
}, [drag]);
|
|
2533
2641
|
return {
|
|
2534
2642
|
handleTouchStart,
|
|
2535
2643
|
handleTouchMove,
|
|
@@ -3385,7 +3493,6 @@ function dispatchKeyboardEvent(type, code) {
|
|
|
3385
3493
|
canvas.dispatchEvent(event);
|
|
3386
3494
|
return true;
|
|
3387
3495
|
}
|
|
3388
|
-
var DRAG_HOLD_DELAY2 = 350;
|
|
3389
3496
|
var CENTER_TOUCH_RADIUS = 0.25;
|
|
3390
3497
|
var Dpad = React2__default.default.memo(function Dpad2({
|
|
3391
3498
|
size = 180,
|
|
@@ -3402,10 +3509,6 @@ var Dpad = React2__default.default.memo(function Dpad2({
|
|
|
3402
3509
|
const dpadRef = React2.useRef(null);
|
|
3403
3510
|
const activeTouchRef = React2.useRef(null);
|
|
3404
3511
|
const activeDirectionsRef = React2.useRef(/* @__PURE__ */ new Set());
|
|
3405
|
-
const [isDragging, setIsDragging] = React2.useState(false);
|
|
3406
|
-
const dragTimerRef = React2.useRef(null);
|
|
3407
|
-
const dragStartRef = React2.useRef({ x: 0, y: 0, touchX: 0, touchY: 0 });
|
|
3408
|
-
const touchStartPosRef = React2.useRef({ x: 0, y: 0, time: 0 });
|
|
3409
3512
|
const upPathRef = React2.useRef(null);
|
|
3410
3513
|
const downPathRef = React2.useRef(null);
|
|
3411
3514
|
const leftPathRef = React2.useRef(null);
|
|
@@ -3413,6 +3516,13 @@ var Dpad = React2__default.default.memo(function Dpad2({
|
|
|
3413
3516
|
const centerCircleRef = React2.useRef(null);
|
|
3414
3517
|
const displayX = customPosition ? customPosition.x : x;
|
|
3415
3518
|
const displayY = customPosition ? customPosition.y : y;
|
|
3519
|
+
const releaseAllDirections = React2.useCallback((getKeyCode2) => {
|
|
3520
|
+
activeDirectionsRef.current.forEach((dir) => {
|
|
3521
|
+
const keyCode = getKeyCode2(dir);
|
|
3522
|
+
if (keyCode) dispatchKeyboardEvent("keyup", keyCode);
|
|
3523
|
+
});
|
|
3524
|
+
activeDirectionsRef.current = /* @__PURE__ */ new Set();
|
|
3525
|
+
}, []);
|
|
3416
3526
|
const getKeyCode = React2.useCallback((direction) => {
|
|
3417
3527
|
if (!controls) {
|
|
3418
3528
|
const defaults = {
|
|
@@ -3425,6 +3535,19 @@ var Dpad = React2__default.default.memo(function Dpad2({
|
|
|
3425
3535
|
}
|
|
3426
3536
|
return controls[direction] || "";
|
|
3427
3537
|
}, [controls]);
|
|
3538
|
+
const drag = useDrag({
|
|
3539
|
+
elementSize: size,
|
|
3540
|
+
displayX,
|
|
3541
|
+
displayY,
|
|
3542
|
+
containerWidth,
|
|
3543
|
+
containerHeight,
|
|
3544
|
+
onPositionChange,
|
|
3545
|
+
centerThreshold: CENTER_TOUCH_RADIUS,
|
|
3546
|
+
onDragStart: () => {
|
|
3547
|
+
releaseAllDirections(getKeyCode);
|
|
3548
|
+
updateVisuals(/* @__PURE__ */ new Set());
|
|
3549
|
+
}
|
|
3550
|
+
});
|
|
3428
3551
|
const getDirectionsFromTouch = React2.useCallback((touchX, touchY, rect) => {
|
|
3429
3552
|
const centerX = rect.left + rect.width / 2;
|
|
3430
3553
|
const centerY = rect.top + rect.height / 2;
|
|
@@ -3486,51 +3609,20 @@ var Dpad = React2__default.default.memo(function Dpad2({
|
|
|
3486
3609
|
activeDirectionsRef.current = newDirections;
|
|
3487
3610
|
updateVisuals(newDirections);
|
|
3488
3611
|
}, [getKeyCode, updateVisuals]);
|
|
3489
|
-
const clearDragTimer = React2.useCallback(() => {
|
|
3490
|
-
if (dragTimerRef.current) {
|
|
3491
|
-
clearTimeout(dragTimerRef.current);
|
|
3492
|
-
dragTimerRef.current = null;
|
|
3493
|
-
}
|
|
3494
|
-
}, []);
|
|
3495
|
-
const startDragging = React2.useCallback((touchX, touchY) => {
|
|
3496
|
-
setIsDragging(true);
|
|
3497
|
-
dragStartRef.current = {
|
|
3498
|
-
x: displayX,
|
|
3499
|
-
y: displayY,
|
|
3500
|
-
touchX,
|
|
3501
|
-
touchY
|
|
3502
|
-
};
|
|
3503
|
-
activeDirectionsRef.current.forEach((dir) => {
|
|
3504
|
-
const keyCode = getKeyCode(dir);
|
|
3505
|
-
if (keyCode) dispatchKeyboardEvent("keyup", keyCode);
|
|
3506
|
-
});
|
|
3507
|
-
activeDirectionsRef.current = /* @__PURE__ */ new Set();
|
|
3508
|
-
updateVisuals(/* @__PURE__ */ new Set());
|
|
3509
|
-
if (navigator.vibrate) navigator.vibrate([10, 30, 10]);
|
|
3510
|
-
}, [displayX, displayY, getKeyCode, updateVisuals]);
|
|
3511
3612
|
const handleTouchStart = React2.useCallback((e) => {
|
|
3512
3613
|
e.preventDefault();
|
|
3513
3614
|
if (activeTouchRef.current !== null) return;
|
|
3514
3615
|
const touch = e.changedTouches[0];
|
|
3515
3616
|
activeTouchRef.current = touch.identifier;
|
|
3516
|
-
touchStartPosRef.current = { x: touch.clientX, y: touch.clientY, time: Date.now() };
|
|
3517
3617
|
const rect = dpadRef.current?.getBoundingClientRect();
|
|
3518
3618
|
if (!rect) return;
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
const distFromCenter = Math.sqrt(
|
|
3522
|
-
Math.pow(touch.clientX - centerX, 2) + Math.pow(touch.clientY - centerY, 2)
|
|
3523
|
-
);
|
|
3524
|
-
const centerRadius = size * CENTER_TOUCH_RADIUS;
|
|
3525
|
-
if (distFromCenter < centerRadius && onPositionChange) {
|
|
3526
|
-
dragTimerRef.current = setTimeout(() => {
|
|
3527
|
-
startDragging(touch.clientX, touch.clientY);
|
|
3528
|
-
}, DRAG_HOLD_DELAY2);
|
|
3619
|
+
if (onPositionChange) {
|
|
3620
|
+
drag.checkDragStart(touch.clientX, touch.clientY, rect);
|
|
3529
3621
|
}
|
|
3530
|
-
if (!isDragging) {
|
|
3622
|
+
if (!drag.isDragging) {
|
|
3531
3623
|
updateDirections(getDirectionsFromTouch(touch.clientX, touch.clientY, rect));
|
|
3532
3624
|
}
|
|
3533
|
-
}, [getDirectionsFromTouch, updateDirections,
|
|
3625
|
+
}, [getDirectionsFromTouch, updateDirections, onPositionChange, drag]);
|
|
3534
3626
|
const handleTouchMove = React2.useCallback((e) => {
|
|
3535
3627
|
e.preventDefault();
|
|
3536
3628
|
let touch = null;
|
|
@@ -3541,31 +3633,19 @@ var Dpad = React2__default.default.memo(function Dpad2({
|
|
|
3541
3633
|
}
|
|
3542
3634
|
}
|
|
3543
3635
|
if (!touch) return;
|
|
3544
|
-
if (isDragging
|
|
3545
|
-
|
|
3546
|
-
const deltaY = touch.clientY - dragStartRef.current.touchY;
|
|
3547
|
-
const newXPercent = dragStartRef.current.x + deltaX / containerWidth * 100;
|
|
3548
|
-
const newYPercent = dragStartRef.current.y + deltaY / containerHeight * 100;
|
|
3549
|
-
const margin = size / 2 / Math.min(containerWidth, containerHeight) * 100;
|
|
3550
|
-
const constrainedX = Math.max(margin, Math.min(100 - margin, newXPercent));
|
|
3551
|
-
const constrainedY = Math.max(margin, Math.min(100 - margin, newYPercent));
|
|
3552
|
-
onPositionChange(constrainedX, constrainedY);
|
|
3636
|
+
if (drag.isDragging) {
|
|
3637
|
+
drag.handleDragMove(touch.clientX, touch.clientY);
|
|
3553
3638
|
} else {
|
|
3554
|
-
const moveDistance = Math.sqrt(
|
|
3555
|
-
Math.pow(touch.clientX - touchStartPosRef.current.x, 2) + Math.pow(touch.clientY - touchStartPosRef.current.y, 2)
|
|
3556
|
-
);
|
|
3557
|
-
if (moveDistance > 15) {
|
|
3558
|
-
clearDragTimer();
|
|
3559
|
-
}
|
|
3560
3639
|
const rect = dpadRef.current?.getBoundingClientRect();
|
|
3561
3640
|
if (rect) {
|
|
3641
|
+
drag.clearDragTimer();
|
|
3562
3642
|
updateDirections(getDirectionsFromTouch(touch.clientX, touch.clientY, rect));
|
|
3563
3643
|
}
|
|
3564
3644
|
}
|
|
3565
|
-
}, [
|
|
3645
|
+
}, [drag, getDirectionsFromTouch, updateDirections]);
|
|
3566
3646
|
const handleTouchEnd = React2.useCallback((e) => {
|
|
3567
3647
|
e.preventDefault();
|
|
3568
|
-
clearDragTimer();
|
|
3648
|
+
drag.clearDragTimer();
|
|
3569
3649
|
let touchEnded = false;
|
|
3570
3650
|
for (let i = 0; i < e.changedTouches.length; i++) {
|
|
3571
3651
|
if (e.changedTouches[i].identifier === activeTouchRef.current) {
|
|
@@ -3575,8 +3655,8 @@ var Dpad = React2__default.default.memo(function Dpad2({
|
|
|
3575
3655
|
}
|
|
3576
3656
|
if (touchEnded) {
|
|
3577
3657
|
activeTouchRef.current = null;
|
|
3578
|
-
if (isDragging) {
|
|
3579
|
-
|
|
3658
|
+
if (drag.isDragging) {
|
|
3659
|
+
drag.handleDragEnd();
|
|
3580
3660
|
} else {
|
|
3581
3661
|
activeDirectionsRef.current.forEach((dir) => {
|
|
3582
3662
|
const keyCode = getKeyCode(dir);
|
|
@@ -3586,7 +3666,7 @@ var Dpad = React2__default.default.memo(function Dpad2({
|
|
|
3586
3666
|
updateVisuals(/* @__PURE__ */ new Set());
|
|
3587
3667
|
}
|
|
3588
3668
|
}
|
|
3589
|
-
}, [getKeyCode, updateVisuals,
|
|
3669
|
+
}, [getKeyCode, updateVisuals, drag]);
|
|
3590
3670
|
React2.useEffect(() => {
|
|
3591
3671
|
const dpad = dpadRef.current;
|
|
3592
3672
|
if (!dpad) return;
|
|
@@ -3599,9 +3679,9 @@ var Dpad = React2__default.default.memo(function Dpad2({
|
|
|
3599
3679
|
dpad.removeEventListener("touchmove", handleTouchMove);
|
|
3600
3680
|
dpad.removeEventListener("touchend", handleTouchEnd);
|
|
3601
3681
|
dpad.removeEventListener("touchcancel", handleTouchEnd);
|
|
3602
|
-
clearDragTimer();
|
|
3682
|
+
drag.clearDragTimer();
|
|
3603
3683
|
};
|
|
3604
|
-
}, [handleTouchStart, handleTouchMove, handleTouchEnd,
|
|
3684
|
+
}, [handleTouchStart, handleTouchMove, handleTouchEnd, drag]);
|
|
3605
3685
|
const leftPx = displayX / 100 * containerWidth - size / 2;
|
|
3606
3686
|
const topPx = displayY / 100 * containerHeight - size / 2;
|
|
3607
3687
|
const dUp = "M 35,5 L 65,5 L 65,35 L 50,50 L 35,35 Z";
|
|
@@ -3612,21 +3692,21 @@ var Dpad = React2__default.default.memo(function Dpad2({
|
|
|
3612
3692
|
"div",
|
|
3613
3693
|
{
|
|
3614
3694
|
ref: dpadRef,
|
|
3615
|
-
className: `absolute pointer-events-auto touch-manipulation select-none ${isDragging ? "opacity-60" : ""}`,
|
|
3695
|
+
className: `absolute pointer-events-auto touch-manipulation select-none ${drag.isDragging ? "opacity-60" : ""}`,
|
|
3616
3696
|
style: {
|
|
3617
3697
|
top: 0,
|
|
3618
3698
|
left: 0,
|
|
3619
|
-
transform: `translate3d(${leftPx}px, ${topPx}px, 0)${isDragging ? " scale(1.05)" : ""}`,
|
|
3699
|
+
transform: `translate3d(${leftPx}px, ${topPx}px, 0)${drag.isDragging ? " scale(1.05)" : ""}`,
|
|
3620
3700
|
width: size,
|
|
3621
3701
|
height: size,
|
|
3622
3702
|
opacity: isLandscape ? 0.75 : 0.9,
|
|
3623
3703
|
WebkitTouchCallout: "none",
|
|
3624
3704
|
WebkitUserSelect: "none",
|
|
3625
3705
|
touchAction: "none",
|
|
3626
|
-
transition: isDragging ? "none" : "transform 0.1s ease-out"
|
|
3706
|
+
transition: drag.isDragging ? "none" : "transform 0.1s ease-out"
|
|
3627
3707
|
},
|
|
3628
3708
|
children: [
|
|
3629
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: `absolute inset-0 rounded-full bg-black/40 backdrop-blur-md border shadow-lg ${isDragging ? "border-white/50 ring-2 ring-white/30" : "border-white/10"}` }),
|
|
3709
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: `absolute inset-0 rounded-full bg-black/40 backdrop-blur-md border shadow-lg ${drag.isDragging ? "border-white/50 ring-2 ring-white/30" : "border-white/10"}` }),
|
|
3630
3710
|
/* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "100%", height: "100%", viewBox: "0 0 100 100", className: "drop-shadow-xl relative z-10", children: [
|
|
3631
3711
|
/* @__PURE__ */ jsxRuntime.jsx("path", { ref: upPathRef, d: dUp, fill: "rgba(255,255,255,0.05)", stroke: "rgba(255,255,255,0.2)", strokeWidth: "1", className: "transition-all duration-75" }),
|
|
3632
3712
|
/* @__PURE__ */ jsxRuntime.jsx("path", { ref: rightPathRef, d: dRight, fill: "rgba(255,255,255,0.05)", stroke: "rgba(255,255,255,0.2)", strokeWidth: "1", className: "transition-all duration-75" }),
|
|
@@ -3639,9 +3719,9 @@ var Dpad = React2__default.default.memo(function Dpad2({
|
|
|
3639
3719
|
cx: "50",
|
|
3640
3720
|
cy: "50",
|
|
3641
3721
|
r: "12",
|
|
3642
|
-
fill: isDragging ? systemColor : "rgba(0,0,0,0.5)",
|
|
3643
|
-
stroke: isDragging ? "#fff" : "rgba(255,255,255,0.3)",
|
|
3644
|
-
strokeWidth: isDragging ? 2 : 1
|
|
3722
|
+
fill: drag.isDragging ? systemColor : "rgba(0,0,0,0.5)",
|
|
3723
|
+
stroke: drag.isDragging ? "#fff" : "rgba(255,255,255,0.3)",
|
|
3724
|
+
strokeWidth: drag.isDragging ? 2 : 1
|
|
3645
3725
|
}
|
|
3646
3726
|
),
|
|
3647
3727
|
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M 50,15 L 50,25 M 45,20 L 50,15 L 55,20", stroke: "white", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", fill: "none", opacity: "0.8", pointerEvents: "none" }),
|
|
@@ -4069,6 +4149,45 @@ function FloatingFullscreenButton({ onClick, disabled = false }) {
|
|
|
4069
4149
|
}
|
|
4070
4150
|
);
|
|
4071
4151
|
}
|
|
4152
|
+
function FloatingPauseButton({
|
|
4153
|
+
isPaused,
|
|
4154
|
+
onClick,
|
|
4155
|
+
disabled = false,
|
|
4156
|
+
systemColor = "#00FF41"
|
|
4157
|
+
}) {
|
|
4158
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4159
|
+
"button",
|
|
4160
|
+
{
|
|
4161
|
+
onClick,
|
|
4162
|
+
disabled,
|
|
4163
|
+
className: `
|
|
4164
|
+
fixed top-3 left-3 z-50
|
|
4165
|
+
px-3 py-2 rounded-xl
|
|
4166
|
+
bg-black/80 backdrop-blur-md
|
|
4167
|
+
border-2
|
|
4168
|
+
shadow-xl
|
|
4169
|
+
flex items-center gap-2
|
|
4170
|
+
transition-all duration-300
|
|
4171
|
+
hover:scale-105
|
|
4172
|
+
active:scale-95
|
|
4173
|
+
disabled:opacity-40 disabled:cursor-not-allowed
|
|
4174
|
+
touch-manipulation
|
|
4175
|
+
`,
|
|
4176
|
+
style: {
|
|
4177
|
+
paddingTop: "max(env(safe-area-inset-top, 0px), 8px)",
|
|
4178
|
+
borderColor: isPaused ? systemColor : "rgba(255,255,255,0.3)"
|
|
4179
|
+
},
|
|
4180
|
+
"aria-label": isPaused ? "Resume game" : "Pause game",
|
|
4181
|
+
children: isPaused ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
4182
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Play, { size: 16, style: { color: systemColor }, fill: systemColor }),
|
|
4183
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-white text-xs font-bold uppercase tracking-wider", children: "Play" })
|
|
4184
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
4185
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pause, { size: 16, className: "text-white/80" }),
|
|
4186
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-white text-xs font-bold uppercase tracking-wider", children: "Pause" })
|
|
4187
|
+
] })
|
|
4188
|
+
}
|
|
4189
|
+
);
|
|
4190
|
+
}
|
|
4072
4191
|
function LoadingSpinner({ color, size = "lg" }) {
|
|
4073
4192
|
const sizeClass = size === "lg" ? "w-12 h-12" : "w-8 h-8";
|
|
4074
4193
|
return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: `${sizeClass} animate-spin`, style: { color } });
|
|
@@ -9202,6 +9321,14 @@ var GamePlayerInner = React2.memo(function GamePlayerInner2(props) {
|
|
|
9202
9321
|
disabled: status === "loading" || status === "error"
|
|
9203
9322
|
}
|
|
9204
9323
|
),
|
|
9324
|
+
isFullscreen2 && isMobile && (status === "running" || status === "paused") && /* @__PURE__ */ jsxRuntime.jsx(
|
|
9325
|
+
FloatingPauseButton,
|
|
9326
|
+
{
|
|
9327
|
+
isPaused,
|
|
9328
|
+
onClick: handlePauseToggle,
|
|
9329
|
+
systemColor
|
|
9330
|
+
}
|
|
9331
|
+
),
|
|
9205
9332
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute top-2 right-2 z-40 flex flex-col items-end gap-2 pointer-events-auto", children: [
|
|
9206
9333
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9207
9334
|
RecordingIndicator_default,
|