Skip to content

Events ​

Guide to subscribing to graph events.

Overview ​

Graphty uses an event-driven architecture. Subscribe to events for user interactions, data changes, and state updates.

Available Events ​

EventTriggerEvent Data
graph-settledLayout finished{ settled: boolean }
data-loadedInitial data loaded{ nodeCount, edgeCount }
data-addedIncremental data added{ nodes, edges }
selection-changedNode selected/deselected{ node, previousNode }
camera-state-changedCamera moved{ state }
style-changedStyles updated{ layers }
node-clickUser clicked node{ node, data, event }
node-hoverMouse entered node{ node, data }
node-drag-startStarted dragging node{ node, position }
node-drag-endFinished dragging node{ node, position }
edge-clickUser clicked edge{ edge, data, event }
errorError occurred{ error, context }

JavaScript API ​

Subscribe using the on() method on the Graph instance:

typescript
const graph = element.graph;

// Layout complete
graph.on("graph-settled", () => {
    console.log("Layout complete!");
    graph.zoomToFit();
});

// Node clicked
graph.on("node-click", ({ node }) => {
    console.log("Clicked:", node.id);
    graph.selectNode(node.id);
});

// Node hover
graph.on("node-hover", ({ node }) => {
    console.log("Hovering:", node.id);
});

// Node drag events
graph.on("node-drag-start", ({ node, position }) => {
    console.log("Started dragging:", node.id, "at", position);
});

graph.on("node-drag-end", ({ node, position }) => {
    console.log("Finished dragging:", node.id, "at", position);
});

// Selection changed
graph.on("selection-changed", ({ node, previousNode }) => {
    if (node) {
        console.log("Selected:", node.id);
    } else {
        console.log("Deselected");
    }
});

// Data loaded
graph.on("data-loaded", ({ nodeCount, edgeCount }) => {
    console.log(`Loaded ${nodeCount} nodes, ${edgeCount} edges`);
});

// Error handling
graph.on("error", ({ error, context }) => {
    console.error("Graph error:", error, "in", context);
});

DOM Events ​

Listen via standard addEventListener on the Web Component:

javascript
const element = document.querySelector("graphty-element");

element.addEventListener("graph-settled", (e) => {
    console.log("Settled!", e.detail);
});

element.addEventListener("node-click", (e) => {
    console.log("Clicked node:", e.detail.node);
});

element.addEventListener("selection-changed", (e) => {
    const { node, previousNode } = e.detail;
    console.log("Selection:", node?.id, "Previous:", previousNode?.id);
});

Event Timing ​

Some events fire asynchronously. Understand the order:

typescript
// Data loading sequence
graph.on("data-loaded", () => console.log("1. Data loaded"));
graph.on("graph-settled", () => console.log("2. Layout settled"));

// When you add data
await graph.addNodes(nodes); // 'data-loaded' or 'data-added' fires
await graph.addEdges(edges);
await graph.waitForSettled(); // 'graph-settled' fires

Removing Listeners ​

Clean up event listeners when done:

typescript
// JavaScript API
const handler = () => console.log("Settled");
graph.on("graph-settled", handler);

// Later, remove it
graph.off("graph-settled", handler);
javascript
// DOM API
const handler = (e) => console.log("Settled", e.detail);
element.addEventListener("graph-settled", handler);

// Later, remove it
element.removeEventListener("graph-settled", handler);

Common Patterns ​

Zoom After Layout ​

typescript
graph.on("graph-settled", () => {
    graph.zoomToFit();
});

Show Node Details ​

typescript
graph.on("node-click", ({ node }) => {
    showDetailsPanel(node);
    graph.selectNode(node.id);
});

graph.on("selection-changed", ({ node }) => {
    if (!node) {
        hideDetailsPanel();
    }
});

Track Node Dragging ​

typescript
graph.on("node-drag-start", ({ node, position }) => {
    console.log(`Started dragging ${node.id} at`, position);
});

graph.on("node-drag-end", ({ node, position }) => {
    console.log(`Dropped ${node.id} at`, position);
    // Save new position or trigger layout update
});

Loading Indicator ​

typescript
let isLoading = false;

graph.on("data-added", () => {
    isLoading = true;
    showLoadingSpinner();
});

graph.on("graph-settled", () => {
    if (isLoading) {
        isLoading = false;
        hideLoadingSpinner();
    }
});

Error Handling ​

typescript
graph.on("error", ({ error, context }) => {
    if (context === "data-loading") {
        showError("Failed to load data");
    } else if (context === "algorithm") {
        showError("Algorithm failed");
    } else {
        showError("An error occurred");
    }
    console.error(error);
});

Camera Tracking ​

typescript
graph.on("camera-state-changed", ({ state }) => {
    // Save camera state for later restoration
    localStorage.setItem("camera-state", JSON.stringify(state));
});

// Restore on load
const savedState = localStorage.getItem("camera-state");
if (savedState) {
    graph.setCameraState(JSON.parse(savedState));
}

React Integration ​

tsx
import { useEffect, useRef } from "react";
import "@graphty/graphty-element";

function GraphComponent({ onNodeClick }) {
    const graphRef = useRef(null);

    useEffect(() => {
        const element = graphRef.current;
        if (!element) return;

        const graph = element.graph;

        const handleClick = ({ node }) => {
            onNodeClick(node);
        };

        graph.on("node-click", handleClick);

        return () => {
            graph.off("node-click", handleClick);
        };
    }, [onNodeClick]);

    return <graphty-element ref={graphRef} />;
}

Vue Integration ​

vue
<script setup>
import "@graphty/graphty-element";
import { ref, onMounted, onUnmounted } from "vue";

const graphRef = ref(null);
const emit = defineEmits(["nodeClick"]);

let cleanup = null;

onMounted(() => {
    const graph = graphRef.value.graph;

    const handler = ({ node }) => emit("nodeClick", node);
    graph.on("node-click", handler);

    cleanup = () => graph.off("node-click", handler);
});

onUnmounted(() => {
    if (cleanup) cleanup();
});
</script>

<template>
    <graphty-element ref="graphRef" />
</template>

Interactive Examples ​