@project-skymap/library 0.7.0 → 0.7.2
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.cjs +92 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +92 -16
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -103,6 +103,7 @@ type StarMapProps = {
|
|
|
103
103
|
onHover?: (node?: SceneNode) => void;
|
|
104
104
|
onArrangementChange?: (arrangement: StarArrangement) => void;
|
|
105
105
|
onFovChange?: (fov: number) => void;
|
|
106
|
+
onLongPress?: (node: SceneNode | null, x: number, y: number) => void;
|
|
106
107
|
};
|
|
107
108
|
type StarMapHandle = {
|
|
108
109
|
getFullArrangement: () => StarArrangement | undefined;
|
package/dist/index.d.ts
CHANGED
|
@@ -103,6 +103,7 @@ type StarMapProps = {
|
|
|
103
103
|
onHover?: (node?: SceneNode) => void;
|
|
104
104
|
onArrangementChange?: (arrangement: StarArrangement) => void;
|
|
105
105
|
onFovChange?: (fov: number) => void;
|
|
106
|
+
onLongPress?: (node: SceneNode | null, x: number, y: number) => void;
|
|
106
107
|
};
|
|
107
108
|
type StarMapHandle = {
|
|
108
109
|
getFullArrangement: () => StarArrangement | undefined;
|
package/dist/index.js
CHANGED
|
@@ -822,12 +822,19 @@ var createEngine_exports = {};
|
|
|
822
822
|
__export(createEngine_exports, {
|
|
823
823
|
createEngine: () => createEngine
|
|
824
824
|
});
|
|
825
|
+
function triggerHaptic(style = "light") {
|
|
826
|
+
if (typeof navigator !== "undefined" && "vibrate" in navigator) {
|
|
827
|
+
const durations = { light: 10, medium: 25, heavy: 50 };
|
|
828
|
+
navigator.vibrate(durations[style]);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
825
831
|
function createEngine({
|
|
826
832
|
container,
|
|
827
833
|
onSelect,
|
|
828
834
|
onHover,
|
|
829
835
|
onArrangementChange,
|
|
830
|
-
onFovChange
|
|
836
|
+
onFovChange,
|
|
837
|
+
onLongPress
|
|
831
838
|
}) {
|
|
832
839
|
let hoveredBookId = null;
|
|
833
840
|
let focusedBookId = null;
|
|
@@ -897,13 +904,20 @@ function createEngine({
|
|
|
897
904
|
pinchStartDistance: 0,
|
|
898
905
|
pinchStartFov: ENGINE_CONFIG.defaultFov,
|
|
899
906
|
pinchCenterX: 0,
|
|
900
|
-
pinchCenterY: 0
|
|
907
|
+
pinchCenterY: 0,
|
|
908
|
+
// Double-tap detection
|
|
909
|
+
lastTapTime: 0,
|
|
910
|
+
lastTapX: 0,
|
|
911
|
+
lastTapY: 0,
|
|
912
|
+
// Long-press detection
|
|
913
|
+
longPressTimer: null,
|
|
914
|
+
longPressTriggered: false
|
|
901
915
|
};
|
|
902
916
|
const mouseNDC = new THREE5.Vector2();
|
|
903
917
|
let isMouseInWindow = false;
|
|
904
918
|
let isTouchDevice = false;
|
|
905
919
|
let edgeHoverStart = 0;
|
|
906
|
-
let handlers = { onSelect, onHover, onArrangementChange, onFovChange };
|
|
920
|
+
let handlers = { onSelect, onHover, onArrangementChange, onFovChange, onLongPress };
|
|
907
921
|
let currentConfig;
|
|
908
922
|
const constellationLayer = new ConstellationArtworkLayer(scene);
|
|
909
923
|
function mix(a, b, t) {
|
|
@@ -2396,6 +2410,11 @@ function createEngine({
|
|
|
2396
2410
|
function onTouchStart(e) {
|
|
2397
2411
|
e.preventDefault();
|
|
2398
2412
|
isTouchDevice = true;
|
|
2413
|
+
if (state.longPressTimer) {
|
|
2414
|
+
clearTimeout(state.longPressTimer);
|
|
2415
|
+
state.longPressTimer = null;
|
|
2416
|
+
}
|
|
2417
|
+
state.longPressTriggered = false;
|
|
2399
2418
|
const touches = e.touches;
|
|
2400
2419
|
state.touchCount = touches.length;
|
|
2401
2420
|
if (touches.length === 1) {
|
|
@@ -2411,6 +2430,23 @@ function createEngine({
|
|
|
2411
2430
|
state.isDragging = true;
|
|
2412
2431
|
state.velocityX = 0;
|
|
2413
2432
|
state.velocityY = 0;
|
|
2433
|
+
state.longPressTimer = setTimeout(() => {
|
|
2434
|
+
if (!state.touchMoved && state.touchCount === 1) {
|
|
2435
|
+
state.longPressTriggered = true;
|
|
2436
|
+
const rect = renderer.domElement.getBoundingClientRect();
|
|
2437
|
+
const mX = state.touchStartX - rect.left;
|
|
2438
|
+
const mY = state.touchStartY - rect.top;
|
|
2439
|
+
mouseNDC.x = mX / rect.width * 2 - 1;
|
|
2440
|
+
mouseNDC.y = -(mY / rect.height) * 2 + 1;
|
|
2441
|
+
const syntheticEvent = {
|
|
2442
|
+
clientX: state.touchStartX,
|
|
2443
|
+
clientY: state.touchStartY
|
|
2444
|
+
};
|
|
2445
|
+
const hit = pick(syntheticEvent);
|
|
2446
|
+
triggerHaptic("heavy");
|
|
2447
|
+
handlers.onLongPress?.(hit?.node ?? null, state.touchStartX, state.touchStartY);
|
|
2448
|
+
}
|
|
2449
|
+
}, ENGINE_CONFIG.longPressDelay);
|
|
2414
2450
|
} else if (touches.length === 2) {
|
|
2415
2451
|
const t0 = touches[0];
|
|
2416
2452
|
const t1 = touches[1];
|
|
@@ -2437,6 +2473,10 @@ function createEngine({
|
|
|
2437
2473
|
const totalDy = touch.clientY - state.touchStartY;
|
|
2438
2474
|
if (Math.sqrt(totalDx * totalDx + totalDy * totalDy) > ENGINE_CONFIG.tapMaxDistance) {
|
|
2439
2475
|
state.touchMoved = true;
|
|
2476
|
+
if (state.longPressTimer) {
|
|
2477
|
+
clearTimeout(state.longPressTimer);
|
|
2478
|
+
state.longPressTimer = null;
|
|
2479
|
+
}
|
|
2440
2480
|
}
|
|
2441
2481
|
const speedScale = state.fov / ENGINE_CONFIG.defaultFov;
|
|
2442
2482
|
const rotLock = Math.max(0, Math.min(1, (state.fov - 100) / (ENGINE_CONFIG.maxFov - 100)));
|
|
@@ -2471,11 +2511,21 @@ function createEngine({
|
|
|
2471
2511
|
}
|
|
2472
2512
|
function onTouchEnd(e) {
|
|
2473
2513
|
e.preventDefault();
|
|
2514
|
+
if (state.longPressTimer) {
|
|
2515
|
+
clearTimeout(state.longPressTimer);
|
|
2516
|
+
state.longPressTimer = null;
|
|
2517
|
+
}
|
|
2474
2518
|
const remainingTouches = e.touches.length;
|
|
2475
2519
|
if (remainingTouches === 0) {
|
|
2476
|
-
const
|
|
2477
|
-
const
|
|
2520
|
+
const now = performance.now();
|
|
2521
|
+
const duration = now - state.touchStartTime;
|
|
2522
|
+
const wasTap = !state.touchMoved && duration < ENGINE_CONFIG.tapMaxDuration && !state.longPressTriggered;
|
|
2478
2523
|
if (wasTap) {
|
|
2524
|
+
const timeSinceLastTap = now - state.lastTapTime;
|
|
2525
|
+
const distFromLastTap = Math.sqrt(
|
|
2526
|
+
Math.pow(state.touchStartX - state.lastTapX, 2) + Math.pow(state.touchStartY - state.lastTapY, 2)
|
|
2527
|
+
);
|
|
2528
|
+
const isDoubleTap = timeSinceLastTap < ENGINE_CONFIG.doubleTapMaxDelay && distFromLastTap < ENGINE_CONFIG.doubleTapMaxDistance;
|
|
2479
2529
|
const rect = renderer.domElement.getBoundingClientRect();
|
|
2480
2530
|
const mX = state.touchStartX - rect.left;
|
|
2481
2531
|
const mY = state.touchStartY - rect.top;
|
|
@@ -2486,13 +2536,28 @@ function createEngine({
|
|
|
2486
2536
|
clientY: state.touchStartY
|
|
2487
2537
|
};
|
|
2488
2538
|
const hit = pick(syntheticEvent);
|
|
2489
|
-
if (
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2539
|
+
if (isDoubleTap) {
|
|
2540
|
+
if (hit) {
|
|
2541
|
+
triggerHaptic("medium");
|
|
2542
|
+
flyTo(hit.node.id, ENGINE_CONFIG.minFov);
|
|
2543
|
+
handlers.onSelect?.(hit.node);
|
|
2544
|
+
}
|
|
2545
|
+
state.lastTapTime = 0;
|
|
2546
|
+
state.lastTapX = 0;
|
|
2547
|
+
state.lastTapY = 0;
|
|
2494
2548
|
} else {
|
|
2495
|
-
|
|
2549
|
+
if (hit) {
|
|
2550
|
+
triggerHaptic("light");
|
|
2551
|
+
handlers.onSelect?.(hit.node);
|
|
2552
|
+
constellationLayer.setFocused(hit.node.id);
|
|
2553
|
+
if (hit.node.level === 2) setFocusedBook(hit.node.id);
|
|
2554
|
+
else if (hit.node.level === 3 && hit.node.parent) setFocusedBook(hit.node.parent);
|
|
2555
|
+
} else {
|
|
2556
|
+
setFocusedBook(null);
|
|
2557
|
+
}
|
|
2558
|
+
state.lastTapTime = now;
|
|
2559
|
+
state.lastTapX = state.touchStartX;
|
|
2560
|
+
state.lastTapY = state.touchStartY;
|
|
2496
2561
|
}
|
|
2497
2562
|
}
|
|
2498
2563
|
state.isDragging = false;
|
|
@@ -2511,6 +2576,10 @@ function createEngine({
|
|
|
2511
2576
|
}
|
|
2512
2577
|
function onTouchCancel(e) {
|
|
2513
2578
|
e.preventDefault();
|
|
2579
|
+
if (state.longPressTimer) {
|
|
2580
|
+
clearTimeout(state.longPressTimer);
|
|
2581
|
+
state.longPressTimer = null;
|
|
2582
|
+
}
|
|
2514
2583
|
state.isDragging = false;
|
|
2515
2584
|
state.dragMode = "none";
|
|
2516
2585
|
state.touchCount = 0;
|
|
@@ -2896,8 +2965,14 @@ var init_createEngine = __esm({
|
|
|
2896
2965
|
// Snappier than mouse (0.92)
|
|
2897
2966
|
tapMaxDuration: 300,
|
|
2898
2967
|
// ms
|
|
2899
|
-
tapMaxDistance: 10
|
|
2968
|
+
tapMaxDistance: 10,
|
|
2900
2969
|
// px
|
|
2970
|
+
doubleTapMaxDelay: 300,
|
|
2971
|
+
// ms between taps
|
|
2972
|
+
doubleTapMaxDistance: 30,
|
|
2973
|
+
// px between tap locations
|
|
2974
|
+
longPressDelay: 500
|
|
2975
|
+
// ms to trigger long-press
|
|
2901
2976
|
};
|
|
2902
2977
|
ORDER_REVEAL_CONFIG = {
|
|
2903
2978
|
globalDim: 0.85,
|
|
@@ -2908,7 +2983,7 @@ var init_createEngine = __esm({
|
|
|
2908
2983
|
}
|
|
2909
2984
|
});
|
|
2910
2985
|
var StarMap = forwardRef(
|
|
2911
|
-
({ config, className, onSelect, onHover, onArrangementChange, onFovChange }, ref) => {
|
|
2986
|
+
({ config, className, onSelect, onHover, onArrangementChange, onFovChange, onLongPress }, ref) => {
|
|
2912
2987
|
const containerRef = useRef(null);
|
|
2913
2988
|
const engineRef = useRef(null);
|
|
2914
2989
|
useImperativeHandle(ref, () => ({
|
|
@@ -2931,7 +3006,8 @@ var StarMap = forwardRef(
|
|
|
2931
3006
|
onSelect,
|
|
2932
3007
|
onHover,
|
|
2933
3008
|
onArrangementChange,
|
|
2934
|
-
onFovChange
|
|
3009
|
+
onFovChange,
|
|
3010
|
+
onLongPress
|
|
2935
3011
|
});
|
|
2936
3012
|
engineRef.current.setConfig(config);
|
|
2937
3013
|
engineRef.current.start();
|
|
@@ -2947,8 +3023,8 @@ var StarMap = forwardRef(
|
|
|
2947
3023
|
engineRef.current?.setConfig?.(config);
|
|
2948
3024
|
}, [config]);
|
|
2949
3025
|
useEffect(() => {
|
|
2950
|
-
engineRef.current?.setHandlers?.({ onSelect, onHover, onArrangementChange, onFovChange });
|
|
2951
|
-
}, [onSelect, onHover, onArrangementChange, onFovChange]);
|
|
3026
|
+
engineRef.current?.setHandlers?.({ onSelect, onHover, onArrangementChange, onFovChange, onLongPress });
|
|
3027
|
+
}, [onSelect, onHover, onArrangementChange, onFovChange, onLongPress]);
|
|
2952
3028
|
return /* @__PURE__ */ jsx("div", { ref: containerRef, className, style: { width: "100%", height: "100%" } });
|
|
2953
3029
|
}
|
|
2954
3030
|
);
|