@project-skymap/library 0.7.0 → 0.7.1

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.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
@@ -827,7 +827,8 @@ function createEngine({
827
827
  onSelect,
828
828
  onHover,
829
829
  onArrangementChange,
830
- onFovChange
830
+ onFovChange,
831
+ onLongPress
831
832
  }) {
832
833
  let hoveredBookId = null;
833
834
  let focusedBookId = null;
@@ -897,13 +898,20 @@ function createEngine({
897
898
  pinchStartDistance: 0,
898
899
  pinchStartFov: ENGINE_CONFIG.defaultFov,
899
900
  pinchCenterX: 0,
900
- pinchCenterY: 0
901
+ pinchCenterY: 0,
902
+ // Double-tap detection
903
+ lastTapTime: 0,
904
+ lastTapX: 0,
905
+ lastTapY: 0,
906
+ // Long-press detection
907
+ longPressTimer: null,
908
+ longPressTriggered: false
901
909
  };
902
910
  const mouseNDC = new THREE5.Vector2();
903
911
  let isMouseInWindow = false;
904
912
  let isTouchDevice = false;
905
913
  let edgeHoverStart = 0;
906
- let handlers = { onSelect, onHover, onArrangementChange, onFovChange };
914
+ let handlers = { onSelect, onHover, onArrangementChange, onFovChange, onLongPress };
907
915
  let currentConfig;
908
916
  const constellationLayer = new ConstellationArtworkLayer(scene);
909
917
  function mix(a, b, t) {
@@ -2396,6 +2404,11 @@ function createEngine({
2396
2404
  function onTouchStart(e) {
2397
2405
  e.preventDefault();
2398
2406
  isTouchDevice = true;
2407
+ if (state.longPressTimer) {
2408
+ clearTimeout(state.longPressTimer);
2409
+ state.longPressTimer = null;
2410
+ }
2411
+ state.longPressTriggered = false;
2399
2412
  const touches = e.touches;
2400
2413
  state.touchCount = touches.length;
2401
2414
  if (touches.length === 1) {
@@ -2411,6 +2424,22 @@ function createEngine({
2411
2424
  state.isDragging = true;
2412
2425
  state.velocityX = 0;
2413
2426
  state.velocityY = 0;
2427
+ state.longPressTimer = setTimeout(() => {
2428
+ if (!state.touchMoved && state.touchCount === 1) {
2429
+ state.longPressTriggered = true;
2430
+ const rect = renderer.domElement.getBoundingClientRect();
2431
+ const mX = state.touchStartX - rect.left;
2432
+ const mY = state.touchStartY - rect.top;
2433
+ mouseNDC.x = mX / rect.width * 2 - 1;
2434
+ mouseNDC.y = -(mY / rect.height) * 2 + 1;
2435
+ const syntheticEvent = {
2436
+ clientX: state.touchStartX,
2437
+ clientY: state.touchStartY
2438
+ };
2439
+ const hit = pick(syntheticEvent);
2440
+ handlers.onLongPress?.(hit?.node ?? null, state.touchStartX, state.touchStartY);
2441
+ }
2442
+ }, ENGINE_CONFIG.longPressDelay);
2414
2443
  } else if (touches.length === 2) {
2415
2444
  const t0 = touches[0];
2416
2445
  const t1 = touches[1];
@@ -2437,6 +2466,10 @@ function createEngine({
2437
2466
  const totalDy = touch.clientY - state.touchStartY;
2438
2467
  if (Math.sqrt(totalDx * totalDx + totalDy * totalDy) > ENGINE_CONFIG.tapMaxDistance) {
2439
2468
  state.touchMoved = true;
2469
+ if (state.longPressTimer) {
2470
+ clearTimeout(state.longPressTimer);
2471
+ state.longPressTimer = null;
2472
+ }
2440
2473
  }
2441
2474
  const speedScale = state.fov / ENGINE_CONFIG.defaultFov;
2442
2475
  const rotLock = Math.max(0, Math.min(1, (state.fov - 100) / (ENGINE_CONFIG.maxFov - 100)));
@@ -2471,11 +2504,21 @@ function createEngine({
2471
2504
  }
2472
2505
  function onTouchEnd(e) {
2473
2506
  e.preventDefault();
2507
+ if (state.longPressTimer) {
2508
+ clearTimeout(state.longPressTimer);
2509
+ state.longPressTimer = null;
2510
+ }
2474
2511
  const remainingTouches = e.touches.length;
2475
2512
  if (remainingTouches === 0) {
2476
- const duration = performance.now() - state.touchStartTime;
2477
- const wasTap = !state.touchMoved && duration < ENGINE_CONFIG.tapMaxDuration;
2513
+ const now = performance.now();
2514
+ const duration = now - state.touchStartTime;
2515
+ const wasTap = !state.touchMoved && duration < ENGINE_CONFIG.tapMaxDuration && !state.longPressTriggered;
2478
2516
  if (wasTap) {
2517
+ const timeSinceLastTap = now - state.lastTapTime;
2518
+ const distFromLastTap = Math.sqrt(
2519
+ Math.pow(state.touchStartX - state.lastTapX, 2) + Math.pow(state.touchStartY - state.lastTapY, 2)
2520
+ );
2521
+ const isDoubleTap = timeSinceLastTap < ENGINE_CONFIG.doubleTapMaxDelay && distFromLastTap < ENGINE_CONFIG.doubleTapMaxDistance;
2479
2522
  const rect = renderer.domElement.getBoundingClientRect();
2480
2523
  const mX = state.touchStartX - rect.left;
2481
2524
  const mY = state.touchStartY - rect.top;
@@ -2486,13 +2529,26 @@ function createEngine({
2486
2529
  clientY: state.touchStartY
2487
2530
  };
2488
2531
  const hit = pick(syntheticEvent);
2489
- if (hit) {
2490
- handlers.onSelect?.(hit.node);
2491
- constellationLayer.setFocused(hit.node.id);
2492
- if (hit.node.level === 2) setFocusedBook(hit.node.id);
2493
- else if (hit.node.level === 3 && hit.node.parent) setFocusedBook(hit.node.parent);
2532
+ if (isDoubleTap) {
2533
+ if (hit) {
2534
+ flyTo(hit.node.id, ENGINE_CONFIG.minFov);
2535
+ handlers.onSelect?.(hit.node);
2536
+ }
2537
+ state.lastTapTime = 0;
2538
+ state.lastTapX = 0;
2539
+ state.lastTapY = 0;
2494
2540
  } else {
2495
- setFocusedBook(null);
2541
+ if (hit) {
2542
+ handlers.onSelect?.(hit.node);
2543
+ constellationLayer.setFocused(hit.node.id);
2544
+ if (hit.node.level === 2) setFocusedBook(hit.node.id);
2545
+ else if (hit.node.level === 3 && hit.node.parent) setFocusedBook(hit.node.parent);
2546
+ } else {
2547
+ setFocusedBook(null);
2548
+ }
2549
+ state.lastTapTime = now;
2550
+ state.lastTapX = state.touchStartX;
2551
+ state.lastTapY = state.touchStartY;
2496
2552
  }
2497
2553
  }
2498
2554
  state.isDragging = false;
@@ -2511,6 +2567,10 @@ function createEngine({
2511
2567
  }
2512
2568
  function onTouchCancel(e) {
2513
2569
  e.preventDefault();
2570
+ if (state.longPressTimer) {
2571
+ clearTimeout(state.longPressTimer);
2572
+ state.longPressTimer = null;
2573
+ }
2514
2574
  state.isDragging = false;
2515
2575
  state.dragMode = "none";
2516
2576
  state.touchCount = 0;
@@ -2896,8 +2956,14 @@ var init_createEngine = __esm({
2896
2956
  // Snappier than mouse (0.92)
2897
2957
  tapMaxDuration: 300,
2898
2958
  // ms
2899
- tapMaxDistance: 10
2959
+ tapMaxDistance: 10,
2900
2960
  // px
2961
+ doubleTapMaxDelay: 300,
2962
+ // ms between taps
2963
+ doubleTapMaxDistance: 30,
2964
+ // px between tap locations
2965
+ longPressDelay: 500
2966
+ // ms to trigger long-press
2901
2967
  };
2902
2968
  ORDER_REVEAL_CONFIG = {
2903
2969
  globalDim: 0.85,
@@ -2908,7 +2974,7 @@ var init_createEngine = __esm({
2908
2974
  }
2909
2975
  });
2910
2976
  var StarMap = forwardRef(
2911
- ({ config, className, onSelect, onHover, onArrangementChange, onFovChange }, ref) => {
2977
+ ({ config, className, onSelect, onHover, onArrangementChange, onFovChange, onLongPress }, ref) => {
2912
2978
  const containerRef = useRef(null);
2913
2979
  const engineRef = useRef(null);
2914
2980
  useImperativeHandle(ref, () => ({
@@ -2931,7 +2997,8 @@ var StarMap = forwardRef(
2931
2997
  onSelect,
2932
2998
  onHover,
2933
2999
  onArrangementChange,
2934
- onFovChange
3000
+ onFovChange,
3001
+ onLongPress
2935
3002
  });
2936
3003
  engineRef.current.setConfig(config);
2937
3004
  engineRef.current.start();
@@ -2947,8 +3014,8 @@ var StarMap = forwardRef(
2947
3014
  engineRef.current?.setConfig?.(config);
2948
3015
  }, [config]);
2949
3016
  useEffect(() => {
2950
- engineRef.current?.setHandlers?.({ onSelect, onHover, onArrangementChange, onFovChange });
2951
- }, [onSelect, onHover, onArrangementChange, onFovChange]);
3017
+ engineRef.current?.setHandlers?.({ onSelect, onHover, onArrangementChange, onFovChange, onLongPress });
3018
+ }, [onSelect, onHover, onArrangementChange, onFovChange, onLongPress]);
2952
3019
  return /* @__PURE__ */ jsx("div", { ref: containerRef, className, style: { width: "100%", height: "100%" } });
2953
3020
  }
2954
3021
  );