@cocorof/graphier 1.4.0 → 1.4.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/README.md +3 -1
- package/dist/index.cjs +90 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +25 -0
- package/dist/index.js +90 -27
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -174,6 +174,19 @@ export declare type LinkEventHandler = (link: GraphLink) => void;
|
|
|
174
174
|
|
|
175
175
|
export declare const minimal: ThemeConfig;
|
|
176
176
|
|
|
177
|
+
export declare interface NavigationConfig {
|
|
178
|
+
/** What left-drag does (default: "rotate" in 3D, "pan" in 2D) */
|
|
179
|
+
leftButton?: "rotate" | "pan";
|
|
180
|
+
/** What right-drag does (default: "pan" in 3D, "rotate" in 2D) */
|
|
181
|
+
rightButton?: "rotate" | "pan";
|
|
182
|
+
/**
|
|
183
|
+
* Keyboard scheme — "pan": arrows/WASD pan + z/x zoom; "fly": thrust
|
|
184
|
+
* flight; "orbit": orbit around target; "off": disabled.
|
|
185
|
+
* (default: cameraMode in 3D, "pan" in 2D)
|
|
186
|
+
*/
|
|
187
|
+
keyboard?: "fly" | "orbit" | "pan" | "off";
|
|
188
|
+
}
|
|
189
|
+
|
|
177
190
|
export declare const neon: ThemeConfig;
|
|
178
191
|
|
|
179
192
|
export declare const NetworkGraph3D: ForwardRefExoticComponent<NetworkGraph3DProps & RefAttributes<NetworkGraph3DRef>>;
|
|
@@ -230,6 +243,12 @@ export declare interface NetworkGraph3DProps {
|
|
|
230
243
|
* simulation untouched. null/undefined shows all edges.
|
|
231
244
|
*/
|
|
232
245
|
linkVisibility?: ((link: GraphLink) => boolean) | null;
|
|
246
|
+
/**
|
|
247
|
+
* Allow dragging individual nodes (default: true). Set false on dense
|
|
248
|
+
* graphs so left-drag always pans/orbits the camera instead of grabbing
|
|
249
|
+
* whichever node happens to be under the pointer.
|
|
250
|
+
*/
|
|
251
|
+
enableNodeDrag?: boolean;
|
|
233
252
|
/** Fly the camera to a node on single-click (default: true) */
|
|
234
253
|
clickToFocus?: boolean;
|
|
235
254
|
/** Highlight a node's neighborhood on hover when nothing is selected (default: false) */
|
|
@@ -335,6 +354,12 @@ export declare interface RendererConfig {
|
|
|
335
354
|
* - "orbit": z/x zoom, arrow keys orbit around target
|
|
336
355
|
*/
|
|
337
356
|
cameraMode?: "fly" | "orbit";
|
|
357
|
+
/**
|
|
358
|
+
* Pointer/keyboard navigation overrides. Defaults depend on layout
|
|
359
|
+
* dimensionality — 3D: left=rotate, right=pan, keyboard=cameraMode;
|
|
360
|
+
* 2D: left=pan, right=rotate, keyboard="pan".
|
|
361
|
+
*/
|
|
362
|
+
navigation?: NavigationConfig;
|
|
338
363
|
}
|
|
339
364
|
|
|
340
365
|
export declare interface ResolvedTheme {
|
package/dist/index.js
CHANGED
|
@@ -2031,6 +2031,45 @@ const _right = new THREE.Vector3();
|
|
|
2031
2031
|
const _q = new THREE.Quaternion();
|
|
2032
2032
|
const _dir = new THREE.Vector3();
|
|
2033
2033
|
const _worldUp = new THREE.Vector3(0, 1, 0);
|
|
2034
|
+
const PAN_SPEED = 0.02;
|
|
2035
|
+
const PAN_ZOOM_FACTOR = 0.02;
|
|
2036
|
+
function createPanUpdate(camera, controls, keys) {
|
|
2037
|
+
const _panRight = new THREE.Vector3();
|
|
2038
|
+
const _panUp = new THREE.Vector3();
|
|
2039
|
+
const _panOffset = new THREE.Vector3();
|
|
2040
|
+
return function update() {
|
|
2041
|
+
let dx = 0;
|
|
2042
|
+
let dy = 0;
|
|
2043
|
+
if (keys["ArrowLeft"] || keys["a"] || keys["A"]) dx -= 1;
|
|
2044
|
+
if (keys["ArrowRight"] || keys["d"] || keys["D"]) dx += 1;
|
|
2045
|
+
if (keys["ArrowUp"] || keys["w"] || keys["W"]) dy += 1;
|
|
2046
|
+
if (keys["ArrowDown"] || keys["s"] || keys["S"]) dy -= 1;
|
|
2047
|
+
const zoomIn = keys["z"] || keys["Z"] || keys["+"] || keys["="];
|
|
2048
|
+
const zoomOut = keys["x"] || keys["X"] || keys["-"] || keys["_"];
|
|
2049
|
+
if (!dx && !dy && !zoomIn && !zoomOut) return false;
|
|
2050
|
+
const target = controls.target;
|
|
2051
|
+
const dist = camera.position.distanceTo(target);
|
|
2052
|
+
if (dx || dy) {
|
|
2053
|
+
const step = Math.max(dist, 50) * PAN_SPEED;
|
|
2054
|
+
_panRight.setFromMatrixColumn(camera.matrix, 0);
|
|
2055
|
+
_panUp.setFromMatrixColumn(camera.matrix, 1);
|
|
2056
|
+
_panOffset.set(0, 0, 0).addScaledVector(_panRight, dx * step).addScaledVector(_panUp, dy * step);
|
|
2057
|
+
camera.position.add(_panOffset);
|
|
2058
|
+
target.add(_panOffset);
|
|
2059
|
+
}
|
|
2060
|
+
if (zoomIn || zoomOut) {
|
|
2061
|
+
const factor = zoomIn ? -PAN_ZOOM_FACTOR : PAN_ZOOM_FACTOR;
|
|
2062
|
+
_panOffset.copy(camera.position).sub(target);
|
|
2063
|
+
const newLen = Math.max(
|
|
2064
|
+
controls.minDistance,
|
|
2065
|
+
Math.min(controls.maxDistance, _panOffset.length() * (1 + factor))
|
|
2066
|
+
);
|
|
2067
|
+
_panOffset.setLength(newLen);
|
|
2068
|
+
camera.position.copy(target).add(_panOffset);
|
|
2069
|
+
}
|
|
2070
|
+
return true;
|
|
2071
|
+
};
|
|
2072
|
+
}
|
|
2034
2073
|
function createOrbitUpdate(camera, controls, keys) {
|
|
2035
2074
|
return function update() {
|
|
2036
2075
|
let zoom = 0;
|
|
@@ -2130,8 +2169,13 @@ function setupKeyboardControls(camera, controls, container, mode = "fly", flySpe
|
|
|
2130
2169
|
container.addEventListener("keyup", onKeyUp);
|
|
2131
2170
|
container.addEventListener("blur", onBlur);
|
|
2132
2171
|
let enabled = true;
|
|
2133
|
-
|
|
2134
|
-
const
|
|
2172
|
+
let currentMode = mode;
|
|
2173
|
+
const updaters = {
|
|
2174
|
+
fly: createFlyUpdate(camera, controls, keys, state),
|
|
2175
|
+
orbit: createOrbitUpdate(camera, controls, keys),
|
|
2176
|
+
pan: createPanUpdate(camera, controls, keys)
|
|
2177
|
+
};
|
|
2178
|
+
const updateFn = () => enabled ? updaters[currentMode]() : false;
|
|
2135
2179
|
function cleanup() {
|
|
2136
2180
|
container.removeEventListener("keydown", onKeyDown2);
|
|
2137
2181
|
container.removeEventListener("keyup", onKeyUp);
|
|
@@ -2144,7 +2188,11 @@ function setupKeyboardControls(camera, controls, container, mode = "fly", flySpe
|
|
|
2144
2188
|
enabled = on;
|
|
2145
2189
|
if (!on) for (const k of Object.keys(keys)) keys[k] = false;
|
|
2146
2190
|
}
|
|
2147
|
-
|
|
2191
|
+
function setMode(m2) {
|
|
2192
|
+
currentMode = m2;
|
|
2193
|
+
for (const k of Object.keys(keys)) keys[k] = false;
|
|
2194
|
+
}
|
|
2195
|
+
return { update: updateFn, cleanup, setFlySpeed, setEnabled, setMode };
|
|
2148
2196
|
}
|
|
2149
2197
|
function createScene(container, backgroundColor, style, rendererConfig) {
|
|
2150
2198
|
const scene = new THREE.Scene();
|
|
@@ -2184,7 +2232,10 @@ function createScene(container, backgroundColor, style, rendererConfig) {
|
|
|
2184
2232
|
const cameraMode = (rendererConfig == null ? void 0 : rendererConfig.cameraMode) ?? "fly";
|
|
2185
2233
|
const keyboard = setupKeyboardControls(camera, controls, container, cameraMode, style.flySpeed ?? 1);
|
|
2186
2234
|
controls.enableZoom = false;
|
|
2187
|
-
const flags = {
|
|
2235
|
+
const flags = {
|
|
2236
|
+
is2D: false,
|
|
2237
|
+
keyboardMode3D: cameraMode
|
|
2238
|
+
};
|
|
2188
2239
|
const _wheelDir = new THREE.Vector3();
|
|
2189
2240
|
const onWheel = (e) => {
|
|
2190
2241
|
if (flags.is2D) return;
|
|
@@ -2215,33 +2266,38 @@ function createScene(container, backgroundColor, style, rendererConfig) {
|
|
|
2215
2266
|
flags
|
|
2216
2267
|
};
|
|
2217
2268
|
}
|
|
2218
|
-
|
|
2269
|
+
const MOUSE_ACTION = {
|
|
2270
|
+
rotate: THREE.MOUSE.ROTATE,
|
|
2271
|
+
pan: THREE.MOUSE.PAN
|
|
2272
|
+
};
|
|
2273
|
+
function applyNavigationMode(state, is2D, nav) {
|
|
2219
2274
|
const { controls, camera, keyboard } = state;
|
|
2220
2275
|
state.flags.is2D = is2D;
|
|
2221
|
-
|
|
2276
|
+
const left = (nav == null ? void 0 : nav.leftButton) ?? (is2D ? "pan" : "rotate");
|
|
2277
|
+
const right = (nav == null ? void 0 : nav.rightButton) ?? (is2D ? "rotate" : "pan");
|
|
2278
|
+
controls.mouseButtons = {
|
|
2279
|
+
LEFT: MOUSE_ACTION[left],
|
|
2280
|
+
MIDDLE: THREE.MOUSE.DOLLY,
|
|
2281
|
+
RIGHT: MOUSE_ACTION[right]
|
|
2282
|
+
};
|
|
2283
|
+
controls.enableRotate = left === "rotate" || right === "rotate";
|
|
2284
|
+
controls.touches = left === "pan" ? { ONE: THREE.TOUCH.PAN, TWO: THREE.TOUCH.DOLLY_PAN } : { ONE: THREE.TOUCH.ROTATE, TWO: THREE.TOUCH.DOLLY_PAN };
|
|
2222
2285
|
controls.enableZoom = is2D;
|
|
2223
2286
|
controls.screenSpacePanning = is2D;
|
|
2224
|
-
keyboard
|
|
2287
|
+
const kb = (nav == null ? void 0 : nav.keyboard) ?? (is2D ? "pan" : state.flags.keyboardMode3D);
|
|
2288
|
+
if (kb === "off") {
|
|
2289
|
+
keyboard.setEnabled(false);
|
|
2290
|
+
} else {
|
|
2291
|
+
keyboard.setEnabled(true);
|
|
2292
|
+
keyboard.setMode(kb);
|
|
2293
|
+
}
|
|
2225
2294
|
if (is2D) {
|
|
2226
|
-
controls.mouseButtons = {
|
|
2227
|
-
LEFT: THREE.MOUSE.PAN,
|
|
2228
|
-
MIDDLE: THREE.MOUSE.DOLLY,
|
|
2229
|
-
RIGHT: THREE.MOUSE.PAN
|
|
2230
|
-
};
|
|
2231
|
-
controls.touches = { ONE: THREE.TOUCH.PAN, TWO: THREE.TOUCH.DOLLY_PAN };
|
|
2232
2295
|
const t = controls.target;
|
|
2233
2296
|
const dist = Math.max(camera.position.distanceTo(t), 50);
|
|
2234
2297
|
t.z = 0;
|
|
2235
2298
|
camera.up.set(0, 1, 0);
|
|
2236
2299
|
camera.position.set(t.x, t.y, dist);
|
|
2237
2300
|
camera.lookAt(t);
|
|
2238
|
-
} else {
|
|
2239
|
-
controls.mouseButtons = {
|
|
2240
|
-
LEFT: THREE.MOUSE.ROTATE,
|
|
2241
|
-
MIDDLE: THREE.MOUSE.DOLLY,
|
|
2242
|
-
RIGHT: THREE.MOUSE.PAN
|
|
2243
|
-
};
|
|
2244
|
-
controls.touches = { ONE: THREE.TOUCH.ROTATE, TWO: THREE.TOUCH.DOLLY_PAN };
|
|
2245
2301
|
}
|
|
2246
2302
|
controls.update();
|
|
2247
2303
|
}
|
|
@@ -2604,11 +2660,12 @@ function setupInteraction(canvas, camera, controls, getNodesMesh, getNodes, call
|
|
|
2604
2660
|
const dragPlane = new THREE.Plane();
|
|
2605
2661
|
const dragIntersect = new THREE.Vector3();
|
|
2606
2662
|
function onPointerDown2(e) {
|
|
2663
|
+
var _a;
|
|
2607
2664
|
mouseDownPos = { x: e.clientX, y: e.clientY };
|
|
2608
2665
|
if (container && document.activeElement !== container) {
|
|
2609
2666
|
container.focus({ preventScroll: true });
|
|
2610
2667
|
}
|
|
2611
|
-
if (callbacks.onNodeDrag) {
|
|
2668
|
+
if (callbacks.onNodeDrag && (((_a = options == null ? void 0 : options.getNodeDragEnabled) == null ? void 0 : _a.call(options)) ?? true)) {
|
|
2612
2669
|
const nodesMesh = getNodesMesh();
|
|
2613
2670
|
if (nodesMesh) {
|
|
2614
2671
|
const rect = canvas.getBoundingClientRect();
|
|
@@ -2920,6 +2977,7 @@ function NetworkGraph3DInner(props, ref) {
|
|
|
2920
2977
|
nodeValueAccessor,
|
|
2921
2978
|
visibleNodeIds,
|
|
2922
2979
|
linkVisibility,
|
|
2980
|
+
enableNodeDrag = true,
|
|
2923
2981
|
clickToFocus = true,
|
|
2924
2982
|
hoverHighlight = false,
|
|
2925
2983
|
hoverHighlightHops = 1
|
|
@@ -2960,6 +3018,7 @@ function NetworkGraph3DInner(props, ref) {
|
|
|
2960
3018
|
const labelFormatterRef = useRef(labelFormatter);
|
|
2961
3019
|
const highlightSetRef = useRef(null);
|
|
2962
3020
|
const clickToFocusRef = useRef(clickToFocus);
|
|
3021
|
+
const enableNodeDragRef = useRef(enableNodeDrag);
|
|
2963
3022
|
const hoverHighlightRef = useRef(hoverHighlight);
|
|
2964
3023
|
const hoverHighlightHopsRef = useRef(hoverHighlightHops);
|
|
2965
3024
|
const adjacencyMapRef = useRef(/* @__PURE__ */ new Map());
|
|
@@ -2981,6 +3040,7 @@ function NetworkGraph3DInner(props, ref) {
|
|
|
2981
3040
|
selectedNodeIdRef.current = selectedNodeId;
|
|
2982
3041
|
labelFormatterRef.current = labelFormatter;
|
|
2983
3042
|
clickToFocusRef.current = clickToFocus;
|
|
3043
|
+
enableNodeDragRef.current = enableNodeDrag;
|
|
2984
3044
|
hoverHighlightRef.current = hoverHighlight;
|
|
2985
3045
|
hoverHighlightHopsRef.current = hoverHighlightHops;
|
|
2986
3046
|
const is2D = (layoutProp == null ? void 0 : layoutProp.dimensions) === 2;
|
|
@@ -3163,7 +3223,8 @@ function NetworkGraph3DInner(props, ref) {
|
|
|
3163
3223
|
},
|
|
3164
3224
|
{
|
|
3165
3225
|
getClickToFocus: () => clickToFocusRef.current,
|
|
3166
|
-
getIs2D: () => is2DRef.current
|
|
3226
|
+
getIs2D: () => is2DRef.current,
|
|
3227
|
+
getNodeDragEnabled: () => enableNodeDragRef.current
|
|
3167
3228
|
}
|
|
3168
3229
|
);
|
|
3169
3230
|
return () => {
|
|
@@ -3443,10 +3504,11 @@ function NetworkGraph3DInner(props, ref) {
|
|
|
3443
3504
|
);
|
|
3444
3505
|
}
|
|
3445
3506
|
}, [visibleSet, linkVisibility, mergedData]);
|
|
3507
|
+
const navKey = JSON.stringify((rendererProp == null ? void 0 : rendererProp.navigation) ?? null);
|
|
3446
3508
|
useEffect(() => {
|
|
3447
3509
|
const s = sceneRef.current;
|
|
3448
|
-
if (s)
|
|
3449
|
-
}, [is2D]);
|
|
3510
|
+
if (s) applyNavigationMode(s, is2D, rendererProp == null ? void 0 : rendererProp.navigation);
|
|
3511
|
+
}, [is2D, navKey]);
|
|
3450
3512
|
useEffect(() => {
|
|
3451
3513
|
const gObj = graphObjRef.current;
|
|
3452
3514
|
const sceneState = sceneRef.current;
|
|
@@ -3698,11 +3760,12 @@ function NetworkGraph3DInner(props, ref) {
|
|
|
3698
3760
|
const s = sceneRef.current;
|
|
3699
3761
|
if (!s) return null;
|
|
3700
3762
|
const cam = s.camera;
|
|
3701
|
-
const
|
|
3763
|
+
const t = s.controls.target;
|
|
3764
|
+
const dist = Math.max(cam.position.distanceTo(t), 1);
|
|
3702
3765
|
const halfH = Math.tan(cam.fov * Math.PI / 360) * dist;
|
|
3703
3766
|
return {
|
|
3704
|
-
cx:
|
|
3705
|
-
cy:
|
|
3767
|
+
cx: t.x,
|
|
3768
|
+
cy: t.y,
|
|
3706
3769
|
halfW: halfH * cam.aspect,
|
|
3707
3770
|
halfH
|
|
3708
3771
|
};
|