@tensor_1/react 2.17.0 → 2.18.0

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
@@ -61,4 +61,33 @@ traversal, three-way JSON diffing, metric parsing/stepping, color conversion and
61
61
  contrast checks, plus brand-profile apply/restore helpers. No graph or date
62
62
  runtime dependency is required.
63
63
 
64
+ ### RelationshipGraph layouts
65
+
66
+ `RelationshipGraph` has two layouts:
67
+ - `radial` (the default): a small neighbourhood ringed around the selection.
68
+ - `layered` (ADR-0003): reads left to right by distance from `rootId`.
69
+
70
+ Layered mode draws shared nodes once, marks cycles, collapses leaf fan-outs into clusters and labels edges. It adds structural keyboard traversal (←/→ along edges, ↑/↓ in a column, Home, End), pan and Ctrl/⌘-wheel zoom.
71
+
72
+ ```tsx
73
+ <RelationshipGraph
74
+ layout="layered"
75
+ rootId={root.id}
76
+ nodes={nodes} // { id, label, type?, description?, depth? }
77
+ edges={edges} // { id, source, target, type?, label?, arrow?, emphasis? }
78
+ labels={translatedLabels} // Partial<RelationshipGraphLabels>
79
+ renderNode={(node) => <MyNodeBody node={node} />}
80
+ nodeAriaLabel={(node) => `${node.label}, ${riskText(node)}`}
81
+ onSelect={openPeek}
82
+ height="clamp(360px, 60vh, 640px)"
83
+ />
84
+ ```
85
+
86
+ Rules the component cannot enforce for you:
87
+
88
+ - **Whatever `renderNode` / `renderCluster` shows must also be in the accessible name.** Pass `nodeAriaLabel` whenever the node body carries meaning beyond `label`, `type` and `description`.
89
+ - **Keep the equivalent list.** `showList={false}` is allowed only when the same labelled region renders a complete equivalent: every node, its relations and its state.
90
+ - **Dimming is not a signal.** `dimmedNodeIds` must be accompanied by text that says what is dimmed and why.
91
+ - **Localise everything.** Every visible and announced string comes from `labels`; the English defaults live in `DEFAULT_RELATIONSHIP_GRAPH_LABELS`.
92
+
64
93
  Ships **ESM + CJS + types**, with per-file `"use client"` so it's safe under React Server Components. Part of the **[UIx v2 styleguide](https://github.com/COx-ORG-H/UIx)**.
@@ -1,10 +1,54 @@
1
1
  import * as react from 'react';
2
- import { RelationshipGraphNode, RelationshipGraphEdge } from '../relationship-graph-model.js';
2
+ import { ReactNode } from 'react';
3
+ import { RelationshipGraphNode, RelationshipGraphEdge, RelationshipGraphCluster } from '../relationship-graph-model.js';
3
4
 
4
5
  interface RelationshipGraphLegendItem {
5
6
  id: string;
6
7
  label: string;
7
8
  }
9
+ /** Every visible or announced string. Pass translations; English defaults fill the gaps. */
10
+ interface RelationshipGraphLabels {
11
+ graph: string;
12
+ controls: string;
13
+ list: string;
14
+ listHeading: string;
15
+ legend: string;
16
+ panLeft: string;
17
+ panRight: string;
18
+ panUp: string;
19
+ panDown: string;
20
+ left: string;
21
+ right: string;
22
+ up: string;
23
+ down: string;
24
+ zoomIn: string;
25
+ zoomOut: string;
26
+ fit: string;
27
+ reset: string;
28
+ zoomLevel: (percent: number) => string;
29
+ bounded: (shown: number, omitted: number) => string;
30
+ graphSummary: (nodes: number, edges: number) => string;
31
+ relationship: string;
32
+ conflict: string;
33
+ connectedTo: (names: string) => string;
34
+ noConnections: string;
35
+ expandNeighbors: string;
36
+ openDetails: string;
37
+ /** Layered column header, e.g. "2 hops · 14". */
38
+ column: (depth: number, count: number) => string;
39
+ cluster: (count: number, type: string | undefined, edgeLabel: string | undefined) => string;
40
+ expandCluster: string;
41
+ collapseCluster: (count: number, type: string | undefined) => string;
42
+ cycle: string;
43
+ relationships: (count: number) => string;
44
+ roleDescription: string;
45
+ keyboardHint: string;
46
+ loading: string;
47
+ retry: string;
48
+ emptyTitle: string;
49
+ emptyHint: string;
50
+ }
51
+ declare const DEFAULT_RELATIONSHIP_GRAPH_LABELS: RelationshipGraphLabels;
8
52
  interface RelationshipGraphProps {
9
53
  nodes: RelationshipGraphNode[];
10
54
  edges: RelationshipGraphEdge[];
@@ -12,6 +56,7 @@ interface RelationshipGraphProps {
12
56
  onSelect?: (node: RelationshipGraphNode) => void;
13
57
  onExpandNeighbors?: (node: RelationshipGraphNode) => void;
14
58
  onOpenDetails?: (node: RelationshipGraphNode) => void;
59
+ /** Node budget. Default 40 (radial) or 400 (layered). */
15
60
  maxNodes?: number;
16
61
  density?: 'compact' | 'standard';
17
62
  highlightedNodeIds?: ReadonlySet<string>;
@@ -23,8 +68,41 @@ interface RelationshipGraphProps {
23
68
  error?: string;
24
69
  onRetry?: () => void;
25
70
  className?: string;
71
+ /** `radial` (default) rings around the selection; `layered` reads left to right by distance from `rootId`. */
72
+ layout?: 'radial' | 'layered';
73
+ /** Layered: the column-0 node (default: the first node). */
74
+ rootId?: string;
75
+ labels?: Partial<RelationshipGraphLabels>;
76
+ /** Layered: node body. Whatever it shows must also be in `nodeAriaLabel`. */
77
+ renderNode?: (node: RelationshipGraphNode, state: {
78
+ selected: boolean;
79
+ dimmed: boolean;
80
+ }) => ReactNode;
81
+ /** Layered: full accessible name for a node. */
82
+ nodeAriaLabel?: (node: RelationshipGraphNode) => string;
83
+ /** Layered: cluster body. Whatever it shows must also be in the cluster's accessible name. */
84
+ renderCluster?: (cluster: RelationshipGraphCluster) => ReactNode;
85
+ /** Layered: minimum leaf-sibling group that collapses into a cluster. Default 6; 0 disables. */
86
+ clusterThreshold?: number;
87
+ /** Layered: controlled set of expanded cluster ids. */
88
+ expandedClusterIds?: ReadonlySet<string>;
89
+ onToggleCluster?: (clusterId: string, expanded: boolean) => void;
90
+ /** Layered: nodes drawn muted (state must also be stated in text by the consumer). */
91
+ dimmedNodeIds?: ReadonlySet<string>;
92
+ /** Layered: when edge labels show. `auto` = emphasised edges, small fan-outs, and edges of the active node. */
93
+ edgeLabels?: 'auto' | 'always' | 'never';
94
+ /** Render the built-in equivalent list. Set false ONLY when an equivalent list is rendered in the same region. */
95
+ showList?: boolean;
96
+ /** Layered: CSS length for the viewport height. */
97
+ height?: string;
98
+ /** Layered: controlled roving focus. */
99
+ focusId?: string;
100
+ onFocusChange?: (id: string) => void;
26
101
  }
27
102
  /** Bounded, deterministic SVG relationship view with an equivalent accessible list. */
28
- declare function RelationshipGraph({ nodes, edges, selectedId, onSelect, onExpandNeighbors, onOpenDetails, maxNodes, density, highlightedNodeIds, conflictedNodeIds, highlightedEdgeIds, conflictedEdgeIds, legend, loading, error, onRetry, className, }: RelationshipGraphProps): react.JSX.Element;
103
+ declare function RelationshipGraph(props: RelationshipGraphProps): react.JSX.Element;
104
+ type ResolvedRelationshipGraphProps = Omit<RelationshipGraphProps, 'labels'> & {
105
+ labels: RelationshipGraphLabels;
106
+ };
29
107
 
30
- export { RelationshipGraph, type RelationshipGraphLegendItem, type RelationshipGraphProps };
108
+ export { DEFAULT_RELATIONSHIP_GRAPH_LABELS, RelationshipGraph, type RelationshipGraphLabels, type RelationshipGraphLegendItem, type RelationshipGraphProps, type ResolvedRelationshipGraphProps };
@@ -1,11 +1,70 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useMemo, useRef, useState } from "react";
4
- import { boundRelationshipGraph, layoutRelationshipGraph, relationshipNeighbors, traverseRelationshipNode } from "../relationship-graph-model.js";
4
+ import { boundRelationshipGraph, layoutRelationshipGraph, traverseRelationshipNode } from "../relationship-graph-model.js";
5
5
  import { cx } from "../cx.js";
6
+ import { LayeredRelationshipGraph } from "./RelationshipGraphLayered.js";
7
+ import { EquivalentRelationshipList } from "./RelationshipGraphList.js";
8
+ const DEFAULT_RELATIONSHIP_GRAPH_LABELS = {
9
+ graph: "Relationship graph",
10
+ controls: "Graph view controls",
11
+ list: "Accessible relationship list",
12
+ listHeading: "Relationship list",
13
+ legend: "Relationship types",
14
+ panLeft: "Pan left",
15
+ panRight: "Pan right",
16
+ panUp: "Pan up",
17
+ panDown: "Pan down",
18
+ left: "Left",
19
+ right: "Right",
20
+ up: "Up",
21
+ down: "Down",
22
+ zoomIn: "Zoom in",
23
+ zoomOut: "Zoom out",
24
+ fit: "Fit",
25
+ reset: "Reset view",
26
+ zoomLevel: (percent) => `Zoom ${percent}%`,
27
+ bounded: (shown, omitted) => `Showing ${shown} nodes; ${omitted} outside this bounded neighborhood.`,
28
+ graphSummary: (nodes, edges) => `Graph with ${nodes} nodes and ${edges} relationships`,
29
+ relationship: "Relationship",
30
+ conflict: "conflict",
31
+ connectedTo: (names) => `Connected to ${names}`,
32
+ noConnections: "No visible connections",
33
+ expandNeighbors: "Expand neighbors",
34
+ openDetails: "Open details",
35
+ column: (depth, count) => `${depth === 0 ? "Start" : `${depth} ${depth === 1 ? "hop" : "hops"}`} \xB7 ${count}`,
36
+ cluster: (count, type, edgeLabel) => `${count} \xD7 ${type ?? "items"}${edgeLabel ? ` \xB7 ${edgeLabel}` : ""}`,
37
+ expandCluster: "Show all",
38
+ collapseCluster: (count, type) => `Collapse ${count} ${type ?? "items"}`,
39
+ cycle: "cycle",
40
+ relationships: (count) => `${count} ${count === 1 ? "relationship" : "relationships"}`,
41
+ roleDescription: "relationship map",
42
+ keyboardHint: "Arrow keys move between connected items, Enter selects, Home returns to the start.",
43
+ loading: "Loading relationships\u2026",
44
+ retry: "Try again",
45
+ emptyTitle: "No relationships to display.",
46
+ emptyHint: "Adjust the neighborhood or filters to include a node."
47
+ };
6
48
  const EMPTY_IDS = /* @__PURE__ */ new Set();
7
49
  const EMPTY_LEGEND = [];
8
- function RelationshipGraph({
50
+ function RelationshipGraph(props) {
51
+ const labels = useMemo(() => ({ ...DEFAULT_RELATIONSHIP_GRAPH_LABELS, ...props.labels }), [props.labels]);
52
+ const stateClass = cx("uix-relationship-graph uix-relationship-graph--state", props.className);
53
+ if (props.loading) return /* @__PURE__ */ jsx("div", { className: stateClass, role: "status", children: labels.loading });
54
+ if (props.error) {
55
+ return /* @__PURE__ */ jsxs("div", { className: stateClass, role: "alert", children: [
56
+ /* @__PURE__ */ jsx("p", { children: props.error }),
57
+ props.onRetry && /* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--secondary", onClick: props.onRetry, children: labels.retry })
58
+ ] });
59
+ }
60
+ if (props.nodes.length === 0) return /* @__PURE__ */ jsxs("div", { className: stateClass, children: [
61
+ /* @__PURE__ */ jsx("p", { children: labels.emptyTitle }),
62
+ /* @__PURE__ */ jsx("p", { children: labels.emptyHint })
63
+ ] });
64
+ if (props.layout === "layered") return /* @__PURE__ */ jsx(LayeredRelationshipGraph, { ...props, labels });
65
+ return /* @__PURE__ */ jsx(RadialRelationshipGraph, { ...props, labels });
66
+ }
67
+ function RadialRelationshipGraph({
9
68
  nodes,
10
69
  edges,
11
70
  selectedId,
@@ -19,10 +78,9 @@ function RelationshipGraph({
19
78
  highlightedEdgeIds = EMPTY_IDS,
20
79
  conflictedEdgeIds = EMPTY_IDS,
21
80
  legend = EMPTY_LEGEND,
22
- loading,
23
- error,
24
- onRetry,
25
- className
81
+ className,
82
+ labels,
83
+ showList = true
26
84
  }) {
27
85
  const bounded = useMemo(() => boundRelationshipGraph(nodes, edges, maxNodes), [nodes, edges, maxNodes]);
28
86
  const positioned = useMemo(() => layoutRelationshipGraph(bounded.nodes, bounded.edges, selectedId), [bounded, selectedId]);
@@ -53,49 +111,30 @@ function RelationshipGraph({
53
111
  setZoom(1);
54
112
  setPan({ x: 0, y: 0 });
55
113
  };
56
- if (loading) return /* @__PURE__ */ jsx("div", { className: cx("uix-relationship-graph uix-relationship-graph--state", className), role: "status", children: "Loading relationships\u2026" });
57
- if (error) return /* @__PURE__ */ jsxs("div", { className: cx("uix-relationship-graph uix-relationship-graph--state", className), role: "alert", children: [
58
- /* @__PURE__ */ jsx("p", { children: error }),
59
- onRetry && /* @__PURE__ */ jsx("button", { type: "button", className: "uix-btn uix-btn--secondary", onClick: onRetry, children: "Try again" })
60
- ] });
61
- if (bounded.nodes.length === 0) return /* @__PURE__ */ jsxs("div", { className: cx("uix-relationship-graph uix-relationship-graph--state", className), children: [
62
- /* @__PURE__ */ jsx("p", { children: "No relationships to display." }),
63
- /* @__PURE__ */ jsx("p", { children: "Adjust the neighborhood or filters to include a node." })
64
- ] });
65
- return /* @__PURE__ */ jsxs("section", { className: cx("uix-relationship-graph", `uix-relationship-graph--${density}`, className), "aria-label": "Relationship graph", children: [
66
- /* @__PURE__ */ jsxs("div", { className: "uix-relationship-graph__toolbar", "aria-label": "Graph view controls", children: [
67
- /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setPan((value) => ({ ...value, x: value.x - 24 })), "aria-label": "Pan left", children: "Left" }),
68
- /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setPan((value) => ({ ...value, y: value.y - 24 })), "aria-label": "Pan up", children: "Up" }),
69
- /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setPan((value) => ({ ...value, y: value.y + 24 })), "aria-label": "Pan down", children: "Down" }),
70
- /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setPan((value) => ({ ...value, x: value.x + 24 })), "aria-label": "Pan right", children: "Right" }),
71
- /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setZoom((value) => Math.min(2, Number((value + 0.2).toFixed(1)))), "aria-label": "Zoom in", children: "Zoom in" }),
72
- /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setZoom((value) => Math.max(0.5, Number((value - 0.2).toFixed(1)))), "aria-label": "Zoom out", children: "Zoom out" }),
73
- /* @__PURE__ */ jsx("button", { type: "button", onClick: reset, children: "Reset view" }),
74
- /* @__PURE__ */ jsxs("span", { "aria-live": "polite", children: [
75
- "Zoom ",
76
- Math.round(zoom * 100),
77
- "%"
78
- ] })
79
- ] }),
80
- bounded.omittedNodeCount > 0 && /* @__PURE__ */ jsxs("p", { className: "uix-relationship-graph__bounded", role: "status", children: [
81
- "Showing ",
82
- bounded.nodes.length,
83
- " nodes; ",
84
- bounded.omittedNodeCount,
85
- " outside this bounded neighborhood."
114
+ return /* @__PURE__ */ jsxs("section", { className: cx("uix-relationship-graph", `uix-relationship-graph--${density}`, className), "aria-label": labels.graph, children: [
115
+ /* @__PURE__ */ jsxs("div", { className: "uix-relationship-graph__toolbar", "aria-label": labels.controls, children: [
116
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setPan((value) => ({ ...value, x: value.x - 24 })), "aria-label": labels.panLeft, children: labels.left }),
117
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setPan((value) => ({ ...value, y: value.y - 24 })), "aria-label": labels.panUp, children: labels.up }),
118
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setPan((value) => ({ ...value, y: value.y + 24 })), "aria-label": labels.panDown, children: labels.down }),
119
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setPan((value) => ({ ...value, x: value.x + 24 })), "aria-label": labels.panRight, children: labels.right }),
120
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setZoom((value) => Math.min(2, Number((value + 0.2).toFixed(1)))), "aria-label": labels.zoomIn, children: labels.zoomIn }),
121
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setZoom((value) => Math.max(0.5, Number((value - 0.2).toFixed(1)))), "aria-label": labels.zoomOut, children: labels.zoomOut }),
122
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: reset, children: labels.reset }),
123
+ /* @__PURE__ */ jsx("span", { "aria-live": "polite", children: labels.zoomLevel(Math.round(zoom * 100)) })
86
124
  ] }),
87
- /* @__PURE__ */ jsx("div", { className: "uix-relationship-graph__visual", children: /* @__PURE__ */ jsx("svg", { ref: svgRef, viewBox: "0 0 600 400", role: "group", "aria-label": `Graph with ${bounded.nodes.length} nodes and ${bounded.edges.length} relationships`, children: /* @__PURE__ */ jsxs("g", { transform: `translate(${pan.x} ${pan.y}) scale(${zoom})`, children: [
125
+ bounded.omittedNodeCount > 0 && /* @__PURE__ */ jsx("p", { className: "uix-relationship-graph__bounded", role: "status", children: labels.bounded(bounded.nodes.length, bounded.omittedNodeCount) }),
126
+ /* @__PURE__ */ jsx("div", { className: "uix-relationship-graph__visual", children: /* @__PURE__ */ jsx("svg", { ref: svgRef, viewBox: "0 0 600 400", role: "group", "aria-label": labels.graphSummary(bounded.nodes.length, bounded.edges.length), children: /* @__PURE__ */ jsxs("g", { transform: `translate(${pan.x} ${pan.y}) scale(${zoom})`, children: [
88
127
  bounded.edges.map((edge) => {
89
128
  const source = positionedById.get(edge.source);
90
129
  const target = positionedById.get(edge.target);
91
- return /* @__PURE__ */ jsx("line", { x1: source.x * 6, y1: source.y * 4, x2: target.x * 6, y2: target.y * 4, className: "uix-relationship-graph__edge", "data-highlighted": highlightedEdgeIds.has(edge.id) || void 0, "data-conflicted": conflictedEdgeIds.has(edge.id) || void 0, children: /* @__PURE__ */ jsx("title", { children: edge.label ?? edge.type ?? "Relationship" }) }, edge.id);
130
+ return /* @__PURE__ */ jsx("line", { x1: source.x * 6, y1: source.y * 4, x2: target.x * 6, y2: target.y * 4, className: "uix-relationship-graph__edge", "data-highlighted": highlightedEdgeIds.has(edge.id) || void 0, "data-conflicted": conflictedEdgeIds.has(edge.id) || void 0, children: /* @__PURE__ */ jsx("title", { children: edge.label ?? edge.type ?? labels.relationship }) }, edge.id);
92
131
  }),
93
132
  positioned.map((node) => /* @__PURE__ */ jsxs(
94
133
  "g",
95
134
  {
96
135
  transform: `translate(${node.x * 6} ${node.y * 4})`,
97
136
  role: "button",
98
- "aria-label": `${node.label}${node.type ? `, ${node.type}` : ""}${conflictedNodeIds.has(node.id) ? ", conflict" : ""}`,
137
+ "aria-label": `${node.label}${node.type ? `, ${node.type}` : ""}${conflictedNodeIds.has(node.id) ? `, ${labels.conflict}` : ""}`,
99
138
  tabIndex: focusedId === node.id ? 0 : -1,
100
139
  "data-node-id": node.id,
101
140
  className: "uix-relationship-graph__node",
@@ -113,26 +152,11 @@ function RelationshipGraph({
113
152
  node.id
114
153
  ))
115
154
  ] }) }) }),
116
- legend.length > 0 && /* @__PURE__ */ jsx("ul", { className: "uix-relationship-graph__legend", "aria-label": "Relationship types", children: legend.map((item) => /* @__PURE__ */ jsx("li", { "data-type": item.id, children: item.label }, item.id)) }),
117
- /* @__PURE__ */ jsxs("div", { className: "uix-relationship-graph__list", role: "region", "aria-label": "Accessible relationship list", children: [
118
- /* @__PURE__ */ jsx("h3", { children: "Relationship list" }),
119
- /* @__PURE__ */ jsx("ul", { children: bounded.nodes.map((node) => {
120
- const neighbors = relationshipNeighbors(node.id, bounded.edges).map((id) => bounded.nodes.find((item) => item.id === id)?.label ?? id);
121
- return /* @__PURE__ */ jsxs("li", { "data-selected": selectedId === node.id || void 0, children: [
122
- /* @__PURE__ */ jsxs("button", { type: "button", onClick: () => select(node.id), "aria-pressed": selectedId === node.id, children: [
123
- /* @__PURE__ */ jsx("strong", { children: node.label }),
124
- node.type && /* @__PURE__ */ jsx("span", { children: node.type }),
125
- /* @__PURE__ */ jsx("span", { children: neighbors.length ? `Connected to ${neighbors.join(", ")}` : "No visible connections" })
126
- ] }),
127
- /* @__PURE__ */ jsxs("span", { className: "uix-relationship-graph__list-actions", children: [
128
- onExpandNeighbors && /* @__PURE__ */ jsx("button", { type: "button", onClick: () => onExpandNeighbors(node), children: "Expand neighbors" }),
129
- onOpenDetails && /* @__PURE__ */ jsx("button", { type: "button", onClick: () => onOpenDetails(node), children: "Open details" })
130
- ] })
131
- ] }, node.id);
132
- }) })
133
- ] })
155
+ legend.length > 0 && /* @__PURE__ */ jsx("ul", { className: "uix-relationship-graph__legend", "aria-label": labels.legend, children: legend.map((item) => /* @__PURE__ */ jsx("li", { "data-type": item.id, children: item.label }, item.id)) }),
156
+ showList && /* @__PURE__ */ jsx(EquivalentRelationshipList, { nodes: bounded.nodes, edges: bounded.edges, selectedId, labels, onSelect: select, onExpandNeighbors, onOpenDetails })
134
157
  ] });
135
158
  }
136
159
  export {
160
+ DEFAULT_RELATIONSHIP_GRAPH_LABELS,
137
161
  RelationshipGraph
138
162
  };
@@ -0,0 +1,8 @@
1
+ import * as react from 'react';
2
+ import { ResolvedRelationshipGraphProps } from './RelationshipGraph.js';
3
+ import '../relationship-graph-model.js';
4
+
5
+ /** `RelationshipGraph layout="layered"` (ADR-0003). Internal — reached through RelationshipGraph. */
6
+ declare function LayeredRelationshipGraph({ nodes, edges, selectedId, onSelect, onExpandNeighbors, onOpenDetails, maxNodes, density, highlightedNodeIds, conflictedNodeIds, highlightedEdgeIds, conflictedEdgeIds, legend, className, labels, rootId, renderNode, nodeAriaLabel, renderCluster, clusterThreshold, expandedClusterIds, onToggleCluster, dimmedNodeIds, edgeLabels, showList, height, focusId, onFocusChange, }: ResolvedRelationshipGraphProps): react.JSX.Element;
7
+
8
+ export { LayeredRelationshipGraph };