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