• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

graphty-org / graphty-element / 16281745178

15 Jul 2025 01:24AM UTC coverage: 70.353% (-2.2%) from 72.536%
16281745178

push

github

apowers313
fix: build and optional argument fixes

735 of 934 branches covered (78.69%)

Branch coverage included in aggregate %.

7 of 8 new or added lines in 2 files covered. (87.5%)

878 existing lines in 24 files now uncovered.

4742 of 6851 relevant lines covered (69.22%)

311.14 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

48.75
/src/NodeBehavior.ts
1
import {
2✔
2
    ActionManager,
2✔
3
    ExecuteCodeAction,
2✔
4
    SixDofDragBehavior,
2✔
5
} from "@babylonjs/core";
2✔
6

7
import type {Graph} from "./Graph";
8
import type {GraphContext} from "./managers/GraphContext";
9
import type {Node as GraphNode, NodeIdType} from "./Node";
10

11
interface NodeBehaviorOptions {
12
    pinOnDrag?: boolean;
13
}
14

15
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
16
export class NodeBehavior {
2✔
17
    /**
18
     * Add default interaction behaviors to a node
19
     */
20
    static addDefaultBehaviors(node: GraphNode, options: NodeBehaviorOptions = {}): void {
2✔
21
        node.mesh.isPickable = true;
1,222✔
22

23
        this.addDragBehavior(node, options);
1,222✔
24
        this.addClickBehavior(node);
1,222✔
25
    }
1,222✔
26

27
    /**
28
     * Add drag behavior to a node
29
     */
30
    private static addDragBehavior(node: GraphNode, options: NodeBehaviorOptions): void {
2✔
31
        // drag behavior setup
32
        node.pinOnDrag = options.pinOnDrag ?? true;
1,222✔
33
        node.meshDragBehavior = new SixDofDragBehavior();
1,222✔
34
        node.mesh.addBehavior(node.meshDragBehavior);
1,222✔
35

36
        // drag started
37
        node.meshDragBehavior.onDragStartObservable.add(() => {
1,222✔
38
            // make sure the graph is running
UNCOV
39
            const context = this.getContext(node);
×
40
            context.setRunning(true);
×
41

42
            // don't let the graph engine update the node -- we are controlling it
UNCOV
43
            node.dragging = true;
×
44
        });
1,222✔
45

46
        // drag ended
47
        node.meshDragBehavior.onDragEndObservable.add(() => {
1,222✔
48
            // make sure the graph is running
49
            const context = this.getContext(node);
×
50
            context.setRunning(true);
×
51

52
            // pin after dragging if configured
UNCOV
53
            if (node.pinOnDrag) {
×
54
                node.pin();
×
UNCOV
55
            }
×
56

57
            // the graph engine can have control of the node again
UNCOV
58
            node.dragging = false;
×
59
        });
1,222✔
60

61
        // position changed
62
        node.meshDragBehavior.onPositionChangedObservable.add((event) => {
1,222✔
63
            // CRITICAL: DO NOT restart layout on position changes!
64
            // This was causing infinite loop - position changes from layout engine
65
            // would trigger this, which would restart layout, causing more position changes
66

67
            // Only update position in layout engine if user is actively dragging
UNCOV
68
            if (node.dragging) {
×
UNCOV
69
                const context = this.getContext(node);
×
70
                // update the node position
UNCOV
71
                context.getLayoutManager().layoutEngine?.setNodePosition(node, event.position);
×
UNCOV
72
            }
×
73
        });
1,222✔
74

75
        // TODO: this apparently updates dragging objects faster and more fluidly
76
        // https://playground.babylonjs.com/#YEZPVT%23840
77
        // https://forum.babylonjs.com/t/expandable-lines/24681/12
78
    }
1,222✔
79

80
    /**
81
     * Add click behavior for node expansion
82
     */
83
    private static addClickBehavior(node: GraphNode): void {
2✔
84
        // click behavior setup
85
        const context = this.getContext(node);
1,222✔
86
        const scene = context.getScene();
1,222✔
87
        node.mesh.actionManager = node.mesh.actionManager ?? new ActionManager(scene);
1,222✔
88

89
        // Available triggers:
90
        // ActionManager.OnDoublePickTrigger
91
        // ActionManager.OnRightPickTrigger
92
        // ActionManager.OnCenterPickTrigger
93
        // ActionManager.OnLongPressTrigger
94

95
        // Only Graph has fetchNodes/fetchEdges, not GraphContext
96
        // For now, check if parentGraph is the full Graph instance
97
        const graph = node.parentGraph as Graph & {fetchNodes?: unknown, fetchEdges?: unknown};
1,222✔
98
        if (graph.fetchNodes && graph.fetchEdges) {
1,222!
99
            const {fetchNodes, fetchEdges} = graph;
×
100

UNCOV
101
            node.mesh.actionManager.registerAction(
×
102
                new ExecuteCodeAction(
×
103
                    {
×
104
                        trigger: ActionManager.OnDoublePickTrigger,
×
105
                        // trigger: ActionManager.OnLongPressTrigger,
106
                    },
×
107
                    () => {
×
108
                        // make sure the graph is running
UNCOV
109
                        context.setRunning(true);
×
110

111
                        // fetch all edges for current node
UNCOV
112
                        const edgeSet = fetchEdges(node, graph as unknown as Graph);
×
113
                        const edges = Array.from(edgeSet);
×
114

115
                        // create set of unique node ids
UNCOV
116
                        const nodeIds = new Set<NodeIdType>();
×
117
                        edges.forEach((e) => {
×
118
                            nodeIds.add(e.src);
×
119
                            nodeIds.add(e.dst);
×
120
                        });
×
UNCOV
121
                        nodeIds.delete(node.id);
×
122

123
                        // fetch all nodes from associated edges
UNCOV
124
                        const nodes = fetchNodes(nodeIds, graph);
×
125

126
                        // add all the nodes and edges we collected
UNCOV
127
                        const dataManager = context.getDataManager();
×
UNCOV
128
                        dataManager.addNodes([... nodes]);
×
UNCOV
129
                        dataManager.addEdges([... edges]);
×
130

131
                        // TODO: fetch and add secondary edges
UNCOV
132
                    },
×
UNCOV
133
                ),
×
UNCOV
134
            );
×
UNCOV
135
        }
×
136
    }
1,222✔
137

138
    /**
139
     * Helper to get GraphContext from a Node
140
     */
141
    private static getContext(node: GraphNode): GraphContext {
2✔
142
        // Check if parentGraph has GraphContext methods
143
        if ("getStyles" in node.parentGraph) {
1,222✔
144
            return node.parentGraph;
1,222✔
145
        }
1,222!
146

147
        // Otherwise, it's a Graph instance which implements GraphContext
UNCOV
148
        return node.parentGraph;
×
149
    }
1,222✔
150
}
2✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc