react-beauti-graph 0.0.0-stage → 1.3.1

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.
@@ -0,0 +1,246 @@
1
+ import type { FC } from 'react';
2
+ import cytoscape from 'cytoscape';
3
+ import type { StylesheetJson } from 'cytoscape';
4
+ export * from './default_setting.cjs';
5
+ /** Downloads the whole graph as a static SVG file named graph.svg. */
6
+ export declare function downloadSvg(cy: cytoscape.Core): void;
7
+ export declare const REPORT_NODE = "node[class = \"macromolecule\"]";
8
+ /** Group (folder box) names mapped to the names of the reports inside them */
9
+ export type NodeGroups = Record<string, string[]>;
10
+ /** Arrows: [source, target] */
11
+ export type Connections = [string, string][];
12
+ /** Layout grid: one list of report ids per row */
13
+ export type Layout = string[][];
14
+ /** Data of a graph element (a group box, a report or an arrow) */
15
+ export interface ElementData {
16
+ id: string;
17
+ class?: string;
18
+ label?: string;
19
+ rawLabel?: string;
20
+ displayLabel?: string;
21
+ group?: string | null;
22
+ parent?: string | undefined;
23
+ source?: string;
24
+ target?: string;
25
+ graphLabel?: string | undefined;
26
+ }
27
+ /** A Cytoscape element as built by buildGraph */
28
+ export interface GraphElement {
29
+ data: ElementData;
30
+ classes?: string;
31
+ }
32
+ /** Grid placement options of the layout */
33
+ export interface LayoutOptions {
34
+ startX?: number | undefined;
35
+ startY?: number | undefined;
36
+ offsetX?: number | undefined;
37
+ offsetY?: number | undefined;
38
+ base?: number | undefined;
39
+ }
40
+ /** A report listed in a group's details: its id, name and group */
41
+ export interface NodeRef {
42
+ id: string;
43
+ name: string;
44
+ group: string | null;
45
+ }
46
+ /** Details of a report shown in the side panel */
47
+ export interface NodeInfo extends NodeRef {
48
+ parents: NodeRef[];
49
+ children: NodeRef[];
50
+ }
51
+ /**
52
+ * Size of a report box: exactly the label plus a little padding.
53
+ * @param {string|undefined} label - The node label text
54
+ * @param {number} [fontSize] - Font size of the label in px
55
+ * @returns {{width: number, height: number}} Box size in px
56
+ */
57
+ export declare function getNodeSize(label: string | undefined, fontSize?: number): {
58
+ width: number;
59
+ height: number;
60
+ };
61
+ /**
62
+ * Compares names alphabetically, case-insensitive; numeric so "n2" comes before "n10".
63
+ * @param {string} a
64
+ * @param {string} b
65
+ * @returns {number}
66
+ */
67
+ export declare function compareNames(a: string, b: string): number;
68
+ /**
69
+ * Renames duplicate node IDs in layout to make them unique.
70
+ * Appends "(n)" suffix when duplicates are detected.
71
+ * @param {string[][]} layout - 2D array of node IDs arranged in rows/columns
72
+ * @returns {string[][]} Layout with renamed duplicates
73
+ */
74
+ export declare function uniquifyLayout(layout?: Layout): Layout;
75
+ /**
76
+ * Positions nodes in the Cytoscape graph based on layout specification.
77
+ * Handles missing/renamed nodes gracefully with fallbacks.
78
+ * @param {cytoscape.Core} cy - Cytoscape instance
79
+ * @param {string[][]} rows - 2D layout array with node IDs
80
+ * @param {Object} opts - Layout options (startX, startY, offsetX, offsetY, base)
81
+ */
82
+ export declare function applyLayout(cy: cytoscape.Core, rows: Layout, opts?: LayoutOptions): void;
83
+ /**
84
+ * Generates a unique ID by appending a counter suffix if needed.
85
+ * @param {string} base - Base ID string
86
+ * @param {Set<string>} taken - Set of already-used IDs (the new ID is added to it)
87
+ * @returns {string} Unique ID
88
+ */
89
+ export declare function uniqueId(base: string, taken: Set<string>): string;
90
+ /**
91
+ * Builds a layout from the groups, used when no layout is given.
92
+ * Each group starts a new row and fills rows of at most `columns` reports
93
+ * (group order, then report order); reports in no group come last.
94
+ * Copies of a report in several groups ("id(1)", ...) sit in their own group.
95
+ * @param {Object[]} elements - Cytoscape elements from buildGraph
96
+ * @param {number} [columns] - Maximum reports per row
97
+ * @returns {string[][]} Layout rows of node IDs
98
+ */
99
+ export declare function layoutFromFolders(elements: GraphElement[], columns?: number): Layout;
100
+ /**
101
+ * Builds the Cytoscape elements from groups, edges and extra node IDs.
102
+ * Folders become compound nodes (FOLDER_CLASS); every report becomes a
103
+ * REPORT_CLASS node. A report listed in several groups gets one copy per
104
+ * group ("id", "id(1)", ...); edges connect to the first copy.
105
+ * @param {Object<string, string[]>} node_groups - Mapping of group names to child IDs
106
+ * @param {Array<[string, string]>} direct_node_connections - Array of [source, target] edge pairs
107
+ * @param {string[]} extraIds - Other node IDs to create even without edges (e.g. from the layout)
108
+ * @returns {Object[]} Cytoscape elements array
109
+ */
110
+ export declare function buildGraph(node_groups?: NodeGroups, direct_node_connections?: Connections, extraIds?: string[]): GraphElement[];
111
+ /**
112
+ * Elements to draw for the current toggles: node label with or without
113
+ * the group, and groups shown or hidden.
114
+ * @param {Object[]} allElements - Output of buildGraph
115
+ * @param {boolean} showFolders - Keep the group (compartment) nodes
116
+ * @param {boolean} showGroupInLabel - Label reports "id (Group)" instead of "id"
117
+ * @returns {Object[]} Fresh element copies, safe to hand to a new Cytoscape instance
118
+ */
119
+ export declare function getVisibleElements(allElements: GraphElement[], showFolders: boolean, showGroupInLabel: boolean): GraphElement[];
120
+ /**
121
+ * Collects the details shown in the side panel for a report node.
122
+ * Parents point to the node, children are pointed to by it;
123
+ * a two-way edge makes the other node both a parent and a child.
124
+ * With allLevels, the lists hold the whole chains instead: the parents of the parents, ...
125
+ * and the children of the children, ... (the node itself is left out).
126
+ * @param {cytoscape.NodeSingular} node - The double-clicked node
127
+ * @param {boolean} [allLevels] - Every level of parents and children, not just the first
128
+ * @returns {{id: string, name: string, group: string|null, parents: Object[], children: Object[]}}
129
+ */
130
+ export declare function getNodeInfo(node: cytoscape.NodeSingular, allLevels?: boolean): NodeInfo;
131
+ /**
132
+ * Follows the edges from a node to collect its whole chain of parents
133
+ * (nodes pointing to it, their parents, ...) or children. A two-way edge
134
+ * counts in both directions, as in getNodeInfo.
135
+ * @param {cytoscape.NodeSingular} start - The tracked node
136
+ * @param {'parents'|'children'} direction - Which way to follow the edges
137
+ * @returns {cytoscape.Collection} The start node plus every node and edge on the chain
138
+ */
139
+ export declare function getTrackedElements(start: cytoscape.NodeSingular, direction: 'parents' | 'children'): cytoscape.Collection;
140
+ /**
141
+ * The elements to hide for the reports unchecked in "Node to Display": those reports
142
+ * (Cytoscape then hides their edges too) and the groups left with no visible report.
143
+ * @param {cytoscape.Core} cy - Cytoscape instance
144
+ * @param {Set<string>} hiddenIds - IDs of the reports to hide
145
+ * @returns {cytoscape.Collection} Elements to hide
146
+ */
147
+ export declare function getHiddenElements(cy: cytoscape.Core, hiddenIds: Set<string>): cytoscape.Collection;
148
+ /**
149
+ * Formats node details as plain text for the clipboard, matching the side panel:
150
+ * the group and the parent/child lists appear only when not empty.
151
+ * @param {ReturnType<typeof getNodeInfo>} info - Output of getNodeInfo
152
+ * @returns {string} Text to copy
153
+ */
154
+ export declare function formatNodeInfo(info: NodeInfo): string;
155
+ /**
156
+ * Mixes a "#rrggbb" or "rgb(r, g, b)" color with white (used as the tracked node's fill).
157
+ * Other color formats fall back to white.
158
+ * @param {string} color - Hex or rgb() color
159
+ * @param {number} [amount=0.88] - Share of white, 0..1
160
+ * @returns {string} Hex color
161
+ */
162
+ export declare function lightTint(color: string, amount?: number): string;
163
+ /**
164
+ * Highlights the tracked chains (Track node 1 and 2) and dims the rest.
165
+ * Elements on one chain get that track's class (track-parents, track2-children, ...);
166
+ * elements on both chains also get "track-mixed" with the common color in
167
+ * data(trackMixColor) and, for the tracked nodes, its light tint in data(trackMixFill).
168
+ * @param {cytoscape.Core} cy - Cytoscape instance
169
+ * @param {{id: string, direction: 'parents'|'children'}[]} tracks - Track 1 and Track 2 ('' id = off)
170
+ * @param {string} [commonColor] - Hex color of elements on both chains
171
+ */
172
+ export interface Track {
173
+ id: string;
174
+ direction: 'parents' | 'children';
175
+ }
176
+ export declare function applyTracking(cy: cytoscape.Core, tracks: Track[], commonColor?: string): void;
177
+ /**
178
+ * Cytoscape stylesheet; later rules override earlier ones.
179
+ * @param {{selectedColor?: string, parentsColor?: string, childrenColor?: string,
180
+ * track2ParentsColor?: string, track2ChildrenColor?: string}} [colors]
181
+ * Selected node color and Track node 1 / Track node 2 chain colors
182
+ * @returns {Object[]} Stylesheet
183
+ */
184
+ export interface StyleColors {
185
+ selectedColor?: string | undefined;
186
+ parentsColor?: string | undefined;
187
+ childrenColor?: string | undefined;
188
+ track2ParentsColor?: string | undefined;
189
+ track2ChildrenColor?: string | undefined;
190
+ }
191
+ export declare function makeStyles({ selectedColor, parentsColor, childrenColor, track2ParentsColor, track2ChildrenColor, }?: StyleColors): StylesheetJson;
192
+ /**
193
+ * Searchable node picker: typing any part of a name (case-insensitive) filters the list.
194
+ * Arrow keys move through the matches, Enter picks one, Escape closes the list.
195
+ * @param {string} [id] - id for the input (to link a <label>)
196
+ * @param {{id: string, label: string}[]} options - Nodes to choose from
197
+ * @param {string} selectedId - Currently selected node id ('' for none)
198
+ * @param {(id: string) => void} onSelect - Called with the chosen node id
199
+ * @param {string} [noneLabel] - Label of the first entry, which selects no node
200
+ * @param {Set<string>} [hiddenIds] - With onToggleVisible: nodes whose checkbox is off
201
+ * @param {(id: string) => void} [onToggleVisible] - When given, each node gets a checkbox
202
+ * (on = shown); called with the node id when it is clicked
203
+ */
204
+ export interface NodeSearchProps {
205
+ id?: string;
206
+ options: {
207
+ id: string;
208
+ label: string;
209
+ }[];
210
+ selectedId: string;
211
+ onSelect: (id: string) => void;
212
+ noneLabel?: string;
213
+ hiddenIds?: Set<string>;
214
+ onToggleVisible?: (id: string) => void;
215
+ }
216
+ export declare function NodeSearch({ id, options, selectedId, onSelect, noneLabel, hiddenIds, onToggleVisible, }: NodeSearchProps): React.JSX.Element;
217
+ /** Options of create_graph: grid placement, colors ("#rrggbb" or "rgb(r, g, b)") and text sizes (CSS lengths) */
218
+ export interface GraphOptions extends LayoutOptions {
219
+ /** Reports per row when the layout is built from the groups */
220
+ layoutColumns?: number | undefined;
221
+ selectedColor?: string | undefined;
222
+ parentsColor?: string | undefined;
223
+ childrenColor?: string | undefined;
224
+ track2ParentsColor?: string | undefined;
225
+ track2ChildrenColor?: string | undefined;
226
+ commonColor?: string | undefined;
227
+ topControlsFontSize?: string | undefined;
228
+ rightDisplayFontSize?: string | undefined;
229
+ }
230
+ /**
231
+ * Factory function that creates a React Graph component.
232
+ * Initializes Cytoscape instance, manages graph state, and handles node selection.
233
+ * @param {Object<string, string[]>} node_groups - Group (group box) names mapped to their report names
234
+ * @param {Array<[string, string]>} direct_node_connections - Edge definitions ([source, target] pairs)
235
+ * @param {string[][]|Object} [layoutOrOptions] - Layout grid (one row per layer). Optional: leave it out
236
+ * and pass the options in its place, e.g. create_graph(node_groups, direct_node_connections, options),
237
+ * to build the layout from the groups
238
+ * @param {Object} [optionsArg] - Layout rendering options (startX, startY, offsetX, offsetY, base,
239
+ * layoutColumns = reports per row when the layout is built from the groups)
240
+ * plus the colors ("#rrggbb"): selectedColor (Node to Display), parentsColor and
241
+ * childrenColor (Track node 1), track2ParentsColor and track2ChildrenColor (Track node 2;
242
+ * commonColor = elements on both chains, default rgb(239, 108, 0)), topControlsFontSize (text size of the top controls row, e.g. '16px')
243
+ * and rightDisplayFontSize (text size of the right details panel)
244
+ * @returns {React.FC} Graph component
245
+ */
246
+ export declare function create_graph(node_groups?: NodeGroups, direct_node_connections?: Connections, layoutOrOptions?: Layout | GraphOptions | null, optionsArg?: GraphOptions): FC;
@@ -0,0 +1,246 @@
1
+ import type { FC } from 'react';
2
+ import cytoscape from 'cytoscape';
3
+ import type { StylesheetJson } from 'cytoscape';
4
+ export * from './default_setting.js';
5
+ /** Downloads the whole graph as a static SVG file named graph.svg. */
6
+ export declare function downloadSvg(cy: cytoscape.Core): void;
7
+ export declare const REPORT_NODE = "node[class = \"macromolecule\"]";
8
+ /** Group (folder box) names mapped to the names of the reports inside them */
9
+ export type NodeGroups = Record<string, string[]>;
10
+ /** Arrows: [source, target] */
11
+ export type Connections = [string, string][];
12
+ /** Layout grid: one list of report ids per row */
13
+ export type Layout = string[][];
14
+ /** Data of a graph element (a group box, a report or an arrow) */
15
+ export interface ElementData {
16
+ id: string;
17
+ class?: string;
18
+ label?: string;
19
+ rawLabel?: string;
20
+ displayLabel?: string;
21
+ group?: string | null;
22
+ parent?: string | undefined;
23
+ source?: string;
24
+ target?: string;
25
+ graphLabel?: string | undefined;
26
+ }
27
+ /** A Cytoscape element as built by buildGraph */
28
+ export interface GraphElement {
29
+ data: ElementData;
30
+ classes?: string;
31
+ }
32
+ /** Grid placement options of the layout */
33
+ export interface LayoutOptions {
34
+ startX?: number | undefined;
35
+ startY?: number | undefined;
36
+ offsetX?: number | undefined;
37
+ offsetY?: number | undefined;
38
+ base?: number | undefined;
39
+ }
40
+ /** A report listed in a group's details: its id, name and group */
41
+ export interface NodeRef {
42
+ id: string;
43
+ name: string;
44
+ group: string | null;
45
+ }
46
+ /** Details of a report shown in the side panel */
47
+ export interface NodeInfo extends NodeRef {
48
+ parents: NodeRef[];
49
+ children: NodeRef[];
50
+ }
51
+ /**
52
+ * Size of a report box: exactly the label plus a little padding.
53
+ * @param {string|undefined} label - The node label text
54
+ * @param {number} [fontSize] - Font size of the label in px
55
+ * @returns {{width: number, height: number}} Box size in px
56
+ */
57
+ export declare function getNodeSize(label: string | undefined, fontSize?: number): {
58
+ width: number;
59
+ height: number;
60
+ };
61
+ /**
62
+ * Compares names alphabetically, case-insensitive; numeric so "n2" comes before "n10".
63
+ * @param {string} a
64
+ * @param {string} b
65
+ * @returns {number}
66
+ */
67
+ export declare function compareNames(a: string, b: string): number;
68
+ /**
69
+ * Renames duplicate node IDs in layout to make them unique.
70
+ * Appends "(n)" suffix when duplicates are detected.
71
+ * @param {string[][]} layout - 2D array of node IDs arranged in rows/columns
72
+ * @returns {string[][]} Layout with renamed duplicates
73
+ */
74
+ export declare function uniquifyLayout(layout?: Layout): Layout;
75
+ /**
76
+ * Positions nodes in the Cytoscape graph based on layout specification.
77
+ * Handles missing/renamed nodes gracefully with fallbacks.
78
+ * @param {cytoscape.Core} cy - Cytoscape instance
79
+ * @param {string[][]} rows - 2D layout array with node IDs
80
+ * @param {Object} opts - Layout options (startX, startY, offsetX, offsetY, base)
81
+ */
82
+ export declare function applyLayout(cy: cytoscape.Core, rows: Layout, opts?: LayoutOptions): void;
83
+ /**
84
+ * Generates a unique ID by appending a counter suffix if needed.
85
+ * @param {string} base - Base ID string
86
+ * @param {Set<string>} taken - Set of already-used IDs (the new ID is added to it)
87
+ * @returns {string} Unique ID
88
+ */
89
+ export declare function uniqueId(base: string, taken: Set<string>): string;
90
+ /**
91
+ * Builds a layout from the groups, used when no layout is given.
92
+ * Each group starts a new row and fills rows of at most `columns` reports
93
+ * (group order, then report order); reports in no group come last.
94
+ * Copies of a report in several groups ("id(1)", ...) sit in their own group.
95
+ * @param {Object[]} elements - Cytoscape elements from buildGraph
96
+ * @param {number} [columns] - Maximum reports per row
97
+ * @returns {string[][]} Layout rows of node IDs
98
+ */
99
+ export declare function layoutFromFolders(elements: GraphElement[], columns?: number): Layout;
100
+ /**
101
+ * Builds the Cytoscape elements from groups, edges and extra node IDs.
102
+ * Folders become compound nodes (FOLDER_CLASS); every report becomes a
103
+ * REPORT_CLASS node. A report listed in several groups gets one copy per
104
+ * group ("id", "id(1)", ...); edges connect to the first copy.
105
+ * @param {Object<string, string[]>} node_groups - Mapping of group names to child IDs
106
+ * @param {Array<[string, string]>} direct_node_connections - Array of [source, target] edge pairs
107
+ * @param {string[]} extraIds - Other node IDs to create even without edges (e.g. from the layout)
108
+ * @returns {Object[]} Cytoscape elements array
109
+ */
110
+ export declare function buildGraph(node_groups?: NodeGroups, direct_node_connections?: Connections, extraIds?: string[]): GraphElement[];
111
+ /**
112
+ * Elements to draw for the current toggles: node label with or without
113
+ * the group, and groups shown or hidden.
114
+ * @param {Object[]} allElements - Output of buildGraph
115
+ * @param {boolean} showFolders - Keep the group (compartment) nodes
116
+ * @param {boolean} showGroupInLabel - Label reports "id (Group)" instead of "id"
117
+ * @returns {Object[]} Fresh element copies, safe to hand to a new Cytoscape instance
118
+ */
119
+ export declare function getVisibleElements(allElements: GraphElement[], showFolders: boolean, showGroupInLabel: boolean): GraphElement[];
120
+ /**
121
+ * Collects the details shown in the side panel for a report node.
122
+ * Parents point to the node, children are pointed to by it;
123
+ * a two-way edge makes the other node both a parent and a child.
124
+ * With allLevels, the lists hold the whole chains instead: the parents of the parents, ...
125
+ * and the children of the children, ... (the node itself is left out).
126
+ * @param {cytoscape.NodeSingular} node - The double-clicked node
127
+ * @param {boolean} [allLevels] - Every level of parents and children, not just the first
128
+ * @returns {{id: string, name: string, group: string|null, parents: Object[], children: Object[]}}
129
+ */
130
+ export declare function getNodeInfo(node: cytoscape.NodeSingular, allLevels?: boolean): NodeInfo;
131
+ /**
132
+ * Follows the edges from a node to collect its whole chain of parents
133
+ * (nodes pointing to it, their parents, ...) or children. A two-way edge
134
+ * counts in both directions, as in getNodeInfo.
135
+ * @param {cytoscape.NodeSingular} start - The tracked node
136
+ * @param {'parents'|'children'} direction - Which way to follow the edges
137
+ * @returns {cytoscape.Collection} The start node plus every node and edge on the chain
138
+ */
139
+ export declare function getTrackedElements(start: cytoscape.NodeSingular, direction: 'parents' | 'children'): cytoscape.Collection;
140
+ /**
141
+ * The elements to hide for the reports unchecked in "Node to Display": those reports
142
+ * (Cytoscape then hides their edges too) and the groups left with no visible report.
143
+ * @param {cytoscape.Core} cy - Cytoscape instance
144
+ * @param {Set<string>} hiddenIds - IDs of the reports to hide
145
+ * @returns {cytoscape.Collection} Elements to hide
146
+ */
147
+ export declare function getHiddenElements(cy: cytoscape.Core, hiddenIds: Set<string>): cytoscape.Collection;
148
+ /**
149
+ * Formats node details as plain text for the clipboard, matching the side panel:
150
+ * the group and the parent/child lists appear only when not empty.
151
+ * @param {ReturnType<typeof getNodeInfo>} info - Output of getNodeInfo
152
+ * @returns {string} Text to copy
153
+ */
154
+ export declare function formatNodeInfo(info: NodeInfo): string;
155
+ /**
156
+ * Mixes a "#rrggbb" or "rgb(r, g, b)" color with white (used as the tracked node's fill).
157
+ * Other color formats fall back to white.
158
+ * @param {string} color - Hex or rgb() color
159
+ * @param {number} [amount=0.88] - Share of white, 0..1
160
+ * @returns {string} Hex color
161
+ */
162
+ export declare function lightTint(color: string, amount?: number): string;
163
+ /**
164
+ * Highlights the tracked chains (Track node 1 and 2) and dims the rest.
165
+ * Elements on one chain get that track's class (track-parents, track2-children, ...);
166
+ * elements on both chains also get "track-mixed" with the common color in
167
+ * data(trackMixColor) and, for the tracked nodes, its light tint in data(trackMixFill).
168
+ * @param {cytoscape.Core} cy - Cytoscape instance
169
+ * @param {{id: string, direction: 'parents'|'children'}[]} tracks - Track 1 and Track 2 ('' id = off)
170
+ * @param {string} [commonColor] - Hex color of elements on both chains
171
+ */
172
+ export interface Track {
173
+ id: string;
174
+ direction: 'parents' | 'children';
175
+ }
176
+ export declare function applyTracking(cy: cytoscape.Core, tracks: Track[], commonColor?: string): void;
177
+ /**
178
+ * Cytoscape stylesheet; later rules override earlier ones.
179
+ * @param {{selectedColor?: string, parentsColor?: string, childrenColor?: string,
180
+ * track2ParentsColor?: string, track2ChildrenColor?: string}} [colors]
181
+ * Selected node color and Track node 1 / Track node 2 chain colors
182
+ * @returns {Object[]} Stylesheet
183
+ */
184
+ export interface StyleColors {
185
+ selectedColor?: string | undefined;
186
+ parentsColor?: string | undefined;
187
+ childrenColor?: string | undefined;
188
+ track2ParentsColor?: string | undefined;
189
+ track2ChildrenColor?: string | undefined;
190
+ }
191
+ export declare function makeStyles({ selectedColor, parentsColor, childrenColor, track2ParentsColor, track2ChildrenColor, }?: StyleColors): StylesheetJson;
192
+ /**
193
+ * Searchable node picker: typing any part of a name (case-insensitive) filters the list.
194
+ * Arrow keys move through the matches, Enter picks one, Escape closes the list.
195
+ * @param {string} [id] - id for the input (to link a <label>)
196
+ * @param {{id: string, label: string}[]} options - Nodes to choose from
197
+ * @param {string} selectedId - Currently selected node id ('' for none)
198
+ * @param {(id: string) => void} onSelect - Called with the chosen node id
199
+ * @param {string} [noneLabel] - Label of the first entry, which selects no node
200
+ * @param {Set<string>} [hiddenIds] - With onToggleVisible: nodes whose checkbox is off
201
+ * @param {(id: string) => void} [onToggleVisible] - When given, each node gets a checkbox
202
+ * (on = shown); called with the node id when it is clicked
203
+ */
204
+ export interface NodeSearchProps {
205
+ id?: string;
206
+ options: {
207
+ id: string;
208
+ label: string;
209
+ }[];
210
+ selectedId: string;
211
+ onSelect: (id: string) => void;
212
+ noneLabel?: string;
213
+ hiddenIds?: Set<string>;
214
+ onToggleVisible?: (id: string) => void;
215
+ }
216
+ export declare function NodeSearch({ id, options, selectedId, onSelect, noneLabel, hiddenIds, onToggleVisible, }: NodeSearchProps): React.JSX.Element;
217
+ /** Options of create_graph: grid placement, colors ("#rrggbb" or "rgb(r, g, b)") and text sizes (CSS lengths) */
218
+ export interface GraphOptions extends LayoutOptions {
219
+ /** Reports per row when the layout is built from the groups */
220
+ layoutColumns?: number | undefined;
221
+ selectedColor?: string | undefined;
222
+ parentsColor?: string | undefined;
223
+ childrenColor?: string | undefined;
224
+ track2ParentsColor?: string | undefined;
225
+ track2ChildrenColor?: string | undefined;
226
+ commonColor?: string | undefined;
227
+ topControlsFontSize?: string | undefined;
228
+ rightDisplayFontSize?: string | undefined;
229
+ }
230
+ /**
231
+ * Factory function that creates a React Graph component.
232
+ * Initializes Cytoscape instance, manages graph state, and handles node selection.
233
+ * @param {Object<string, string[]>} node_groups - Group (group box) names mapped to their report names
234
+ * @param {Array<[string, string]>} direct_node_connections - Edge definitions ([source, target] pairs)
235
+ * @param {string[][]|Object} [layoutOrOptions] - Layout grid (one row per layer). Optional: leave it out
236
+ * and pass the options in its place, e.g. create_graph(node_groups, direct_node_connections, options),
237
+ * to build the layout from the groups
238
+ * @param {Object} [optionsArg] - Layout rendering options (startX, startY, offsetX, offsetY, base,
239
+ * layoutColumns = reports per row when the layout is built from the groups)
240
+ * plus the colors ("#rrggbb"): selectedColor (Node to Display), parentsColor and
241
+ * childrenColor (Track node 1), track2ParentsColor and track2ChildrenColor (Track node 2;
242
+ * commonColor = elements on both chains, default rgb(239, 108, 0)), topControlsFontSize (text size of the top controls row, e.g. '16px')
243
+ * and rightDisplayFontSize (text size of the right details panel)
244
+ * @returns {React.FC} Graph component
245
+ */
246
+ export declare function create_graph(node_groups?: NodeGroups, direct_node_connections?: Connections, layoutOrOptions?: Layout | GraphOptions | null, optionsArg?: GraphOptions): FC;
package/dist/index.cjs ADDED
@@ -0,0 +1,3 @@
1
+ "use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,s)=>(s=n==null?{}:e(i(n)),o(r||!n||!n.__esModule||!a.call(n,`default`)?t(s,`default`,{value:n,enumerable:!0}):s,n));let c=require("react"),l=require("cytoscape");l=s(l,1);let u=require("cytoscape-svg");u=s(u,1);let d=require("react/jsx-runtime");var f=`#e91e63`,p=`#2e7d32`,m=`#7b1fa2`,h=`#efeb00`,g=`#8b4513`,_=`rgb(239, 108, 0)`,v=`22px`,y=`18px`,b=`#0066cc`,x=`#fff`,S=`#f0f8ff`;l.default.use(u.default);function C(e){let t=e.svg({full:!0,scale:1}),n=URL.createObjectURL(new Blob([t],{type:`image/svg+xml`})),r=document.createElement(`a`);r.href=n,r.download=`graph.svg`,r.click(),URL.revokeObjectURL(n)}var w=2,T=-8,E=16,D=16,O=14,ee=[[`top`,`Font Top Panel`],[`right`,`Font Right Panel`],[`graph`,`Font Graph Panel`]],k=`Helvetica Neue, Helvetica, Arial, sans-serif`,A=1.2,j=.6,M=`macromolecule`,N=`compartment`,P=`node[class = "${M}"]`,F;function te(e,t){if(F===void 0)try{F=document.createElement(`canvas`).getContext(`2d`)}catch{F=null}return F?(F.font=`${t}px ${k}`,F.measureText(e).width):e.length*t*j}function ne(e,t=D){return{width:Math.ceil(te(e??``,t))+8,height:Math.ceil(t*A)+8}}function I(e,t){return e.localeCompare(t,void 0,{numeric:!0,sensitivity:`base`})}function L(e=[]){let t=new Map;return e.map(e=>e.map(e=>{let n=t.get(e)??0;if(t.set(e,n+1),n===0)return e;let r=`${e}(${n})`;return console.warn(`layout: duplicate id "${e}" — renamed to "${r}"`),r}))}function re(e,t,n={}){let{startX:r=0,startY:i=0,offsetX:a=200,offsetY:o=200,base:s=1}=n,c=new Set;t.forEach((t,n)=>{t.forEach((t,l)=>{let u=t,d=e.getElementById(u);if(d.empty()){let n=/^(.+)\((\d+)\)$/.exec(t)?.[1];n!==void 0&&e.getElementById(n).nonempty()&&(u=n,d=e.getElementById(u),console.warn(`applyLayout: "${t}" not found — using "${u}" instead`))}d.empty()?console.warn(`applyLayout: node "${t}" not found`):c.has(u)?console.warn(`applyLayout: duplicate layout entry for node "${u}" (from "${t}") — keeping the first position`):(c.add(u),d.position({x:r+(l+s)*a,y:i+(n+s)*o}))})})}function R(e,t){let n=e,r=2;for(;t.has(n);)n=`${e}_${r++}`;return t.add(n),n}function z(e,t=10){let n=Math.max(1,Math.floor(t)||1),r=e.filter(e=>e.data.class===M),i=e.filter(e=>e.data.class===N).map(e=>r.filter(t=>t.data.parent===e.data.id));i.push(r.filter(e=>!e.data.parent));let a=[];for(let e of i)for(let t=0;t<e.length;t+=n)a.push(e.slice(t,t+n).map(e=>e.data.id));return a}function B(e={},t=[],n=[]){let r=[],i=new Set,a=new Set([...t.flat(),...n]);for(let t of Object.values(e))for(let e of t)i.add(e);for(let e of a)i.add(e);let o=e=>{let t=e.replace(/\s+/g,``);return t?t.charAt(0).toLowerCase()+t.slice(1):`folder`},s=new Map,c=new Map;for(let[t,n]of Object.entries(e)){let e=R(o(t),i);c.set(t,e),r.push({data:{id:e,class:N,label:t}});for(let e of n){let n=s.get(e)??[],r=e;if(n.length>0){let t=n.length;for(r=`${e}(${t})`;i.has(r);)r=`${e}(${++t})`;i.add(r)}n.push({actualId:r,folderLabel:t}),s.set(e,n)}}let l=[];for(let[e,t]of s)for(let{actualId:n,folderLabel:r}of t)l.push({actualId:n,originalId:e,folderLabel:r});for(let e of a)s.has(e)||l.push({actualId:e,originalId:e,folderLabel:null});for(let{actualId:e,originalId:t,folderLabel:n}of l){let i={id:e,class:M,rawLabel:t,displayLabel:n?`${t} (${n})`:t,group:n};n&&(i.parent=c.get(n)),r.push({data:i})}let u=e=>s.get(e)?.[0]?.actualId??e,d=(e,t)=>`${e}->${t}`,f=new Map;return t.forEach(([e,t],n)=>{let a=u(e),o=u(t);if(f.has(d(a,o)))return;let s=f.get(d(o,a));if(s){s.classes=`bidirectional`;return}let c={data:{id:R(`e${n+1}`,i),source:a,target:o}};f.set(d(a,o),c),r.push(c)}),r}function ie(e,t,n){return e.filter(e=>t||e.data.class!==N).map(e=>{if(e.data.class!==M)return{...e,data:{...e.data}};let t=n?e.data.displayLabel:e.data.rawLabel;return{...e,data:{...e.data,graphLabel:t}}})}function V(e,t=!1){let n=e=>({id:e.id(),name:e.data(`rawLabel`)??e.id(),group:e.data(`group`)??null}),r=new Map,i=new Map;if(e.connectedEdges().forEach(t=>{let a=t.hasClass(`bidirectional`);t.target().same(e)&&(r.set(t.source().id(),n(t.source())),a&&i.set(t.source().id(),n(t.source()))),t.source().same(e)&&(i.set(t.target().id(),n(t.target())),a&&r.set(t.target().id(),n(t.target())))}),t){let t=[[r,`parents`],[i,`children`]];for(let[r,i]of t)r.clear(),H(e,i).nodes().forEach(t=>{t.same(e)||r.set(t.id(),n(t))})}let a=(e,t)=>I(e.name,t.name);return{...n(e),parents:[...r.values()].sort(a),children:[...i.values()].sort(a)}}function H(e,t){let n=t===`parents`,r=e.cy().collection(),i=new Set([e.id()]),a=[e];for(let e=a.shift();e!==void 0;e=a.shift()){let t=e;t.connectedEdges().forEach(e=>{let o=e.hasClass(`bidirectional`),s=null;e.target().same(t)&&(n||o)&&(s=e.source()),e.source().same(t)&&(!n||o)&&(s=e.target()),s&&(r=r.union(e),i.has(s.id())||(i.add(s.id()),a.push(s)))})}return r.union(e.cy().nodes().filter(e=>i.has(e.id())))}function U(e){return[[`Parent Nodes`,e.parents],[`Child Nodes`,e.children]].filter(([,e])=>e.length>0)}function ae(e,t){let n=e.nodes(P).filter(e=>t.has(e.id())),r=e.nodes(`:parent`).filter(e=>e.children().every(e=>n.has(e)));return n.union(r)}function W(e){let t=e=>e.group?`${e.name} (${e.group})`:e.name,n=[`Node: ${e.name}`];e.group&&n.push(`Group: ${e.group}`);for(let[r,i]of U(e))n.push(``,`${r} (${i.length}):`,...i.map(t));return n.join(`
2
+ `)}function G(e){let t=/^#([0-9a-f]{6})$/i.exec(e)?.[1];if(t!==void 0)return parseInt(t,16);let n=/^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/i.exec(e);if(n===null)return null;let[r=0,i=0,a=0]=n.slice(1).map(e=>Math.min(Number(e),255));return r<<16|i<<8|a}function K(e,t=.88){let n=G(e);return n===null?`#ffffff`:`#${[16,8,0].map(e=>{let r=n>>e&255;return Math.round(r+(255-r)*t).toString(16).padStart(2,`0`)}).join(``)}`}function oe(e,t,n=_){let r=[`track`,`track2`],i=r.flatMap(e=>[`${e}-parents`,`${e}-children`,`${e}-root`]).concat(`faded`,`track-mixed`).join(` `);e.batch(()=>{e.elements().removeClass(i).removeData(`trackMixColor trackMixFill`);let a=t.map(({id:t,direction:n},i)=>{if(!t)return null;let a=e.getElementById(t);if(a.empty())return null;let o=H(a,n),s=r[i]??`track`;return o.addClass(`${s}-${n}`),a.addClass(`${s}-root`),o}),o=a.filter(e=>e!==null);if(o.length===0)return;let[s,c]=a;s&&c&&s.intersection(c).forEach(e=>{e.data({trackMixColor:n,trackMixFill:K(n)}),e.addClass(`track-mixed`)});let l=o.reduce((e,t)=>e.union(t));e.elements().not(l).not(`node:parent`).addClass(`faded`)})}function q({selectedColor:e=f,parentsColor:t=p,childrenColor:n=m,track2ParentsColor:r=h,track2ChildrenColor:i=g}={}){return[{selector:`node`,style:{"background-color":x,label:`data(graphLabel)`,shape:`rectangle`,"font-family":k,"font-size":D,color:b,"border-width":2,"border-color":b,"text-valign":`center`,"text-halign":`center`}},{selector:`node:parent`,style:{"background-color":S,"background-opacity":.5,"border-style":`dashed`,label:`data(label)`,"text-valign":`top`,"font-size":O,"font-weight":`bold`,padding:`18px`}},{selector:`.selected`,style:{"overlay-color":e,"overlay-opacity":.35,"overlay-padding":8}},{selector:`edge`,style:{"line-color":b,width:2,"curve-style":`bezier`,"target-arrow-shape":`vee`,"target-arrow-color":b,"target-arrow-fill":`filled`,"arrow-scale":2}},{selector:`edge:loop`,style:{"loop-direction":`0deg`,"loop-sweep":`-50deg`,"control-point-step-size":70}},{selector:`edge.bidirectional`,style:{"source-arrow-shape":`vee`,"source-arrow-color":b,"source-arrow-fill":`filled`}},{selector:`.faded`,style:{opacity:.15}},...[[`track-parents`,`track-root`,t],[`track-children`,`track-root`,n],[`track2-parents`,`track2-root`,r],[`track2-children`,`track2-root`,i]].flatMap(([e,t,n])=>[{selector:`edge.${e}`,style:{"line-color":n,"target-arrow-color":n,"source-arrow-color":n,width:4}},{selector:`node.${e}`,style:{"border-color":n,"border-width":3}},{selector:`node.${e}.${t}`,style:{"border-width":5,"background-color":K(n)}}]),{selector:`edge.track-mixed`,style:{"line-color":`data(trackMixColor)`,"target-arrow-color":`data(trackMixColor)`,"source-arrow-color":`data(trackMixColor)`,width:5}},{selector:`node.track-mixed`,style:{"border-color":`data(trackMixColor)`,"border-width":4}},{selector:`node.track-mixed.track-root, node.track-mixed.track2-root`,style:{"border-width":6,"background-color":`data(trackMixFill)`}},{selector:`.hidden`,style:{display:`none`}}]}var J=`ALL`;function Y({id:e,options:t,selectedId:n,onSelect:r,noneLabel:i=J,hiddenIds:a,onToggleVisible:o}){let[s,l]=(0,c.useState)(``),[u,f]=(0,c.useState)(``),[p,m]=(0,c.useState)(!1),[h,g]=(0,c.useState)(0),_=(0,c.useRef)(null),v=(0,c.useRef)(null),y=t.find(e=>e.id===n)?.label??``,[b,x]=(0,c.useState)(null);b!==y&&(x(y),l(y));let S=u.trim().toLowerCase(),C=S?t.filter(e=>e.label.toLowerCase().includes(S)):t,w={id:``,label:i},T=!S||i.toLowerCase().includes(S)?[w,...C]:C,E=()=>{f(``),g(0),m(!0)},D=()=>{m(!1),f(``),l(y)},O=e=>{r(e.id),l(e.id?e.label:``),f(``),m(!1)};return(0,c.useEffect)(()=>{if(!p)return;let e=e=>{_.current?.contains(e.target)||D()};return document.addEventListener(`mousedown`,e),()=>document.removeEventListener(`mousedown`,e)}),(0,c.useEffect)(()=>{v.current?.children[h]?.scrollIntoView({block:`nearest`})},[h,p]),(0,d.jsxs)(`div`,{className:`node-search`,ref:_,children:[(0,d.jsx)(`input`,{id:e,type:`text`,value:s,placeholder:`-- type to search --`,autoComplete:`off`,onChange:e=>{l(e.target.value),f(e.target.value),g(0),m(!0)},onFocus:e=>{e.target.select(),E()},onClick:()=>{p||E()},onKeyDown:e=>{if(e.key===`ArrowDown`)e.preventDefault(),p||m(!0),g(e=>Math.min(e+1,T.length-1));else if(e.key===`ArrowUp`)e.preventDefault(),g(e=>Math.max(e-1,0));else if(e.key===`Enter`&&p){let t=T[h];t&&(e.preventDefault(),O(t))}else e.key===`Escape`&&D()}}),s&&(0,d.jsx)(`button`,{type:`button`,className:`node-search-clear`,"aria-label":`Clear`,onClick:()=>{r(``),l(``),f(``)},children:`×`}),p&&(0,d.jsx)(`ul`,{className:`node-search-list`,ref:v,role:`listbox`,children:T.length===0?(0,d.jsx)(`li`,{className:`node-search-empty`,role:`option`,"aria-selected":`false`,"aria-disabled":`true`,children:`no match`}):T.map((e,t)=>(0,d.jsxs)(`li`,{role:`option`,"aria-selected":t===h,className:[t===h&&`active`,a?.has(e.id)&&`off`].filter(Boolean).join(` `)||void 0,onMouseDown:t=>{t.preventDefault(),O(e)},onMouseEnter:()=>g(t),children:[o&&e.id&&(0,d.jsx)(`input`,{type:`checkbox`,checked:!a?.has(e.id),"aria-label":`Show ${e.label}`,onMouseDown:e=>{e.preventDefault(),e.stopPropagation()},onClick:e=>e.stopPropagation(),onChange:()=>o(e.id)}),e.label]},e.id||`__none__`))})]})}function X(e={},t=[],n=null,r={}){let i=Array.isArray(n)?n:null,a=n!==null&&!Array.isArray(n)?n:r,o=i!==null&&i.length>0,{selectedColor:s=f,parentsColor:u=p,childrenColor:b=m,track2ParentsColor:x=h,track2ChildrenColor:S=g,commonColor:k=_,topControlsFontSize:A=v,rightDisplayFontSize:j=y,layoutColumns:M=10}=a,N=q({selectedColor:s,parentsColor:u,childrenColor:b,track2ParentsColor:x,track2ChildrenColor:S}),F={"--rbg-parents-color":u,"--rbg-children-color":b},te={"--rbg-parents-color":x,"--rbg-children-color":S},R=B(e,t,i!==null&&o?i.flat():[]),H=i!==null&&o?L(i):z(R,M);function G(){let e=(0,c.useRef)(null),t=(0,c.useRef)(null),n=(0,c.useId)(),[r,i]=(0,c.useState)([]),[o,s]=(0,c.useState)(``),[u,f]=(0,c.useState)(0),[p,m]=(0,c.useState)(``),[h,g]=(0,c.useState)(`parents`),[_,v]=(0,c.useState)(``),[y,b]=(0,c.useState)(`parents`),[x,S]=(0,c.useState)(!0),[M,L]=(0,c.useState)(!0),[z,B]=(0,c.useState)(null),[G,K]=(0,c.useState)(null),[q,J]=(0,c.useState)(`top`),[X,se]=(0,c.useState)({top:0,right:0,graph:0}),[Z,ce]=(0,c.useState)(!1),Q=(0,c.useRef)(!1);(0,c.useEffect)(()=>{Q.current=Z},[Z]);let[$,le]=(0,c.useState)(()=>new Set);(0,c.useEffect)(()=>{let n=e.current;if(!n)return;let r=(0,l.default)({container:n,elements:ie(R,x,M),style:N,layout:{name:`preset`,fit:!1}});r.on(`cxttap`,P,e=>{let t=W(V(e.target,Q.current)),{clientX:n,clientY:r}=e.originalEvent;B({x:n,y:r,copyText:t})}),r.on(`dbltap`,P,e=>{K(V(e.target,Q.current))}),r.on(`tap cxttap`,e=>{e.target===r&&B(null)}),r.on(`tap`,`node`,()=>B(null)),r.on(`viewport`,()=>B(null));let o=e=>e.preventDefault();return n.addEventListener(`contextmenu`,o),re(r,H,a),requestAnimationFrame(()=>r.resize()),i(r.nodes(P).map(e=>({id:e.id(),label:e.data(`displayLabel`)})).sort((e,t)=>I(e.label,t.label))),t.current=r,()=>{n.removeEventListener(`contextmenu`,o),B(null),r.destroy(),t.current=null}},[x,M]),(0,c.useEffect)(()=>{let e=t.current;if(!e)return;let n=X.graph;e.batch(()=>{e.nodes(P).forEach(e=>{let t=D+n,{width:r,height:i}=ne(e.data(`graphLabel`),t);e.style({"font-size":t,width:r,height:i})}),e.nodes(`:parent`).style(`font-size`,O+n)})},[X.graph,x,M]),(0,c.useEffect)(()=>{if(!z)return;let e=e=>{e.target.closest(`.node-context-menu`)||B(null)},t=e=>{e.key===`Escape`&&B(null)};return document.addEventListener(`mousedown`,e),document.addEventListener(`keydown`,t),()=>{document.removeEventListener(`mousedown`,e),document.removeEventListener(`keydown`,t)}},[z]);let ue=()=>{if(z===null)return;let{copyText:e}=z;B(null),navigator.clipboard.writeText(e).catch(e=>{console.error(`Failed to copy:`,e)})},de=()=>{t.current&&C(t.current)},fe=e=>{le(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},pe=e=>{let n=t.current?.getElementById(e);n&&!n.empty()&&($.has(e)&&fe(e),s(e),K(V(n,Z)))},me=()=>{s(``),K(null),f(e=>e+1)};(0,c.useEffect)(()=>{let e=t.current;e&&e.batch(()=>{e.elements().removeClass(`hidden`),ae(e,$).addClass(`hidden`)})},[$,x,M]),(0,c.useEffect)(()=>{let e=t.current;u&&e&&(e.stop(),e.resize(),e.animate({fit:{eles:e.elements().not(`.hidden`),padding:30}},{duration:400}))},[u]),(0,c.useEffect)(()=>{let e=t.current;if(!e||(e.nodes().removeClass(`selected`),!o))return;let n=e.getElementById(o);n.nonempty()&&(n.addClass(`selected`),e.animate({center:{eles:n},zoom:1.8},{duration:400}))},[o,x,M]),(0,c.useEffect)(()=>{let e=t.current;e&&oe(e,[{id:p,direction:h},{id:_,direction:y}],k)},[p,h,_,y,x,M]),(0,c.useEffect)(()=>{t.current?.resize()},[G!==null]),(0,c.useEffect)(()=>{let e=()=>t.current?.resize();return window.addEventListener(`resize`,e),()=>window.removeEventListener(`resize`,e)},[]);let he=[{n:1,id:p,setId:m,direction:h,setDirection:g,style:void 0},{n:2,id:_,setId:v,direction:y,setDirection:b,style:te}],ge=[{label:`Smaller text`,symbol:`−`,step:-2,atLimit:X[q]<=T},{label:`Larger text`,symbol:`+`,step:w,atLimit:X[q]>=E}];return(0,d.jsxs)(`div`,{className:`rbg-graph`,style:{...F,"--rbg-top-controls-font-size":`calc(${A} + ${X.top}px)`,"--rbg-right-display-font-size":`calc(${j} + ${X.right}px)`},children:[(0,d.jsxs)(`div`,{className:`controls`,children:[(0,d.jsxs)(`span`,{className:`control-group`,children:[(0,d.jsx)(`label`,{htmlFor:`${n}-search`,children:`Node to Display:`}),(0,d.jsx)(Y,{id:`${n}-search`,options:r,selectedId:o,onSelect:e=>e?pe(e):me(),hiddenIds:$,onToggleVisible:fe})]}),(0,d.jsxs)(`label`,{children:[(0,d.jsx)(`input`,{type:`checkbox`,checked:x,onChange:e=>S(e.target.checked)}),`Show Groups`]}),(0,d.jsxs)(`label`,{children:[(0,d.jsx)(`input`,{type:`checkbox`,checked:M,onChange:e=>L(e.target.checked)}),`Add Group to Node Name`]}),he.map(({n:e,id:t,setId:i,direction:a,setDirection:o,style:s})=>(0,d.jsxs)(`span`,{className:`control-group`,style:s,children:[(0,d.jsxs)(`label`,{htmlFor:`${n}-track${e}`,children:[`Show Dependency `,e,`:`]}),(0,d.jsx)(Y,{id:`${n}-track${e}`,options:r,selectedId:t,onSelect:i,noneLabel:`Show Nothing`}),(0,d.jsxs)(`span`,{className:t?`track-switch`:`track-switch disabled`,children:[(0,d.jsx)(`span`,{className:a===`parents`?`parents on`:`parents`,children:`Parents`}),(0,d.jsx)(`button`,{type:`button`,role:`switch`,"aria-checked":a===`children`,"aria-label":`Show Dependency ${e}: track children instead of parents`,className:`switch`,disabled:!t,onClick:()=>o(e=>e===`parents`?`children`:`parents`),children:(0,d.jsx)(`span`,{className:`switch-knob`})}),(0,d.jsx)(`span`,{className:a===`children`?`children on`:`children`,children:`Children`})]})]},e)),(0,d.jsxs)(`span`,{className:`help`,children:[(0,d.jsx)(`button`,{type:`button`,className:`help-icon`,"aria-label":`Help`,"aria-describedby":`${n}-help`,children:`?`}),(0,d.jsxs)(`span`,{id:`${n}-help`,role:`tooltip`,className:`help-tooltip`,children:[(0,d.jsx)(`strong`,{children:`Double click`}),` on a node to see its information, parents and children.`,(0,d.jsx)(`br`,{}),(0,d.jsx)(`strong`,{children:`Right click`}),` on a node to save the same information to the clipboard.`]})]}),(0,d.jsxs)(`span`,{className:`font-size-buttons`,children:[(0,d.jsx)(`select`,{"aria-label":`Area whose text size the + and - buttons change`,value:q,onChange:e=>J(e.target.value),children:ee.map(([e,t])=>(0,d.jsx)(`option`,{value:e,children:t},e))}),ge.map(({label:e,symbol:t,step:n,atLimit:r})=>(0,d.jsx)(`button`,{type:`button`,className:`help-icon`,"aria-label":e,disabled:r,onClick:()=>se(e=>({...e,[q]:e[q]+n})),children:t},e))]}),(0,d.jsx)(`button`,{type:`button`,className:`export-svg`,onClick:de,children:`Export SVG`})]}),(0,d.jsxs)(`div`,{className:`graph-area`,children:[(0,d.jsx)(`div`,{ref:e,className:`cy-container`}),G&&(0,d.jsxs)(`aside`,{className:`node-info-panel`,children:[(0,d.jsxs)(`div`,{className:`node-info-header`,children:[(0,d.jsxs)(`h2`,{children:[(0,d.jsx)(`strong`,{children:`Node:`}),` `,G.name]}),(0,d.jsx)(`button`,{type:`button`,onClick:()=>K(null),"aria-label":`Close`,children:`×`})]}),G.group&&(0,d.jsxs)(`p`,{children:[(0,d.jsx)(`strong`,{children:`Group:`}),` `,G.group]}),(0,d.jsxs)(`div`,{className:`track-switch level-switch`,children:[(0,d.jsx)(`span`,{className:Z?``:`parents on`,children:`One level`}),(0,d.jsx)(`button`,{type:`button`,role:`switch`,"aria-checked":Z,"aria-label":`Show all levels of parents and children`,className:`switch`,onClick:()=>{let e=!Z;ce(e);let n=t.current?.getElementById(G.id);n&&!n.empty()&&K(V(n,e))},children:(0,d.jsx)(`span`,{className:`switch-knob`})}),(0,d.jsx)(`span`,{className:Z?`children on`:``,children:`All levels`})]}),U(G).map(([e,t])=>(0,d.jsxs)(`section`,{children:[(0,d.jsxs)(`h3`,{children:[e,` (`,t.length,`):`]}),(0,d.jsx)(`ul`,{children:t.map(e=>(0,d.jsx)(`li`,{children:(0,d.jsxs)(`button`,{type:`button`,onClick:()=>pe(e.id),children:[e.name,e.group&&(0,d.jsxs)(`span`,{children:[` (`,e.group,`)`]})]})},e.id))})]},e))]})]}),z&&(0,d.jsx)(`div`,{className:`node-context-menu`,style:{left:z.x,top:z.y},onContextMenu:e=>e.preventDefault(),children:(0,d.jsx)(`button`,{type:`button`,onClick:ue,children:`Save to clipboard`})})]})}return G.displayName=`CytoscapeGraph`,G}exports.NodeSearch=Y,exports.create_graph=X,exports.downloadSvg=C,exports.formatNodeInfo=W,exports.getNodeInfo=V;
3
+ //# sourceMappingURL=index.cjs.map