@cocorof/graphier 1.4.1 → 1.4.3

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 CHANGED
@@ -39,12 +39,14 @@ const ref = useRef<NetworkGraph3DRef>(null);
39
39
  <GraphMinimap graphRef={ref} width={200} height={140} />
40
40
  ```
41
41
 
42
- - `layout.dimensions: 2` — simulation runs in 2D (z locked to 0); camera locks to pan/zoom (drag pans, wheel/pinch zooms, rotation off).
42
+ - `layout.dimensions: 2` — flat Obsidian-style plane: simulation runs in 2D (z locked to 0). Left-drag pans, right-drag tilts/orbits the world, wheel/pinch zooms, arrows/WASD pan and z/x zoom (v1.4.1).
43
+ - `enableNodeDrag={false}` — left-drag always reaches the camera even over nodes; essential for dense graphs (v1.4.1).
43
44
  - `layout.clusterBy: "type" | "group"` + `clusterStrength` — pulls same-key nodes toward a shared centroid so categories form visible clusters.
44
45
  - `linkVisibility={(link) => bool}` — reheat-free per-edge filter (e.g. hide a link type).
45
46
  - `visibleNodeIds` — client-side filter; hidden nodes/edges/labels vanish via per-instance scale + collapsed segments. Positions are preserved: toggling filters never reheats the simulation.
46
47
  - `hoverHighlight` / `hoverHighlightHops` — Obsidian-style neighborhood emphasis on hover (selection wins while active).
47
48
  - `theme="paper"` — light background preset; edge blending switches to normal automatically (additive lines vanish on white) and highlight/dim directions invert.
49
+ - `renderer.navigation` — remap pointer/keyboard navigation per consumer, e.g. `{ leftButton: "pan", rightButton: "rotate", keyboard: "pan" }` for pan-first 3D graphs (v1.4.2).
48
50
  - `GraphMinimap` — 2D-canvas overview (no second WebGL context): draws all visible nodes + the camera viewport rectangle, click/drag to pan. Powered by `ref.getGraphSnapshot()` / `ref.getViewportRect()` / `ref.panTo(x, y)`.
49
51
 
50
52
  ## Install
package/dist/index.cjs CHANGED
@@ -2231,6 +2231,14 @@ function createScene(container, backgroundColor, style, rendererConfig) {
2231
2231
  renderer.toneMappingExposure = 1.2;
2232
2232
  const canvas = renderer.domElement;
2233
2233
  canvas.style.setProperty("display", "block", "important");
2234
+ canvas.draggable = false;
2235
+ canvas.style.userSelect = "none";
2236
+ canvas.style.setProperty("-webkit-user-drag", "none");
2237
+ canvas.style.touchAction = "none";
2238
+ const onDragStart = (e) => e.preventDefault();
2239
+ canvas.addEventListener("dragstart", onDragStart);
2240
+ const onSelectStart = (e) => e.preventDefault();
2241
+ canvas.addEventListener("selectstart", onSelectStart);
2234
2242
  container.appendChild(canvas);
2235
2243
  const controls = new OrbitControls(camera, canvas);
2236
2244
  controls.enableDamping = true;
@@ -2268,7 +2276,11 @@ function createScene(container, backgroundColor, style, rendererConfig) {
2268
2276
  controls.target.addScaledVector(_wheelDir, moveAmount);
2269
2277
  };
2270
2278
  canvas.addEventListener("wheel", onWheel, { passive: false });
2271
- const scrollCleanup = () => canvas.removeEventListener("wheel", onWheel);
2279
+ const scrollCleanup = () => {
2280
+ canvas.removeEventListener("wheel", onWheel);
2281
+ canvas.removeEventListener("dragstart", onDragStart);
2282
+ canvas.removeEventListener("selectstart", onSelectStart);
2283
+ };
2272
2284
  return {
2273
2285
  scene,
2274
2286
  camera,
@@ -2284,34 +2296,38 @@ function createScene(container, backgroundColor, style, rendererConfig) {
2284
2296
  flags
2285
2297
  };
2286
2298
  }
2287
- function applyCameraMode2D(state, is2D) {
2299
+ const MOUSE_ACTION = {
2300
+ rotate: THREE__namespace.MOUSE.ROTATE,
2301
+ pan: THREE__namespace.MOUSE.PAN
2302
+ };
2303
+ function applyNavigationMode(state, is2D, nav) {
2288
2304
  const { controls, camera, keyboard } = state;
2289
2305
  state.flags.is2D = is2D;
2290
- controls.enableRotate = true;
2306
+ const left = (nav == null ? void 0 : nav.leftButton) ?? (is2D ? "pan" : "rotate");
2307
+ const right = (nav == null ? void 0 : nav.rightButton) ?? (is2D ? "rotate" : "pan");
2308
+ controls.mouseButtons = {
2309
+ LEFT: MOUSE_ACTION[left],
2310
+ MIDDLE: THREE__namespace.MOUSE.DOLLY,
2311
+ RIGHT: MOUSE_ACTION[right]
2312
+ };
2313
+ controls.enableRotate = left === "rotate" || right === "rotate";
2314
+ controls.touches = left === "pan" ? { ONE: THREE__namespace.TOUCH.PAN, TWO: THREE__namespace.TOUCH.DOLLY_PAN } : { ONE: THREE__namespace.TOUCH.ROTATE, TWO: THREE__namespace.TOUCH.DOLLY_PAN };
2291
2315
  controls.enableZoom = is2D;
2292
2316
  controls.screenSpacePanning = is2D;
2293
- keyboard.setEnabled(true);
2294
- keyboard.setMode(is2D ? "pan" : state.flags.keyboardMode3D);
2317
+ const kb = (nav == null ? void 0 : nav.keyboard) ?? (is2D ? "pan" : state.flags.keyboardMode3D);
2318
+ if (kb === "off") {
2319
+ keyboard.setEnabled(false);
2320
+ } else {
2321
+ keyboard.setEnabled(true);
2322
+ keyboard.setMode(kb);
2323
+ }
2295
2324
  if (is2D) {
2296
- controls.mouseButtons = {
2297
- LEFT: THREE__namespace.MOUSE.PAN,
2298
- MIDDLE: THREE__namespace.MOUSE.DOLLY,
2299
- RIGHT: THREE__namespace.MOUSE.ROTATE
2300
- };
2301
- controls.touches = { ONE: THREE__namespace.TOUCH.PAN, TWO: THREE__namespace.TOUCH.DOLLY_PAN };
2302
2325
  const t = controls.target;
2303
2326
  const dist = Math.max(camera.position.distanceTo(t), 50);
2304
2327
  t.z = 0;
2305
2328
  camera.up.set(0, 1, 0);
2306
2329
  camera.position.set(t.x, t.y, dist);
2307
2330
  camera.lookAt(t);
2308
- } else {
2309
- controls.mouseButtons = {
2310
- LEFT: THREE__namespace.MOUSE.ROTATE,
2311
- MIDDLE: THREE__namespace.MOUSE.DOLLY,
2312
- RIGHT: THREE__namespace.MOUSE.PAN
2313
- };
2314
- controls.touches = { ONE: THREE__namespace.TOUCH.ROTATE, TWO: THREE__namespace.TOUCH.DOLLY_PAN };
2315
2331
  }
2316
2332
  controls.update();
2317
2333
  }
@@ -3518,10 +3534,11 @@ function NetworkGraph3DInner(props, ref) {
3518
3534
  );
3519
3535
  }
3520
3536
  }, [visibleSet, linkVisibility, mergedData]);
3537
+ const navKey = JSON.stringify((rendererProp == null ? void 0 : rendererProp.navigation) ?? null);
3521
3538
  react.useEffect(() => {
3522
3539
  const s = sceneRef.current;
3523
- if (s) applyCameraMode2D(s, is2D);
3524
- }, [is2D]);
3540
+ if (s) applyNavigationMode(s, is2D, rendererProp == null ? void 0 : rendererProp.navigation);
3541
+ }, [is2D, navKey]);
3525
3542
  react.useEffect(() => {
3526
3543
  const gObj = graphObjRef.current;
3527
3544
  const sceneState = sceneRef.current;
@@ -3773,11 +3790,12 @@ function NetworkGraph3DInner(props, ref) {
3773
3790
  const s = sceneRef.current;
3774
3791
  if (!s) return null;
3775
3792
  const cam = s.camera;
3776
- const dist = Math.abs(cam.position.z);
3793
+ const t = s.controls.target;
3794
+ const dist = Math.max(cam.position.distanceTo(t), 1);
3777
3795
  const halfH = Math.tan(cam.fov * Math.PI / 360) * dist;
3778
3796
  return {
3779
- cx: cam.position.x,
3780
- cy: cam.position.y,
3797
+ cx: t.x,
3798
+ cy: t.y,
3781
3799
  halfW: halfH * cam.aspect,
3782
3800
  halfH
3783
3801
  };
@@ -3798,7 +3816,14 @@ function NetworkGraph3DInner(props, ref) {
3798
3816
  {
3799
3817
  ref: containerRef,
3800
3818
  tabIndex: -1,
3801
- style: { width: "100%", height: "100%", outline: "none" }
3819
+ style: {
3820
+ width: "100%",
3821
+ height: "100%",
3822
+ outline: "none",
3823
+ userSelect: "none",
3824
+ WebkitUserSelect: "none"
3825
+ },
3826
+ onDragStart: (e) => e.preventDefault()
3802
3827
  }
3803
3828
  );
3804
3829
  }