@yworks/react-yfiles-company-ownership
Version:
yFiles React Company Ownership Component - A powerful and versatile React component based on the yFiles library, allows you to seamlessly incorporate dynamic and interactive company ownership diagrams into your applications.
1,584 lines (1,564 loc) • 68.3 kB
JavaScript
import {
componentBackgroundColor,
createLayout,
defaultExportMargins,
defaultGraphFitInsets,
defaultLayoutOptions,
defaultOwnershipEdgeStyle,
defaultPortAdjustmentPolicy,
defaultRelationEdgeStyle,
hoverHighlightColor,
initializeWebWorker,
maximumZoom,
minimumZoom,
neighborhoodHighlightColor,
selectionHighlightColor
} from "./chunk-7BEEWV7H.js";
// src/CompanyOwnershipProvider.tsx
import { createContext, useContext, useMemo } from "react";
import { useGraphComponent, withGraphComponentProvider } from "@yworks/react-yfiles-core";
import { Graph as Graph2, ViewportLimitingPolicy } from "@yfiles/yfiles";
// src/CompanyOwnershipModel.ts
function getNode(item, graph) {
return item ? getNodeFromId(item.id, graph) : null;
}
function getNodeFromId(id, graph) {
return graph.nodes.find((node) => node.tag.id === id);
}
function getFilteredGraphWrapper(graphComponent) {
return graphComponent.graph;
}
// src/CompanyOwnershipModelImpl.ts
import { Command, Insets, MutableRectangle } from "@yfiles/yfiles";
import {
exportImageAndSave,
exportSvgAndSave,
printDiagram
} from "@yworks/react-yfiles-core";
// src/core/NeighborhoodIndicatorManager.ts
import {
AdjacencyTypes,
Arrow,
EdgeStyleIndicatorRenderer,
GraphComponent,
HighlightIndicatorManager,
IEdge,
IEdgeStyle,
ILookupDecorator,
INode,
INodeStyle,
LookupDecorator,
Neighborhood,
NodeStyleIndicatorRenderer,
PolylineEdgeStyle,
ShapeNodeStyle,
TraversalDirection
} from "@yfiles/yfiles";
var NeighborhoodIndicatorManager = class extends HighlightIndicatorManager {
// remember the neighborhood's master items to restore the highlighting after collapse/expand
neighborhoodCollection = /* @__PURE__ */ new Set();
nodeStyle = INodeStyle.VOID_NODE_STYLE;
edgeStyle = IEdgeStyle.VOID_EDGE_STYLE;
constructor(graphComponent, highlightOptions) {
super();
this.canvasComponent = graphComponent;
this.update(highlightOptions);
}
update(highlightOptions) {
const neighborhoodColor = highlightOptions?.neighborhoodHighlightColor ?? neighborhoodHighlightColor;
const neighborhoodClass = highlightOptions?.neighborhoodHighlightCssClass ?? "";
this.nodeStyle = new ShapeNodeStyle({
shape: "round-rectangle",
stroke: `3px ${neighborhoodColor}`,
fill: "none",
cssClass: neighborhoodClass
});
this.edgeStyle = new PolylineEdgeStyle({
targetArrow: new Arrow({
type: "triangle",
stroke: `2px ${neighborhoodColor}`,
fill: neighborhoodColor
}),
stroke: `3px ${neighborhoodColor}`,
cssClass: neighborhoodClass
});
}
getRenderer(item) {
return item instanceof INode ? new NodeStyleIndicatorRenderer({
nodeStyle: this.nodeStyle,
// the padding from the actual node to its highlight visualization
margins: 2
}) : item instanceof IEdge ? new EdgeStyleIndicatorRenderer({
edgeStyle: this.edgeStyle,
zoomPolicy: "world-coordinates"
}) : super.getRenderer(item);
}
highlightNeighborhood(viewStartNode) {
super.items?.clear();
const graph = this.canvasComponent.graph;
this.addHighlight(viewStartNode);
const neighborhood = new Neighborhood({
startNodes: [viewStartNode],
traversalDirection: TraversalDirection.BOTH
});
const result = neighborhood.run(graph);
for (const node of result.neighbors) {
const highlightItem2 = node;
if (highlightItem2) {
this.addHighlight(highlightItem2);
}
for (const adjacentOutEdge of graph.edgesAt(node, AdjacencyTypes.OUTGOING)) {
if (result.neighbors.includes(adjacentOutEdge.targetNode) || adjacentOutEdge.targetNode === viewStartNode) {
const viewItem = adjacentOutEdge;
if (viewItem) {
this.addHighlight(viewItem);
}
}
}
for (const adjacentInEdge of graph.edgesAt(node, AdjacencyTypes.INCOMING)) {
if (result.neighbors.includes(adjacentInEdge.sourceNode) || adjacentInEdge.sourceNode === viewStartNode) {
const viewItem = adjacentInEdge;
if (viewItem) {
this.addHighlight(viewItem);
}
}
}
}
}
addHighlight(viewItem) {
super.items?.add(viewItem);
this.neighborhoodCollection.add(viewItem);
}
clearHighlights() {
this.neighborhoodCollection.clear();
super.items?.clear();
}
deactivateHighlights() {
super.items?.clear();
}
activateHighlights() {
let visibleItem = null;
for (const item of this.neighborhoodCollection) {
if (item instanceof INode) {
visibleItem = item;
} else if (item instanceof IEdge) {
visibleItem = item;
}
if (visibleItem) {
super.items?.add(visibleItem);
}
}
}
};
function registerNeighborHoodIndicatorManager(graphComponent, highlightOptions) {
const neighborhoodIndicatorManager = new NeighborhoodIndicatorManager(
graphComponent,
highlightOptions
);
const lookupDecorator = graphComponent.lookup(ILookupDecorator);
new LookupDecorator(GraphComponent, NeighborhoodIndicatorManager, lookupDecorator).addConstant(
neighborhoodIndicatorManager
);
}
function getNeighborhoodIndicatorManager(graphComponent) {
const manager = graphComponent.lookup(NeighborhoodIndicatorManager);
if (!manager) {
throw new Error("No NeighborhoodIndicatorManager registered on the GraphComponent");
}
return manager;
}
// src/CompanyOwnershipModelImpl.ts
function createCompanyOwnershipModel(graphComponent, collapsibleTree, layoutSupport) {
let onRenderedCallback = null;
const setRenderedCallback = (cb) => {
onRenderedCallback = cb;
};
const onRendered = () => {
onRenderedCallback?.();
onRenderedCallback = null;
};
function showGenealogy(item) {
const node = getNode(item, graphComponent.graph);
if (node) {
clearConnectedItemsHighlight();
collapsibleTree.executeShowGenealogy(node);
}
}
function isConnection(item) {
return "sourceId" in item || "connections" in item;
}
function applyLayout(incremental, incrementalItems, fixedItem = null, fitViewport = false) {
if (!layoutSupport) {
return Promise.resolve();
}
const incrementalNodes = [];
incrementalItems?.forEach((item) => {
const graph = graphComponent.graph;
const node = getNode(item, graph);
if (node) {
incrementalNodes.push(node);
}
});
const fixedNode = getNode(fixedItem, graphComponent.graph);
return layoutSupport.runLayout(incremental ?? false, incrementalNodes, fixedNode, fitViewport);
}
function highlightConnectedItems(item) {
const startNode = getNode(item, graphComponent.graph);
if (!startNode) {
return;
}
const neighborhoodIndicatorManager = getNeighborhoodIndicatorManager(graphComponent);
neighborhoodIndicatorManager.highlightNeighborhood(startNode);
}
function canClearConnectedItemsHighlight() {
const neighborhoodIndicatorManager = getNeighborhoodIndicatorManager(graphComponent);
return neighborhoodIndicatorManager.items ? neighborhoodIndicatorManager.items.size > 0 : false;
}
function clearConnectedItemsHighlight() {
const neighborhoodIndicatorManager = getNeighborhoodIndicatorManager(graphComponent);
neighborhoodIndicatorManager.clearHighlights();
}
async function showAll() {
const neighborhoodHighlightManager = getNeighborhoodIndicatorManager(graphComponent);
neighborhoodHighlightManager.clearHighlights();
await collapsibleTree.executeShowAll();
}
function canShowAll() {
return collapsibleTree.canExecuteShowAll();
}
function zoomIn() {
graphComponent.executeCommand(Command.INCREASE_ZOOM, null);
}
function zoomOut() {
graphComponent.executeCommand(Command.DECREASE_ZOOM, null);
}
function fitContent(insets = 0) {
graphComponent.fitGraphBounds(new Insets(insets), true);
}
function zoomToOriginal() {
graphComponent.executeCommand(Command.ZOOM, 1);
}
async function exportToSvg(exportSettings) {
const settings = Object.assign(
{
zoom: graphComponent.zoom,
scale: graphComponent.zoom,
margins: defaultExportMargins,
inlineImages: true,
background: componentBackgroundColor
},
exportSettings
);
const callback = _hasReactItems ? setRenderedCallback : (resolve) => {
resolve();
};
await exportSvgAndSave(settings, graphComponent, callback);
}
async function exportToPng(exportSettings) {
const settings = Object.assign(
{
zoom: graphComponent.zoom,
scale: graphComponent.zoom,
margins: defaultExportMargins,
inlineImages: true,
background: componentBackgroundColor
},
exportSettings
);
const callback = _hasReactItems ? setRenderedCallback : (resolve) => {
resolve();
};
await exportImageAndSave(settings, graphComponent, callback);
}
async function print(printSettings) {
const settings = Object.assign(
{
zoom: graphComponent.zoom,
scale: 1,
margins: defaultExportMargins
},
printSettings
);
await printDiagram(settings, graphComponent);
}
function refresh() {
graphComponent.invalidate();
}
function zoomTo(items) {
if (items.length === 0) {
return;
}
const graph = graphComponent.graph;
const targetBounds = new MutableRectangle();
items.forEach((item) => {
if (isConnection(item)) {
const source = getNodeFromId(item.sourceId, graph);
const target = getNodeFromId(item.targetId, graph);
targetBounds.add(source.layout);
targetBounds.add(target.layout);
} else {
const node = getNode(item, graph);
targetBounds.add(node.layout);
}
});
const enlargedTargetBounds = targetBounds.toRect().getEnlarged(200);
const gcSize = graphComponent.size;
const newZoom = Math.min(
gcSize.width / enlargedTargetBounds.width,
gcSize.height / enlargedTargetBounds.height
);
graphComponent.zoomToAnimated(
Math.max(newZoom, graphComponent.zoom),
enlargedTargetBounds.center
);
}
function addGraphUpdatedListener(listener) {
collapsibleTree.addGraphUpdatedListener(listener);
}
function removeGraphUpdatedListener(listener) {
collapsibleTree.removeGraphUpdatedListener(listener);
}
function getVisibleItems() {
return collapsibleTree.graphComponent.graph.nodes.map((item) => item.tag).toArray();
}
async function showPredecessors(item) {
const node = getNode(item, graphComponent.graph);
if (node) {
const neighborhoodHighlightManager = getNeighborhoodIndicatorManager(graphComponent);
neighborhoodHighlightManager.clearHighlights();
await collapsibleTree.executeShowParent(node);
}
}
function canShowPredecessors(item) {
const node = getNode(item, graphComponent.graph);
if (node) {
return collapsibleTree.canExecuteShowParent(node);
}
return false;
}
async function hidePredecessors(item) {
const node = getNode(item, graphComponent.graph);
if (node) {
const neighborhoodHighlightManager = getNeighborhoodIndicatorManager(graphComponent);
neighborhoodHighlightManager.clearHighlights();
await collapsibleTree.executeHideParent(node);
}
}
function canHidePredecessors(item) {
const node = getNode(item, graphComponent.graph);
if (node) {
return collapsibleTree.canExecuteHideParent(node);
}
return false;
}
async function showSuccessors(item) {
const node = getNode(item, graphComponent.graph);
if (node) {
const neighborhoodHighlightManager = getNeighborhoodIndicatorManager(graphComponent);
neighborhoodHighlightManager.clearHighlights();
await collapsibleTree.executeShowChildren(node);
}
}
function canShowSuccessors(item) {
const node = getNode(item, graphComponent.graph);
if (node) {
return collapsibleTree.canExecuteShowChildren(node);
}
return false;
}
async function hideSuccessors(item) {
const node = getNode(item, graphComponent.graph);
if (node) {
const neighborhoodHighlightManager = getNeighborhoodIndicatorManager(graphComponent);
neighborhoodHighlightManager.clearHighlights();
await collapsibleTree.executeHideChildren(node);
}
}
function canHideSuccessors(item) {
const node = getNode(item, graphComponent.graph);
if (node) {
return collapsibleTree.canExecuteHideChildren(node);
}
return false;
}
let _hasReactItems = false;
return {
graphComponent,
layoutSupport,
applyLayout,
canClearConnectedItemsHighlight,
clearConnectedItemsHighlight,
highlightConnectedItems,
showGenealogy,
canShowAll,
showAll,
exportToPng,
exportToSvg,
print,
isConnection,
fitContent,
zoomIn,
zoomOut,
zoomTo,
zoomToOriginal,
getSearchHits: () => [],
// will be replaced during initialization
onRendered,
refresh,
addGraphUpdatedListener,
removeGraphUpdatedListener,
getVisibleItems,
showPredecessors,
canShowPredecessors,
hidePredecessors,
canHidePredecessors,
showSuccessors,
canShowSuccessors,
hideSuccessors,
canHideSuccessors,
get hasReactItems() {
return _hasReactItems;
},
set hasReactItems(value) {
_hasReactItems = value;
}
};
}
// src/core/LayoutSupport.ts
import {
HierarchicalLayoutData,
INode as INode3,
LayoutAnchoringPolicy,
LayoutAnchoringStageData,
LayoutExecutor,
LayoutExecutorAsync
} from "@yfiles/yfiles";
import { registerWebWorker } from "@yworks/react-yfiles-core";
var LayoutSupport = class {
constructor(graphComponent) {
this.graphComponent = graphComponent;
}
workerPromise = null;
set layoutWorker(worker) {
if (worker) {
this.workerPromise = registerWebWorker(worker);
} else {
this.workerPromise = null;
}
}
executorAsync = null;
executor = null;
layoutOptions = defaultLayoutOptions;
setLayoutRunning;
createLayoutData(incremental, incrementalNodes, fixedNode = null) {
let layoutData = null;
if (incremental) {
layoutData = new HierarchicalLayoutData({
incrementalNodes: (node) => node instanceof INode3 && incrementalNodes.includes(node)
});
}
if (fixedNode) {
const fixNodeLayoutData = new LayoutAnchoringStageData();
fixNodeLayoutData.nodeAnchoringPolicies.mapperFunction = (key) => key === fixedNode ? LayoutAnchoringPolicy.CENTER : LayoutAnchoringPolicy.NONE;
if (!layoutData) {
layoutData = new HierarchicalLayoutData();
}
layoutData = layoutData.combineWith(fixNodeLayoutData);
}
return layoutData;
}
async runLayout(incremental = false, incrementalNodes = [], fixedNode = null, fitViewport = false) {
this.setLayoutRunning?.(true);
const graph = this.graphComponent.graph;
if (incrementalNodes.length > 0) {
incrementalNodes.map((node) => graph.edgesAt(node).toArray()).flat().forEach((edge) => {
edge.bends.toArray().forEach((bend) => graph.remove(bend));
});
}
const executor = this.workerPromise !== null ? await this.createLayoutExecutorAsync(
incremental,
incrementalNodes,
fixedNode,
fitViewport
) : await this.createLayoutExecutor(incremental, incrementalNodes, fixedNode, fitViewport);
try {
await executor.start();
} catch (e) {
if (e.name === "AlgorithmAbortedError") {
console.error("Layout calculation was aborted because maximum duration time was exceeded.");
} else {
console.error("Something went wrong during the layout calculation");
console.error(e);
}
} finally {
this.setLayoutRunning?.(false);
}
}
/**
* When a layout animation is already running, it might have started
* with now obsolete node sizes - stop the running animation and restore
* the latest measured node sizes.
*/
async maybeCancel() {
const syncRunning = this.executor && this.executor.running;
const asyncRunning = this.executorAsync && this.executorAsync.running;
if (syncRunning || asyncRunning) {
const layouts = /* @__PURE__ */ new Map();
for (const node of this.graphComponent.graph.nodes) {
layouts.set(node, node.layout.toRect());
}
await this.executor?.stop();
await this.executorAsync?.cancel();
for (const node of this.graphComponent.graph.nodes) {
if (layouts.has(node)) {
this.graphComponent.graph.setNodeLayout(node, layouts.get(node));
}
}
for (const edge of this.graphComponent.graph.edges) {
this.graphComponent.graph.clearBends(edge);
}
}
}
async createLayoutExecutor(incremental, incrementalNodes, fixedNode = null, fitViewport) {
await this.maybeCancel();
this.executor = new LayoutExecutor({
graphComponent: this.graphComponent,
layout: createLayout(incremental, this.layoutOptions),
layoutData: this.createLayoutData(incremental, incrementalNodes, fixedNode),
animationDuration: "300ms",
animateViewport: fitViewport,
updateContentBounds: true,
targetBoundsPadding: defaultGraphFitInsets,
portAdjustmentPolicies: defaultPortAdjustmentPolicy
});
return Promise.resolve(this.executor);
}
async createLayoutExecutorAsync(incremental, incrementalNodes, fixedNode = null, fitViewport) {
await this.maybeCancel();
const worker = await this.workerPromise;
const webWorkerMessageHandler = (data) => {
const thisRequest = data.token;
data.incremental = incremental;
data.layoutOptions = this.layoutOptions;
return new Promise((resolve, reject) => {
worker.onmessage = (e) => {
if (e.data && thisRequest === e.data.token) {
if (e.data.name === "AlgorithmAbortedError") {
reject(e.data);
} else {
resolve(e.data);
}
}
};
worker.postMessage(data);
});
};
this.executorAsync = new LayoutExecutorAsync({
messageHandler: webWorkerMessageHandler,
graphComponent: this.graphComponent,
layoutData: this.createLayoutData(incremental, incrementalNodes, fixedNode),
animationDuration: "300ms",
animateViewport: fitViewport,
updateContentBounds: true,
targetBoundsPadding: defaultGraphFitInsets,
portAdjustmentPolicies: defaultPortAdjustmentPolicy
});
return this.executorAsync;
}
};
// src/core/CollapsibleTree.ts
import {
Graph,
delegate,
FilteredGraphWrapper,
Neighborhood as Neighborhood2,
PlaceNodesAtBarycenterStage,
PlaceNodesAtBarycenterStageData,
TraversalDirection as TraversalDirection2
} from "@yfiles/yfiles";
var CollapsibleTree = class _CollapsibleTree {
constructor(_graphComponent, completeGraph = new Graph(), layoutSupport) {
this._graphComponent = _graphComponent;
this.completeGraph = completeGraph;
this.layoutSupport = layoutSupport;
const nodeFilter = (node) => !this.itemsHiddenByCollapse.has(node) && !this.itemsHiddenByGenealogy.has(node);
this.filteredGraph = new FilteredGraphWrapper(completeGraph, nodeFilter);
}
itemsHiddenByCollapse = /* @__PURE__ */ new Set();
itemsHiddenByGenealogy = /* @__PURE__ */ new Set();
filteredGraph;
doingLayout = false;
graphUpdatedListener = null;
collapsedStateUpdatedListener = null;
get graphComponent() {
return this._graphComponent;
}
/**
* Adds an event listener to the graphUpdated event that is fired after the filtered graph
* has changed and the layout was updated.
*/
addGraphUpdatedListener(listener) {
this.graphUpdatedListener = delegate.combine(this.graphUpdatedListener, listener);
}
removeGraphUpdatedListener(listener) {
this.graphUpdatedListener = delegate.remove(this.graphUpdatedListener, listener);
}
/**
* Adds an event listener to the collapsedStateUpdated event that is fired when the collapsed
* state of a port has changed.
*/
addCollapsedStateUpdatedListener(listener) {
this.collapsedStateUpdatedListener = delegate.combine(
this.collapsedStateUpdatedListener,
listener
);
}
removeCollapsedStateUpdatedListener(listener) {
this.collapsedStateUpdatedListener = delegate.remove(
this.collapsedStateUpdatedListener,
listener
);
}
/**
* Hides the children of the given node and updates the layout.
*/
async executeHideChildren(item) {
if (!this.canExecuteHideChildren(item)) {
return Promise.resolve();
}
const descendants = _CollapsibleTree.collectDescendants(this.completeGraph, item);
for (const node of descendants) {
this.updateCollapsedState(node, true);
}
this.updateCollapsedState(item, true);
const allSiblingNodes = this.completeGraph.outEdgesAt(item).map((outEdge) => outEdge.targetNode).flatMap((targetNode) => this.completeGraph.inEdgesAt(targetNode)).map((edge) => edge.sourceNode).filter((sourceNode) => sourceNode !== item).distinct();
allSiblingNodes.forEach((node) => {
if (this.filteredGraph.outEdgesAt(node).some((edge) => descendants.has(edge.targetNode))) {
this.updateCollapsedState(node, true);
}
});
this.removeEmptyGroups(descendants);
this.filteredGraph.nodePredicateChanged();
await this.refreshLayout(item, descendants, true);
this.addToHiddenNodes(descendants);
this.filteredGraph.nodePredicateChanged();
this.onGraphUpdated();
}
/**
* @returns Whether the children of the given node can be hidden.
*/
canExecuteHideChildren(item) {
return !this.doingLayout && this.filteredGraph.outDegree(item) > 0;
}
/**
* Shows the children of the given node and updates the layout.
*/
async executeShowChildren(item) {
if (!this.canExecuteShowChildren(item)) {
return Promise.resolve();
}
const descendants = _CollapsibleTree.collectDescendants(this.completeGraph, item);
const incrementalNodes = new Set(
Array.from(descendants).filter((node) => !this.filteredGraph.contains(node))
);
this.showChildren(item);
this.filteredGraph.nodePredicateChanged();
await this.refreshLayout(item, incrementalNodes, false);
this.updateCollapsedState(item, false);
const allSiblingNodes = this.completeGraph.outEdgesAt(item).map((outEdge) => outEdge.targetNode).flatMap((targetNode) => this.completeGraph.inEdgesAt(targetNode)).filter((edge) => edge.sourceNode !== item).map((edge) => edge.sourceNode).distinct();
allSiblingNodes.forEach((node) => {
if (this.completeGraph.outEdgesAt(node).every((edge) => descendants.has(edge.targetNode))) {
this.updateCollapsedState(node, false);
}
});
this.onGraphUpdated();
}
showChildren(node) {
for (const childEdge of this.completeGraph.outEdgesAt(node)) {
const child = childEdge.targetNode;
this.itemsHiddenByCollapse.delete(child);
_CollapsibleTree.restoreGroup(this.completeGraph, this.itemsHiddenByCollapse, child);
this.onCollapsedStateUpdated(childEdge.sourcePort, false);
}
}
/**
* @returns Whether the children of the given node can be shown.
*/
canExecuteShowChildren(item) {
return !this.doingLayout && this.filteredGraph.outDegree(item) !== this.completeGraph.outDegree(item);
}
/**
* Shows the parent of the given node and updates the layout.
*
* In contrast to {@link executeHideParent}, it only shows the
* direct parent and not any of its children.
*/
async executeShowParent(node) {
if (this.doingLayout) {
return Promise.resolve();
}
const incrementalNodes = /* @__PURE__ */ new Set();
this.showParents(node, incrementalNodes);
this.filteredGraph.nodePredicateChanged();
await this.refreshLayout(node, incrementalNodes, false);
this.onGraphUpdated();
}
showParents(node, incrementalNodes) {
for (const parentEdge of this.completeGraph.inEdgesAt(node)) {
const parent = parentEdge.sourceNode;
this.itemsHiddenByCollapse.delete(parent);
_CollapsibleTree.restoreGroup(this.completeGraph, this.itemsHiddenByCollapse, parent);
incrementalNodes.add(parent);
}
}
/**
* @returns Whether the parent of the given node can be shown.
*/
canExecuteShowParent(node) {
return !this.doingLayout && this.filteredGraph.inDegree(node) === 0 && this.completeGraph.inDegree(node) > 0;
}
/**
* Hides the parent of the given node and updates the layout.
*
* In contrast to {@link executeShowParent}, this method also hides all ancestors
* and their descendants and other isolated trees leaving only the node and its descendants
* in the graph.
*/
async executeHideParent(node) {
if (this.doingLayout) {
return Promise.resolve();
}
const nodes = _CollapsibleTree.collectAllNodesExceptSubtree(this.completeGraph, node);
this.removeEmptyGroups(nodes);
this.filteredGraph.nodePredicateChanged();
await this.refreshLayout(node, nodes, true);
this.addToHiddenNodes(nodes);
this.filteredGraph.nodePredicateChanged();
this.onGraphUpdated();
}
/**
* @returns Whether the parent of the given node can be hidden.
*/
canExecuteHideParent(node) {
return !this.doingLayout && this.filteredGraph.inDegree(node) > 0;
}
/**
* Shows all nodes and updates the layout.
*/
async executeShowAll() {
if (this.doingLayout) {
return Promise.resolve();
}
const incrementalNodes = /* @__PURE__ */ new Set([
...this.itemsHiddenByCollapse,
...this.itemsHiddenByGenealogy
]);
this.itemsHiddenByCollapse.clear();
this.itemsHiddenByGenealogy.clear();
for (const edge of this.completeGraph.edges) {
this.onCollapsedStateUpdated(edge.sourcePort, false);
}
this.filteredGraph.nodePredicateChanged();
await this.refreshLayout(
this._graphComponent.currentItem,
incrementalNodes,
false
);
this.onGraphUpdated();
}
/**
* @returns Whether {@link executeShowAll} can be executed.
*/
canExecuteShowAll() {
return (this.itemsHiddenByCollapse.size !== 0 || this.itemsHiddenByGenealogy.size !== 0) && !this.doingLayout;
}
/**
* Refreshes the node after modifications on the tree.
* @returns a promise which is resolved when the layout has been executed.
*/
async refreshLayout(centerNode, incrementalNodes, collapse) {
if (this.doingLayout) {
return Promise.resolve();
}
this.doingLayout = true;
if (!collapse) {
this.prepareSmoothExpandLayoutAnimation(incrementalNodes);
}
this.layoutSupport.runLayout(
incrementalNodes.size > 0,
[...incrementalNodes],
centerNode,
true
).then(() => {
this.doingLayout = false;
});
}
/**
* Moves incremental nodes to a location between their neighbors before expanding for a smooth animation.
*/
prepareSmoothExpandLayoutAnimation(incrementalNodes) {
const graph = this._graphComponent.graph;
const layoutData = new PlaceNodesAtBarycenterStageData({
affectedNodes: incrementalNodes
});
const layout = new PlaceNodesAtBarycenterStage();
graph.applyLayout(layout, layoutData);
}
addToHiddenNodes(nodes) {
for (const node of nodes) {
this.itemsHiddenByCollapse.add(node);
}
}
/**
* Set the collapsed state to all the node's ports.
*/
updateCollapsedState(node, collapsed) {
for (const outEdge of this.completeGraph.outEdgesAt(node)) {
this.onCollapsedStateUpdated(outEdge.sourcePort, collapsed);
}
}
/**
* Restores the group containing the given node if needed.
*/
static restoreGroup(graph, hiddenNodesSet, node) {
const parent = graph.getParent(node);
if (parent && hiddenNodesSet.has(parent)) {
hiddenNodesSet.delete(parent);
}
}
/**
* Removes all groups in the given graph that will be empty after removing the given nodes.
*/
removeEmptyGroups(nodesToHide) {
const emptyGroups = _CollapsibleTree.findEmptyGroups(this.filteredGraph, nodesToHide).toArray();
for (const group of emptyGroups) {
this.itemsHiddenByCollapse.add(group);
}
}
static findEmptyGroups(graph, nodesToHide) {
return graph.nodes.filter(
(node) => graph.isGroupNode(node) && graph.degree(node) === 0 && graph.getChildren(node).every((child) => nodesToHide.has(child))
);
}
/**
* @returns all descendants of the passed node excluding the node itself.
*/
static collectDescendants(graph, root) {
const nodes = /* @__PURE__ */ new Set();
const queue = [root];
while (queue.length > 0) {
const node = queue.pop();
for (const outEdge of graph.outEdgesAt(node)) {
queue.unshift(outEdge.targetNode);
nodes.add(outEdge.targetNode);
}
}
return nodes;
}
/**
* Creates an array of all nodes excluding the nodes in the subtree rooted in the excluded sub-root.
*/
static collectAllNodesExceptSubtree(graph, excludedRoot) {
const subtree = this.collectDescendants(graph, excludedRoot);
subtree.add(excludedRoot);
return new Set(graph.nodes.filter((node) => !subtree.has(node)));
}
/**
* Informs the listener that the graph was updated.
*/
onGraphUpdated() {
this.graphUpdatedListener?.();
}
/**
* Informs the listener that the collapsed state was updated.
*/
onCollapsedStateUpdated(port, collapsed) {
this.collapsedStateUpdatedListener?.(port, collapsed);
}
executeShowGenealogy(node) {
const graph = this.graphComponent.graph;
const filteredGraphWrapper = getFilteredGraphWrapper(this.graphComponent);
this.itemsHiddenByCollapse.clear();
this.itemsHiddenByGenealogy.clear();
filteredGraphWrapper.nodePredicateChanged();
filteredGraphWrapper.edgePredicateChanged();
const neighborhood = new Neighborhood2({
startNodes: [node],
traversalDirection: TraversalDirection2.BOTH
});
const result = neighborhood.run(graph);
const resultNodes = new Set(result.neighbors.toArray());
resultNodes.add(node);
const groupingSupport = graph.groupingSupport;
const genealogy = /* @__PURE__ */ new Set();
resultNodes.forEach((node2) => {
genealogy.add(node2);
groupingSupport.getAncestors(node2).forEach((parent) => genealogy.add(parent));
});
graph.nodes.forEach((node2) => {
if (!genealogy.has(node2)) {
this.itemsHiddenByGenealogy.add(node2);
if (graph.isGroupNode(node2)) {
groupingSupport.getDescendants(node2).forEach((child) => {
this.itemsHiddenByGenealogy.add(child);
});
}
}
});
const neighborhoodHighlightManager = getNeighborhoodIndicatorManager(this.graphComponent);
neighborhoodHighlightManager.deactivateHighlights();
filteredGraphWrapper.nodePredicateChanged();
filteredGraphWrapper.edgePredicateChanged();
this.onGraphUpdated();
this.layoutSupport.runLayout(false, [], null, true).then(() => {
neighborhoodHighlightManager.activateHighlights();
});
}
};
// src/CompanyOwnershipProvider.tsx
import { jsx } from "react/jsx-runtime";
var CompanyOwnershipContext = createContext(null);
function useCompanyOwnershipContextInternal() {
return useContext(CompanyOwnershipContext);
}
function useCompanyOwnershipContext() {
const context = useContext(CompanyOwnershipContext);
if (context === null) {
throw new Error(
"This method can only be used inside a CompanyOwnership component or CompanyOwnershipProvider."
);
}
return context;
}
var CompanyOwnershipProvider = withGraphComponentProvider(
({ children }) => {
const graphComponent = useGraphComponent();
if (!graphComponent) {
return children;
}
const CompanyOwnership2 = useMemo(() => {
const fullGraph = new Graph2();
graphComponent.htmlElement.style.backgroundColor = componentBackgroundColor;
graphComponent.viewportLimiter.policy = ViewportLimitingPolicy.WITHIN_MARGINS;
graphComponent.viewportLimiter.viewportContentMargins = defaultGraphFitInsets.getEnlarged(20);
graphComponent.maximumZoom = maximumZoom;
graphComponent.minimumZoom = minimumZoom;
const layoutSupport = new LayoutSupport(graphComponent);
const collapsibleTree = new CollapsibleTree(graphComponent, fullGraph, layoutSupport);
graphComponent.graph = collapsibleTree.filteredGraph;
return createCompanyOwnershipModel(graphComponent, collapsibleTree, layoutSupport);
}, []);
return /* @__PURE__ */ jsx(CompanyOwnershipContext.Provider, { value: CompanyOwnership2, children });
}
);
// src/components/template-utils.ts
function stringifyData(data) {
return typeof data === "object" ? JSON.stringify(data) : String(data);
}
function getNodeClass(level) {
return level === "high" ? "yfiles-react-detail-node" : "yfiles-react-overview-node";
}
function getHighlightClasses(selected, hovered, focused) {
const highlights = ["yfiles-react-node-highlight"];
if (focused) {
highlights.push("yfiles-react-node-highlight--focused");
}
if (hovered) {
highlights.push("yfiles-react-node-highlight--hovered");
}
if (selected) {
highlights.push("yfiles-react-node-highlight--selected");
}
return highlights.join(" ");
}
function getUserProperties(data) {
const internalProperties = [
"id",
"className",
"style",
"width",
"height",
"parentId",
"connections"
];
return Object.fromEntries(
Object.entries(data).filter(
([property, _value]) => !property.startsWith("__") && internalProperties.every((key) => property !== key)
).slice(0, 10)
);
}
// src/components/RenderEntity.tsx
import { Fragment } from "react";
import { Fragment as Fragment2, jsx as jsx2, jsxs } from "react/jsx-runtime";
function RenderEntity({
dataItem,
detail,
hovered,
focused,
selected
}) {
const customCompanyOwnershipItem = dataItem;
const properties = getUserProperties(customCompanyOwnershipItem);
const companyType = customCompanyOwnershipItem.type ?? "Unknown";
const style = {
...customCompanyOwnershipItem.style,
...{
width: "100%",
height: "100%",
overflow: "hidden"
}
};
return /* @__PURE__ */ jsx2(Fragment2, { children: /* @__PURE__ */ jsx2(
"div",
{
style,
className: `yfiles-react-node ${getNodeClass(detail)} ${getHighlightClasses(
selected,
hovered,
focused
)} company-node-${companyType.toLowerCase()}
${customCompanyOwnershipItem.className ?? ""}`.trim(),
children: detail === "high" ? /* @__PURE__ */ jsxs("div", { className: "yfiles-react-detail-node__content", children: [
/* @__PURE__ */ jsx2("div", { className: "yfiles-react-detail-node__name", children: customCompanyOwnershipItem.name ?? `ID: ${customCompanyOwnershipItem.id}` }),
/* @__PURE__ */ jsx2("div", { className: "yfiles-react-detail-node__data-grid", children: Object.entries(properties).map(([property, value], i) => /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx2("div", { className: "yfiles-react-detail-node__data-grid--key", children: property }),
/* @__PURE__ */ jsx2("div", { className: "yfiles-react-detail-node__data-grid--value", children: stringifyData(value) })
] }, i)) })
] }) : /* @__PURE__ */ jsx2("div", { className: "yfiles-react-overview-node__content", children: customCompanyOwnershipItem.name ?? `ID: ${customCompanyOwnershipItem.id}` })
}
) });
}
// src/components/RenderTooltip.tsx
import { Fragment as Fragment3 } from "react";
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
function RenderTooltip({
data
}) {
const context = useCompanyOwnershipContext();
if (!data) {
return null;
}
let title = "";
if (context.isConnection(data)) {
title = `${data.sourceId} -> ${data.targetId}`;
} else {
title = data.name ?? String(data.id);
}
const properties = getUserProperties(data);
return /* @__PURE__ */ jsxs2("div", { className: "yfiles-react-tooltip", children: [
title && /* @__PURE__ */ jsx3("div", { className: "yfiles-react-tooltip__name", children: stringifyData(title) }),
/* @__PURE__ */ jsx3("div", { className: "yfiles-react-tooltip__data-grid", children: Object.entries(properties).map(([property, value], i) => /* @__PURE__ */ jsxs2(Fragment3, { children: [
/* @__PURE__ */ jsx3("div", { className: "yfiles-react-tooltip__data-grid--key", children: property }),
/* @__PURE__ */ jsx3("div", { className: "yfiles-react-tooltip__data-grid--value", children: stringifyData(value) })
] }, i)) })
] });
}
// src/components/CompanyOwnershipContextMenuItems.tsx
function DefaultContextMenuItems(item) {
const {
isConnection,
highlightConnectedItems,
showGenealogy,
canClearConnectedItemsHighlight,
clearConnectedItemsHighlight,
canShowAll,
showAll,
canShowSuccessors,
showSuccessors,
canHideSuccessors,
hideSuccessors,
canShowPredecessors,
showPredecessors,
canHidePredecessors,
hidePredecessors
} = useCompanyOwnershipContext();
const menuItems = [];
if (item) {
const isEdge = isConnection(item);
if (!isEdge) {
menuItems.push({
title: "Highlight Visible Connected Entities",
action: () => {
highlightConnectedItems(item);
}
});
menuItems.push({
title: "Filter Company Genealogy",
action: () => {
showGenealogy(item);
}
});
if (canShowSuccessors(item)) {
menuItems.push({
title: "Show Successors",
action: () => {
void showSuccessors(item);
}
});
}
if (canHideSuccessors(item)) {
menuItems.push({
title: "Hide Successors",
action: () => {
void hideSuccessors(item);
}
});
}
if (canShowPredecessors(item)) {
menuItems.push({
title: "Show Predecessors",
action: () => {
void showPredecessors(item);
}
});
}
if (canHidePredecessors(item)) {
menuItems.push({
title: "Hide Predecessors",
action: () => {
void hidePredecessors(item);
}
});
}
}
} else {
if (canClearConnectedItemsHighlight()) {
menuItems.push({
title: "Clear Connected Entities Highlight",
action: () => {
clearConnectedItemsHighlight();
}
});
}
if (canShowAll()) {
menuItems.push({
title: "Show Entire Diagram",
action: () => {
showAll();
}
});
}
}
return menuItems;
}
// src/components/CompanyOwnershipControlButtons.tsx
import {
DefaultControlButtons as CoreDefaultControlButtons
} from "@yworks/react-yfiles-core";
function DefaultControlButtons() {
return CoreDefaultControlButtons();
}
// src/CompanyOwnership.tsx
import {
useEffect,
useMemo as useMemo2,
useState
} from "react";
import {
BridgeCrossingStyle,
BridgeManager,
GraphObstacleProvider,
GroupPaddingProvider,
Size as Size2
} from "@yfiles/yfiles";
// src/core/input.ts
import {
GraphItemTypes,
GraphViewerInputMode as GraphViewerInputMode2,
IEdge as IEdge4,
INode as INode5
} from "@yfiles/yfiles";
// src/core/data-loading.ts
import {
LabelStyle as LabelStyle2,
GraphBuilder
} from "@yfiles/yfiles";
import {
convertToPolylineEdgeStyle,
ReactComponentHtmlNodeStyle
} from "@yworks/react-yfiles-core";
// src/styles/CompanyOwnershipNodeStyles.ts
import {
LabelStyle,
InteriorNodeLabelModel,
LabelDefaults,
Size
} from "@yfiles/yfiles";
// src/styles/CustomShapeNodeStyle.ts
import {
Fill,
GeneralPath,
GeneralPathNodeStyle,
NodeStyleBase,
Point,
Rect as Rect2,
Stroke
} from "@yfiles/yfiles";
var CustomShapeNodeStyle = class extends NodeStyleBase {
/**
* Creates the custom style for the given type of node.
*/
constructor(type, stroke, fill) {
super();
this.type = type;
this.stroke = stroke;
this.fill = fill;
this.type = type;
this.stroke = Stroke.from(stroke ?? "black");
this.fill = Fill.from(fill ?? "white");
let gp;
this.gpNodeStyle = new GeneralPathNodeStyle();
this.gpNodeStyle.stroke = this.stroke;
this.gpNodeStyle.fill = this.fill;
switch (type) {
case "Corporation":
gp = createCorporationPath();
break;
case "CTB":
gp = createCtbPath();
break;
case "Partnership":
gp = createPartnershipPath();
break;
case "RCTB":
gp = createRctbPath();
break;
case "Branch":
case "Individual":
case "Unknown":
gp = createBranchPath();
break;
case "Disregarded":
gp = createDisregardedPath();
break;
case "Dual Resident":
gp = createDualResidentPath();
break;
case "Multiple":
gp = createMultiplePath();
break;
case "Trust":
gp = createTrustPath();
break;
case "Third Party":
gp = createThirdPartyPath();
break;
case "Trapezoid":
gp = createTrapezoidPath();
break;
case "PE_Risk":
this.gpNodeStyle.stroke = new Stroke({
fill: this.stroke.fill,
thickness: 2,
dashStyle: "dash",
lineCap: "square"
});
this.gpNodeStyle.stroke.freeze();
gp = createPeRiskPath();
break;
case "Asset":
gp = createAssetPath();
break;
case "Octagon":
gp = createOctagonPath();
break;
case "Bank":
gp = createBankPath();
break;
default:
throw new Error("Unknown Type");
}
this.gpNodeStyle.path = gp;
this.gpNodeStyle.cssClass = `company-node company-node-${type.toLowerCase().replace(/[_\s]/g, "-")}`;
}
gpNodeStyle;
/**
* Creates the visual for the given node.
* @param renderContext The render context
* @param node The node to which this style is assigned
* @see Overrides {@link NodeStyleBase.createVisual}
*/
createVisual(renderContext, node) {
return this.gpNodeStyle.renderer.getVisualCreator(node, this.gpNodeStyle).createVisual(renderContext);
}
/**
* Updates the visual for the given node.
* @param renderContext The render context
* @param oldVisual The visual that has been created in the call to createVisual
* @param node The node to which this style is assigned
*/
updateVisual(renderContext, oldVisual, node) {
return this.gpNodeStyle.renderer.getVisualCreator(node, this.gpNodeStyle).updateVisual(renderContext, oldVisual);
}
/**
* Gets the outline of the visual style.
* @param node The node to which this style is assigned
*/
getOutline(node) {
switch (this.type) {
case "Asset":
case "Bank":
case "Branch":
case "Individual":
case "Multiple":
case "Octagon":
case "PE_Risk":
case "Partnership":
case "Third Party":
case "Trust":
case "Unknown":
return this.gpNodeStyle.renderer.getShapeGeometry(node, this.gpNodeStyle).getOutline();
default:
return null;
}
}
};
function createPartnershipPath() {
const gp = new GeneralPath();
gp.moveTo(0, 1);
gp.lineTo(0.5, 0);
gp.lineTo(1, 1);
gp.close();
return gp;
}
function createRctbPath() {
const gp = new GeneralPath();
gp.moveTo(0, 0);
gp.lineTo(1, 0);
gp.lineTo(1, 1);
gp.lineTo(0, 1);
gp.close();
gp.moveTo(1, 0);
gp.lineTo(0.5, 1);
gp.lineTo(0, 0);
return gp;
}
function createTrapezoidPath() {
const gp = new GeneralPath();
gp.moveTo(0, 0);
gp.lineTo(1, 0);
gp.lineTo(1, 1);
gp.lineTo(0, 1);
gp.close();
gp.moveTo(0.2, 0);
gp.lineTo(0.8, 0);
gp.lineTo(1, 1);
gp.lineTo(0, 1);
gp.lineTo(0.2, 0);
return gp;
}
function createBranchPath() {
const gp = new GeneralPath();
gp.appendEllipse(new Rect2(0, 0, 1, 1), false);
return gp;
}
function createDisregardedPath() {
const gp = new GeneralPath();
gp.moveTo(0, 0);
gp.lineTo(1, 0);
gp.lineTo(1, 1);
gp.lineTo(0, 1);
gp.close();
gp.appendEllipse(new Rect2(0, 0, 1, 1), false);
return gp;
}
function createDualResidentPath() {
const gp = new GeneralPath();
gp.moveTo(0, 0);
gp.lineTo(1, 0);
gp.lineTo(1, 1);
gp.lineTo(0, 1);
gp.close();
gp.moveTo(0, 1);
gp.lineTo(1, 0);
return gp;
}
function createMultiplePath() {
const gp = new GeneralPath();
gp.moveTo(0, 0);
gp.lineTo(0.9, 0);
gp.lineTo(0.9, 0.9);
gp.lineTo(0, 0.9);
gp.close();
gp.moveTo(0.9, 0.1);
gp.lineTo(1, 0.1);
gp.lineTo(1, 1);
gp.lineTo(0.1, 1);
gp.lineTo(0.1, 0.9);
gp.lineTo(0.9, 0.9);
gp.close();
return gp;
}
function createTrustPath() {
const gp = new GeneralPath();
gp.moveTo(0, 0.5);
gp.lineTo(0.5, 0);
gp.lineTo(1, 0.5);
gp.lineTo(0.5, 1);
gp.close();
return gp;
}
function createPeRiskPath() {
const gp = new GeneralPath();
gp.appendEllipse(new Rect2(0, 0, 1, 1), false);
return gp;
}
function createThirdPartyPath() {
const gp = new GeneralPath();
gp.moveTo(0.25273825759228363, 0.2106077406985223);
gp.cubicTo(
new Point(0.37940464379944383, 0.008533694660719517),
new Point(0.5427384738867617, -0.07436838307589484),
new Point(0.7327381431952041, 0.20542116176184805)
);
gp.cubicTo(
new Point(0.9727395859583705, 0.2054237109534111),
new Point(1.026070204427681, 0.5059367593821318),
new Point(0.9360671322061148, 0.6302855466359552)
);
gp.cubicTo(
new Point(0.9727385659844104, 1.0499824248785579),
new Point(0.7327384631870348, 0.9929823150021839),
new Point(0.5727383979886994, 0.9308125068113157)
);
gp.cubicTo(
new Point(0.37607164889080386, 1.044795193100142),
new Point(0.23606605323366095, 0.9878057307616991),
new Point(0.17274109991971903, 0.8064517974237797)
);
gp.cubicTo(
new Point(-0.1039264767570484, 0.68210650753643),
new Point(0.012736869827713297, 0.2572344906314848),
new Point(0.25273825759228363, 0.2106077406985223)
);
gp.close();
return gp;
}
function createCorporationPath() {
const gp = new GeneralPath();
gp.moveTo(0, 0);
gp.lineTo(1, 0);
gp.lineTo(1, 1);
gp.lineTo(0, 1);
gp.close();
return gp;
}
function createCtbPath() {
const gp = new GeneralPath();
gp.moveTo(0, 0);
gp.lineTo(1, 0);
gp.lineTo(1, 1);
gp.lineTo(0, 1);
gp.close();
gp.moveTo(0, 1);
gp.lineTo(0.5, 0);
gp.lineTo(1, 1);
return gp;
}
function createOctagonPath() {
const gp = new GeneralPath();
gp.moveTo(0.25, 0);
gp.lineTo(0.75, 0);
gp.lineTo(1, 0.25);
gp.lineTo(1, 0.75);
gp.lineTo(0.75, 1);
gp.lineTo(0.25, 1);
gp.lineTo(0, 0.75);
gp.lineTo(0, 0.25);
gp.close();
return gp;
}
function createAssetPath() {
const gp = new GeneralPath();
gp.moveTo(0, 0.5);
gp.lineTo(0.25, 0);
gp.lineTo(0.75, 0);
gp.lineTo(1, 0.5);
gp.lineTo(0.75, 1);
gp.lineTo(0.25, 1);
gp.lineTo(0, 0.5);
gp.close();
return gp;
}
function createBankPath() {
const gp = new GeneralPath();
gp.moveTo(0.5, 0);
gp.lineTo(0, 0.47);
gp.lineTo(0.191, 1);
gp.lineTo(0.809, 1);
gp.lineTo(1, 0.47);
gp.lineTo(0.5, 0);
gp.close();
return gp;
}
// src/styles/CompanyOwnershipNodeStyles.ts
function getNodeStyle(item) {
return new CustomShapeNodeStyle(item.type ?? "Unknown");
}
var nodeLabelStyle = new LabelStyle({
wrapping: "wrap-word-ellipsis",
horizontalTextAlignment: "center",
verticalTextAlignment: "center"
});
var nodeLabelParameter = InteriorNodeLabelModel.CENTER;
var labelSizeDefaults = new Size(80, 60);
var nameLabelDefaults = new LabelDefaults({
style: nodeLabelStyle,
layoutParameter: nodeLabelParameter,
autoAdjustPreferredSize: false
});
// src/core/data-loading.ts
var GraphManager = class {
constructor(graphBuilder, nodesSource, connectionsSource) {
this.graphBuilder = graphBuilder;
this.nodesSource = nodesSource;
this.connectionsSource = connectionsSource;
}
nodeData = [];
connectionsData = [];
renderEntities;
connectionStyleProvider;
connectionLabelProvider;
itemLabelProvider;
incrementalElements = [];
updateGraph(data, renderEntity, connectionStyleProvider, connectionLabelProvider, itemLabelProvider) {
const nodeData = data.companies;
const connectionsData = data.connections;
this.incrementalElements = compareData(this.nodeData, nodeData);
this.nodeData = nodeData;
this.connectionsData = connectionsData;
if (!this.graphBuilder || !this.nodesSource || !this.connectionsSource) {
return;
}
if (renderEntity) {
this.renderEntities = renderEntity;
}
if (connectionStyleProvider) {
this.connectionStyleProvider = connectionStyleProvider;
}
if (connectionLabelProvider) {
this.connectionLabelProvider = connectionLabelProvider;
} else {
this.connectionLabelProvider = (item) => {
return item.type === "Ownership" && typeof item.ownership !== "undefined" ? {
text: String(item.ownership)
} : void 0;
};
}
if (itemLabelProvider) {
this.itemLabelProvider = itemLabelProvider;
} else {
this.itemLabelProvider = (item) => {
return item.name ? {
text: item.name
} : {
text: `ID: ${item.id}`
};
};
}
this.graphBuilder.setData(this.nodesSource, nodeData);
this.graphBuilder.setData(this.connectionsSource, connectionsData);
this.graphBuilder.updateGraph();
}
};
function initializeGraphManager(graph, setNodeInfos, compa