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.
- package/LICENSE +191 -0
- package/NOTICE +8 -0
- package/README.md +399 -2
- package/dist/default_setting.d.cts +17 -0
- package/dist/default_setting.d.ts +17 -0
- package/dist/graphFactory.d.cts +246 -0
- package/dist/graphFactory.d.ts +246 -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 +887 -0
- package/dist/index.js.map +1 -0
- package/dist/style.css +380 -0
- package/package.json +95 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["svg","useState","useRef","useId","__create","__defProp","__getOwnPropDesc","__getOwnPropNames","__getProtoOf","__hasOwnProp","__copyProps","from","keys","i","n","key","to","except","k","desc","__toESM","mod","isNodeMode","target","react","cytoscape","cytoscape_svg","react_jsx_runtime","DEFAULT_SELECTED_COLOR","DEFAULT_TRACK1_PARENTS_COLOR","DEFAULT_TRACK1_CHILDREN_COLOR","DEFAULT_TRACK2_PARENTS_COLOR","DEFAULT_TRACK2_CHILDREN_COLOR","DEFAULT_TRACK_COMMON_COLOR","DEFAULT_TOP_CONTROLS_FONT_SIZE","DEFAULT_RIGHT_DISPLAY_FONT_SIZE","GRAPH_COLOR","REPORT_BACKGROUND_COLOR","FOLDER_BACKGROUND_COLOR","downloadSvg","markup","cy","url","a","FONT_STEP","MIN_FONT_DELTA","MAX_FONT_DELTA","REPORT_FONT_SIZE","FOLDER_FONT_SIZE","FONT_AREAS","LABEL_FONT_FAMILY","LINE_HEIGHT","FALLBACK_CHAR_WIDTH","REPORT_CLASS","FOLDER_CLASS","REPORT_NODE","measureContext","measureText","fontSize","text","getNodeSize","label","compareNames","b","uniquifyLayout","counts","layout","row","id","newId","applyLayout","opts","placed","rows","ids","rawId","node","original","startX","colIndex","base","offsetX","startY","rowIndex","offsetY","uniqueId","taken","layoutFromFolders","perRow","columns","reports","elements","groups","r","folder","group","buildGraph","takenIds","looseIds","direct_node_connections","extraIds","children","node_groups","makeFolderId","cleaned","instances","folderIdByLabel","folderLabel","folderId","childId","existing","actualId","originalId","copies","data","resolve","pairKey","source","edgeByPair","rawSource","rawTarget","reverse","edge","index","getVisibleElements","allElements","showFolders","elem","graphLabel","showGroupInLabel","getNodeInfo","describe","parents","twoWay","allLevels","chains","map","direction","getTrackedElements","byName","upstream","trail","start","seen","queue","current","next","infoSections","info","list","getHiddenElements","hiddenIds","emptyFolders","child","formatNodeInfo","item","lines","title","parseColor","hex","color","rgb","red","green","blue","part","lightTint","value","channel","shift","amount","applyTracking","PREFIXES","allClasses","p","trails","tracks","root","prefix","active","first","second","ele","commonColor","onChains","all","makeStyles","selectedColor","parentsColor","childrenColor","track2ParentsColor","track2ChildrenColor","cls","NO_NODE_LABEL","NodeSearch","query","setQuery","filter","setFilter","open","setOpen","setActive","boxRef","listRef","selectedLabel","options","o","selectedId","shownLabel","setShownLabel","needle","nodeMatches","noneOption","noneLabel","matches","openList","close","choose","onSelect","opt","onMouseDown","chosen","e","onToggleVisible","create_graph","layoutOrOptions","optionsArg","hasLayout","GRAPH_STYLES","CSS_VARS","TRACK2_CSS_VARS","ALL_ELEMENTS","LAYOUT","layoutColumns","Graph","containerRef","cyRef","uid","nodeOptions","setNodeOptions","setSelectedId","fitRequest","setFitRequest","trackedId","setTrackedId","trackDirection","setTrackDirection","tracked2Id","setTracked2Id","track2Direction","setTrack2Direction","setShowFolders","setShowGroupInLabel","contextMenu","setContextMenu","nodeInfo","setNodeInfo","fontArea","setFontArea","fontDeltas","setFontDeltas","setAllLevels","allLevelsRef","setHiddenIds","containerEl","copyText","evt","clientX","clientY","preventNativeMenu","delta","onKeyDown","copyToClipboard","err","exportSvg","toggleVisible","prev","showNode","showAll","onResize","TRACK_CONTROLS","fontButtons","topControlsFontSize","rightDisplayFontSize","setId","setDirection","d","area","atLimit","step","symbol"],"sources":["../src/default_setting.ts","../src/graphFactory.tsx"],"sourcesContent":["/**\r\n * default_setting.js\r\n * Default colors, text sizes and layout of the graph. The DEFAULT_* ones can be overridden\r\n * with the create_graph option named in its comment.\r\n */\r\n\r\n// Color of the node picked in \"Node to Display\" (options.selectedColor)\r\nexport const DEFAULT_SELECTED_COLOR = '#e91e63';\r\n// \"Track node 1\" chain colors (options.parentsColor, options.childrenColor)\r\nexport const DEFAULT_TRACK1_PARENTS_COLOR = '#2e7d32';\r\nexport const DEFAULT_TRACK1_CHILDREN_COLOR = '#7b1fa2';\r\n// \"Track node 2\" chain colors (options.track2ParentsColor, options.track2ChildrenColor).\r\n// Nodes and edges on both chains are drawn in DEFAULT_TRACK_COMMON_COLOR.\r\nexport const DEFAULT_TRACK2_PARENTS_COLOR = '#efeb00';\r\nexport const DEFAULT_TRACK2_CHILDREN_COLOR = '#8b4513';\r\n// Color of nodes and edges common to both tracks: rgb(239, 108, 0) (options.commonColor)\r\nexport const DEFAULT_TRACK_COMMON_COLOR = 'rgb(239, 108, 0)';\r\n// Reports per row when the layout is built from the folders (options.layoutColumns)\r\nexport const DEFAULT_LAYOUT_COLUMNS = 10;\r\n// Text size of the top controls row: \"Node to Display\", \"Track node\", ... (options.topControlsFontSize)\r\nexport const DEFAULT_TOP_CONTROLS_FONT_SIZE = '22px';\r\n// Text size of the right details panel (options.rightDisplayFontSize)\r\nexport const DEFAULT_RIGHT_DISPLAY_FONT_SIZE = '18px';\r\n\r\n// ---- Fixed colors (not create_graph options) ----\r\n\r\n// Base color of the graph: node text, node borders, edges and arrows (blue)\r\nexport const GRAPH_COLOR = '#0066cc';\r\n// Fill of a report box\r\nexport const REPORT_BACKGROUND_COLOR = '#fff';\r\n// Fill of a folder (group) box\r\nexport const FOLDER_BACKGROUND_COLOR = '#f0f8ff';\r\n","'use client';\r\n\r\n/**\r\n * graphFactory.tsx\r\n * Factory for creating Cytoscape graph components: report nodes grouped in\r\n * groups, placed on a grid layout, with search, a details panel and a\r\n * \"save to clipboard\" context menu.\r\n */\r\n\r\nimport { useEffect, useId, useRef, useState } from 'react';\r\nimport type { CSSProperties, FC } from 'react';\r\nimport cytoscape from 'cytoscape';\r\nimport type { StylesheetJson } from 'cytoscape';\r\n// @ts-expect-error cytoscape-svg ships no type declarations\r\nimport svg from 'cytoscape-svg';\r\n\r\n// Defaults live in default_setting.ts; re-exported so they stay importable from here\r\nimport {\r\n DEFAULT_SELECTED_COLOR,\r\n DEFAULT_TRACK1_PARENTS_COLOR,\r\n DEFAULT_TRACK1_CHILDREN_COLOR,\r\n DEFAULT_TRACK2_PARENTS_COLOR,\r\n DEFAULT_TRACK2_CHILDREN_COLOR,\r\n DEFAULT_TRACK_COMMON_COLOR,\r\n DEFAULT_LAYOUT_COLUMNS,\r\n DEFAULT_TOP_CONTROLS_FONT_SIZE,\r\n DEFAULT_RIGHT_DISPLAY_FONT_SIZE,\r\n GRAPH_COLOR,\r\n REPORT_BACKGROUND_COLOR,\r\n FOLDER_BACKGROUND_COLOR,\r\n} from './default_setting.ts';\r\n\r\nexport * from './default_setting.ts';\r\n\r\n// cytoscape-svg adds cy.svg(); it has no types, so the method is typed here\r\ncytoscape.use(svg as cytoscape.Ext);\r\ninterface SvgExporter {\r\n svg(options: { full: boolean; scale: number }): string;\r\n}\r\n\r\n/** Downloads the whole graph as a static SVG file named graph.svg. */\r\nexport function downloadSvg(cy: cytoscape.Core): void {\r\n const markup = (cy as unknown as SvgExporter).svg({ full: true, scale: 1 });\r\n const url = URL.createObjectURL(new Blob([markup], { type: 'image/svg+xml' }));\r\n const a = document.createElement('a');\r\n a.href = url;\r\n a.download = 'graph.svg';\r\n a.click();\r\n URL.revokeObjectURL(url);\r\n}\r\n\r\n// The + / - buttons change the text size of the chosen area by this many pixels per click,\r\n// within these limits. Sizes of the graph text (px) before the buttons change them.\r\nconst FONT_STEP = 2;\r\nconst MIN_FONT_DELTA = -8;\r\nconst MAX_FONT_DELTA = 16;\r\nconst REPORT_FONT_SIZE = 16;\r\nconst FOLDER_FONT_SIZE = 14;\r\n// Areas the font dropdown can pick\r\ntype FontArea = 'top' | 'right' | 'graph';\r\nconst FONT_AREAS: readonly (readonly [FontArea, string])[] = [\r\n ['top', 'Font Top Panel'],\r\n ['right', 'Font Right Panel'],\r\n ['graph', 'Font Graph Panel'],\r\n];\r\n\r\n// Report box: as big as its label plus this much space (px) around the text on every side\r\nconst REPORT_PADDING = 4;\r\nconst LABEL_FONT_FAMILY = 'Helvetica Neue, Helvetica, Arial, sans-serif';\r\n// Height of one line of text, as a share of the font size\r\nconst LINE_HEIGHT = 1.2;\r\n// Width of one character as a share of the font size, for when the text cannot be measured\r\nconst FALLBACK_CHAR_WIDTH = 0.6;\r\n\r\n// Element types, stored in data.class (not a Cytoscape class)\r\nconst REPORT_CLASS = 'macromolecule';\r\nconst FOLDER_CLASS = 'compartment';\r\n// Selector for report nodes (selects by data, see above)\r\nexport const REPORT_NODE = `node[class = \"${REPORT_CLASS}\"]`;\r\n\r\n/** Group (folder box) names mapped to the names of the reports inside them */\r\nexport type NodeGroups = Record<string, string[]>;\r\n/** Arrows: [source, target] */\r\nexport type Connections = [string, string][];\r\n/** Layout grid: one list of report ids per row */\r\nexport type Layout = string[][];\r\n\r\n/** Data of a graph element (a group box, a report or an arrow) */\r\nexport interface ElementData {\r\n id: string;\r\n class?: string;\r\n label?: string;\r\n rawLabel?: string;\r\n displayLabel?: string;\r\n group?: string | null;\r\n parent?: string | undefined;\r\n source?: string;\r\n target?: string;\r\n graphLabel?: string | undefined;\r\n}\r\n\r\n/** A Cytoscape element as built by buildGraph */\r\nexport interface GraphElement {\r\n data: ElementData;\r\n classes?: string;\r\n}\r\n\r\n/** Grid placement options of the layout */\r\nexport interface LayoutOptions {\r\n startX?: number | undefined;\r\n startY?: number | undefined;\r\n offsetX?: number | undefined;\r\n offsetY?: number | undefined;\r\n base?: number | undefined;\r\n}\r\n\r\n/** A report listed in a group's details: its id, name and group */\r\nexport interface NodeRef {\r\n id: string;\r\n name: string;\r\n group: string | null;\r\n}\r\n\r\n/** Details of a report shown in the side panel */\r\nexport interface NodeInfo extends NodeRef {\r\n parents: NodeRef[];\r\n children: NodeRef[];\r\n}\r\n\r\nlet measureContext: CanvasRenderingContext2D | null | undefined;\r\n\r\n/**\r\n * Width in px of a text drawn with the label font, measured on a canvas.\r\n * Without a canvas (tests, server) it is estimated from the number of characters.\r\n * @param {string} text - The label\r\n * @param {number} fontSize - Font size in px\r\n * @returns {number} Width in px\r\n */\r\nfunction measureText(text: string, fontSize: number): number {\r\n if (measureContext === undefined) {\r\n try {\r\n measureContext = document.createElement('canvas').getContext('2d');\r\n } catch {\r\n measureContext = null;\r\n }\r\n }\r\n if (!measureContext) {\r\n return text.length * fontSize * FALLBACK_CHAR_WIDTH;\r\n }\r\n measureContext.font = `${fontSize}px ${LABEL_FONT_FAMILY}`;\r\n return measureContext.measureText(text).width;\r\n}\r\n\r\n/**\r\n * Size of a report box: exactly the label plus a little padding.\r\n * @param {string|undefined} label - The node label text\r\n * @param {number} [fontSize] - Font size of the label in px\r\n * @returns {{width: number, height: number}} Box size in px\r\n */\r\nexport function getNodeSize(\r\n label: string | undefined,\r\n fontSize: number = REPORT_FONT_SIZE\r\n): { width: number; height: number } {\r\n return {\r\n width: Math.ceil(measureText(label ?? '', fontSize)) + REPORT_PADDING * 2,\r\n height: Math.ceil(fontSize * LINE_HEIGHT) + REPORT_PADDING * 2,\r\n };\r\n}\r\n\r\n/**\r\n * Compares names alphabetically, case-insensitive; numeric so \"n2\" comes before \"n10\".\r\n * @param {string} a\r\n * @param {string} b\r\n * @returns {number}\r\n */\r\nexport function compareNames(a: string, b: string): number {\r\n return a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' });\r\n}\r\n\r\n/**\r\n * Renames duplicate node IDs in layout to make them unique.\r\n * Appends \"(n)\" suffix when duplicates are detected.\r\n * @param {string[][]} layout - 2D array of node IDs arranged in rows/columns\r\n * @returns {string[][]} Layout with renamed duplicates\r\n */\r\nexport function uniquifyLayout(layout: Layout = []): Layout {\r\n const counts = new Map<string, number>();\r\n return layout.map((row) =>\r\n row.map((id) => {\r\n const n = counts.get(id) ?? 0;\r\n counts.set(id, n + 1);\r\n if (n === 0) {\r\n return id;\r\n }\r\n const newId = `${id}(${n})`;\r\n console.warn(`layout: duplicate id \"${id}\" — renamed to \"${newId}\"`);\r\n return newId;\r\n })\r\n );\r\n}\r\n\r\n/**\r\n * Positions nodes in the Cytoscape graph based on layout specification.\r\n * Handles missing/renamed nodes gracefully with fallbacks.\r\n * @param {cytoscape.Core} cy - Cytoscape instance\r\n * @param {string[][]} rows - 2D layout array with node IDs\r\n * @param {Object} opts - Layout options (startX, startY, offsetX, offsetY, base)\r\n */\r\nexport function applyLayout(cy: cytoscape.Core, rows: Layout, opts: LayoutOptions = {}): void {\r\n const { startX = 0, startY = 0, offsetX = 200, offsetY = 200, base = 1 } = opts;\r\n\r\n const placed = new Set();\r\n\r\n rows.forEach((ids, rowIndex) => {\r\n ids.forEach((rawId, colIndex) => {\r\n let id = rawId;\r\n let node = cy.getElementById(id);\r\n\r\n if (node.empty()) {\r\n const m = /^(.+)\\((\\d+)\\)$/.exec(rawId);\r\n const original = m?.[1];\r\n if (original !== undefined && cy.getElementById(original).nonempty()) {\r\n id = original;\r\n node = cy.getElementById(id);\r\n console.warn(`applyLayout: \"${rawId}\" not found — using \"${id}\" instead`);\r\n }\r\n }\r\n\r\n if (node.empty()) {\r\n console.warn(`applyLayout: node \"${rawId}\" not found`);\r\n return;\r\n }\r\n\r\n if (placed.has(id)) {\r\n console.warn(\r\n `applyLayout: duplicate layout entry for node \"${id}\" ` +\r\n `(from \"${rawId}\") — keeping the first position`\r\n );\r\n return;\r\n }\r\n placed.add(id);\r\n\r\n node.position({\r\n x: startX + (colIndex + base) * offsetX,\r\n y: startY + (rowIndex + base) * offsetY,\r\n });\r\n });\r\n });\r\n}\r\n\r\n/**\r\n * Generates a unique ID by appending a counter suffix if needed.\r\n * @param {string} base - Base ID string\r\n * @param {Set<string>} taken - Set of already-used IDs (the new ID is added to it)\r\n * @returns {string} Unique ID\r\n */\r\nexport function uniqueId(base: string, taken: Set<string>): string {\r\n let id = base;\r\n let n = 2;\r\n while (taken.has(id)) {\r\n id = `${base}_${n++}`;\r\n }\r\n taken.add(id);\r\n return id;\r\n}\r\n\r\n/**\r\n * Builds a layout from the groups, used when no layout is given.\r\n * Each group starts a new row and fills rows of at most `columns` reports\r\n * (group order, then report order); reports in no group come last.\r\n * Copies of a report in several groups (\"id(1)\", ...) sit in their own group.\r\n * @param {Object[]} elements - Cytoscape elements from buildGraph\r\n * @param {number} [columns] - Maximum reports per row\r\n * @returns {string[][]} Layout rows of node IDs\r\n */\r\nexport function layoutFromFolders(elements: GraphElement[], columns = DEFAULT_LAYOUT_COLUMNS): Layout {\r\n const perRow = Math.max(1, Math.floor(columns) || 1);\r\n const reports = elements.filter((e) => e.data.class === REPORT_CLASS);\r\n const groups = elements\r\n .filter((e) => e.data.class === FOLDER_CLASS)\r\n .map((folder) => reports.filter((r) => r.data.parent === folder.data.id));\r\n groups.push(reports.filter((r) => !r.data.parent));\r\n\r\n const rows: Layout = [];\r\n for (const group of groups) {\r\n for (let i = 0; i < group.length; i += perRow) {\r\n rows.push(group.slice(i, i + perRow).map((r) => r.data.id));\r\n }\r\n }\r\n return rows;\r\n}\r\n\r\n/**\r\n * Builds the Cytoscape elements from groups, edges and extra node IDs.\r\n * Folders become compound nodes (FOLDER_CLASS); every report becomes a\r\n * REPORT_CLASS node. A report listed in several groups gets one copy per\r\n * group (\"id\", \"id(1)\", ...); edges connect to the first copy.\r\n * @param {Object<string, string[]>} node_groups - Mapping of group names to child IDs\r\n * @param {Array<[string, string]>} direct_node_connections - Array of [source, target] edge pairs\r\n * @param {string[]} extraIds - Other node IDs to create even without edges (e.g. from the layout)\r\n * @returns {Object[]} Cytoscape elements array\r\n */\r\nexport function buildGraph(\r\n node_groups: NodeGroups = {},\r\n direct_node_connections: Connections = [],\r\n extraIds: string[] = []\r\n): GraphElement[] {\r\n const elements: GraphElement[] = [];\r\n const takenIds = new Set<string>();\r\n\r\n // IDs of reports that are not necessarily in a group\r\n const looseIds = new Set([...direct_node_connections.flat(), ...extraIds]);\r\n for (const children of Object.values(node_groups)) {\r\n for (const id of children) {\r\n takenIds.add(id);\r\n }\r\n }\r\n for (const id of looseIds) {\r\n takenIds.add(id);\r\n }\r\n\r\n const makeFolderId = (label: string): string => {\r\n const cleaned = label.replace(/\\s+/g, '');\r\n if (!cleaned) {\r\n return 'folder';\r\n }\r\n return cleaned.charAt(0).toLowerCase() + cleaned.slice(1);\r\n };\r\n\r\n // originalId -> [{ actualId, folderLabel }, ...] (one entry per group the report appears in)\r\n const instances = new Map<string, { actualId: string; folderLabel: string }[]>();\r\n const folderIdByLabel = new Map<string, string>();\r\n\r\n for (const [folderLabel, children] of Object.entries(node_groups)) {\r\n const folderId = uniqueId(makeFolderId(folderLabel), takenIds);\r\n folderIdByLabel.set(folderLabel, folderId);\r\n elements.push({ data: { id: folderId, class: FOLDER_CLASS, label: folderLabel } });\r\n\r\n for (const childId of children) {\r\n const existing = instances.get(childId) ?? [];\r\n let actualId = childId;\r\n if (existing.length > 0) {\r\n let n = existing.length;\r\n actualId = `${childId}(${n})`;\r\n while (takenIds.has(actualId)) {\r\n actualId = `${childId}(${++n})`;\r\n }\r\n takenIds.add(actualId);\r\n }\r\n existing.push({ actualId, folderLabel });\r\n instances.set(childId, existing);\r\n }\r\n }\r\n\r\n const reports: { actualId: string; originalId: string; folderLabel: string | null }[] = [];\r\n for (const [originalId, copies] of instances) {\r\n for (const { actualId, folderLabel } of copies) {\r\n reports.push({ actualId, originalId, folderLabel });\r\n }\r\n }\r\n for (const id of looseIds) {\r\n if (!instances.has(id)) {\r\n reports.push({ actualId: id, originalId: id, folderLabel: null });\r\n }\r\n }\r\n\r\n for (const { actualId, originalId, folderLabel } of reports) {\r\n const data: ElementData = {\r\n id: actualId,\r\n class: REPORT_CLASS,\r\n rawLabel: originalId, // report name\r\n displayLabel: folderLabel ? `${originalId} (${folderLabel})` : originalId,\r\n group: folderLabel,\r\n };\r\n if (folderLabel) {\r\n data.parent = folderIdByLabel.get(folderLabel);\r\n }\r\n elements.push({ data });\r\n }\r\n\r\n // [a,b] + [b,a] become one edge with arrows at both ends\r\n const resolve = (id: string): string => instances.get(id)?.[0]?.actualId ?? id;\r\n const pairKey = (source: string, target: string): string => `${source}->${target}`;\r\n const edgeByPair = new Map<string, GraphElement>();\r\n\r\n direct_node_connections.forEach(([rawSource, rawTarget], index) => {\r\n const source = resolve(rawSource);\r\n const target = resolve(rawTarget);\r\n\r\n // Same edge listed twice: skip it\r\n if (edgeByPair.has(pairKey(source, target))) {\r\n return;\r\n }\r\n\r\n // Reverse edge already added: give it two arrows instead of adding a second line\r\n const reverse = edgeByPair.get(pairKey(target, source));\r\n if (reverse) {\r\n reverse.classes = 'bidirectional';\r\n return;\r\n }\r\n\r\n const edge: GraphElement = { data: { id: uniqueId(`e${index + 1}`, takenIds), source, target } };\r\n edgeByPair.set(pairKey(source, target), edge);\r\n elements.push(edge);\r\n });\r\n\r\n return elements;\r\n}\r\n\r\n/**\r\n * Elements to draw for the current toggles: node label with or without\r\n * the group, and groups shown or hidden.\r\n * @param {Object[]} allElements - Output of buildGraph\r\n * @param {boolean} showFolders - Keep the group (compartment) nodes\r\n * @param {boolean} showGroupInLabel - Label reports \"id (Group)\" instead of \"id\"\r\n * @returns {Object[]} Fresh element copies, safe to hand to a new Cytoscape instance\r\n */\r\nexport function getVisibleElements(\r\n allElements: GraphElement[],\r\n showFolders: boolean,\r\n showGroupInLabel: boolean\r\n): GraphElement[] {\r\n return allElements\r\n .filter((elem) => showFolders || elem.data.class !== FOLDER_CLASS)\r\n .map((elem) => {\r\n // Fresh copies each time, so one Cytoscape instance never shares objects with the next\r\n if (elem.data.class !== REPORT_CLASS) {\r\n return { ...elem, data: { ...elem.data } };\r\n }\r\n const graphLabel = showGroupInLabel ? elem.data.displayLabel : elem.data.rawLabel;\r\n return { ...elem, data: { ...elem.data, graphLabel } };\r\n });\r\n}\r\n\r\n/**\r\n * Collects the details shown in the side panel for a report node.\r\n * Parents point to the node, children are pointed to by it;\r\n * a two-way edge makes the other node both a parent and a child.\r\n * With allLevels, the lists hold the whole chains instead: the parents of the parents, ...\r\n * and the children of the children, ... (the node itself is left out).\r\n * @param {cytoscape.NodeSingular} node - The double-clicked node\r\n * @param {boolean} [allLevels] - Every level of parents and children, not just the first\r\n * @returns {{id: string, name: string, group: string|null, parents: Object[], children: Object[]}}\r\n */\r\nexport function getNodeInfo(node: cytoscape.NodeSingular, allLevels = false): NodeInfo {\r\n const describe = (n: cytoscape.NodeSingular): NodeRef => ({\r\n id: n.id(),\r\n name: (n.data('rawLabel') as string | undefined) ?? n.id(),\r\n group: (n.data('group') as string | null | undefined) ?? null,\r\n });\r\n const parents = new Map<string, NodeRef>();\r\n const children = new Map<string, NodeRef>();\r\n\r\n node.connectedEdges().forEach((edge) => {\r\n const twoWay = edge.hasClass('bidirectional');\r\n if (edge.target().same(node)) {\r\n parents.set(edge.source().id(), describe(edge.source()));\r\n if (twoWay) {\r\n children.set(edge.source().id(), describe(edge.source()));\r\n }\r\n }\r\n if (edge.source().same(node)) {\r\n children.set(edge.target().id(), describe(edge.target()));\r\n if (twoWay) {\r\n parents.set(edge.target().id(), describe(edge.target()));\r\n }\r\n }\r\n });\r\n\r\n if (allLevels) {\r\n const chains: [Map<string, NodeRef>, 'parents' | 'children'][] = [\r\n [parents, 'parents'],\r\n [children, 'children'],\r\n ];\r\n for (const [map, direction] of chains) {\r\n map.clear();\r\n getTrackedElements(node, direction)\r\n .nodes()\r\n .forEach((n) => {\r\n if (!n.same(node)) {\r\n map.set(n.id(), describe(n));\r\n }\r\n });\r\n }\r\n }\r\n\r\n const byName = (a: NodeRef, b: NodeRef): number => compareNames(a.name, b.name);\r\n return {\r\n ...describe(node),\r\n parents: [...parents.values()].sort(byName),\r\n children: [...children.values()].sort(byName),\r\n };\r\n}\r\n\r\n/**\r\n * Follows the edges from a node to collect its whole chain of parents\r\n * (nodes pointing to it, their parents, ...) or children. A two-way edge\r\n * counts in both directions, as in getNodeInfo.\r\n * @param {cytoscape.NodeSingular} start - The tracked node\r\n * @param {'parents'|'children'} direction - Which way to follow the edges\r\n * @returns {cytoscape.Collection} The start node plus every node and edge on the chain\r\n */\r\nexport function getTrackedElements(\r\n start: cytoscape.NodeSingular,\r\n direction: 'parents' | 'children'\r\n): cytoscape.Collection {\r\n const upstream = direction === 'parents';\r\n let trail = start.cy().collection();\r\n const seen = new Set([start.id()]);\r\n const queue: cytoscape.NodeSingular[] = [start];\r\n\r\n for (let node = queue.shift(); node !== undefined; node = queue.shift()) {\r\n const current = node;\r\n current.connectedEdges().forEach((edge) => {\r\n const twoWay = edge.hasClass('bidirectional');\r\n let next: cytoscape.NodeSingular | null = null;\r\n if (edge.target().same(current) && (upstream || twoWay)) {\r\n next = edge.source();\r\n }\r\n if (edge.source().same(current) && (!upstream || twoWay)) {\r\n next = edge.target();\r\n }\r\n if (!next) {\r\n return;\r\n }\r\n trail = trail.union(edge);\r\n if (!seen.has(next.id())) {\r\n seen.add(next.id());\r\n queue.push(next);\r\n }\r\n });\r\n }\r\n\r\n return trail.union(\r\n start\r\n .cy()\r\n .nodes()\r\n .filter((n) => seen.has(n.id()))\r\n );\r\n}\r\n\r\n/**\r\n * The parent/child lists shown in the side panel and copied to the clipboard;\r\n * empty lists are left out.\r\n * @param {ReturnType<typeof getNodeInfo>} info - Output of getNodeInfo\r\n * @returns {Array<[string, Object[]]>} [title, list] pairs\r\n */\r\nfunction infoSections(info: NodeInfo): [string, NodeRef[]][] {\r\n const sections: [string, NodeRef[]][] = [\r\n ['Parent Nodes', info.parents],\r\n ['Child Nodes', info.children],\r\n ];\r\n return sections.filter(([, list]) => list.length > 0);\r\n}\r\n\r\n/**\r\n * The elements to hide for the reports unchecked in \"Node to Display\": those reports\r\n * (Cytoscape then hides their edges too) and the groups left with no visible report.\r\n * @param {cytoscape.Core} cy - Cytoscape instance\r\n * @param {Set<string>} hiddenIds - IDs of the reports to hide\r\n * @returns {cytoscape.Collection} Elements to hide\r\n */\r\nexport function getHiddenElements(cy: cytoscape.Core, hiddenIds: Set<string>): cytoscape.Collection {\r\n const reports = cy.nodes(REPORT_NODE).filter((n) => hiddenIds.has(n.id()));\r\n const emptyFolders = cy\r\n .nodes(':parent')\r\n .filter((folder) => folder.children().every((child) => reports.has(child)));\r\n return reports.union(emptyFolders);\r\n}\r\n\r\n/**\r\n * Formats node details as plain text for the clipboard, matching the side panel:\r\n * the group and the parent/child lists appear only when not empty.\r\n * @param {ReturnType<typeof getNodeInfo>} info - Output of getNodeInfo\r\n * @returns {string} Text to copy\r\n */\r\nexport function formatNodeInfo(info: NodeInfo): string {\r\n const describe = (item: NodeRef): string => (item.group ? `${item.name} (${item.group})` : item.name);\r\n const lines = [`Node: ${info.name}`];\r\n if (info.group) {\r\n lines.push(`Group: ${info.group}`);\r\n }\r\n for (const [title, list] of infoSections(info)) {\r\n lines.push('', `${title} (${list.length}):`, ...list.map(describe));\r\n }\r\n return lines.join('\\n');\r\n}\r\n\r\n/**\r\n * Reads a \"#rrggbb\" or \"rgb(r, g, b)\" color as one number (0xrrggbb).\r\n * @param {string} color - Hex or rgb() color\r\n * @returns {number|null} The color, or null for any other format\r\n */\r\nfunction parseColor(color: string): number | null {\r\n const hex = /^#([0-9a-f]{6})$/i.exec(color)?.[1];\r\n if (hex !== undefined) {\r\n return parseInt(hex, 16);\r\n }\r\n const rgb = /^rgb\\(\\s*(\\d{1,3})\\s*,\\s*(\\d{1,3})\\s*,\\s*(\\d{1,3})\\s*\\)$/i.exec(color);\r\n if (rgb === null) {\r\n return null;\r\n }\r\n const [red = 0, green = 0, blue = 0] = rgb.slice(1).map((part) => Math.min(Number(part), 255));\r\n return (red << 16) | (green << 8) | blue;\r\n}\r\n\r\n/**\r\n * Mixes a \"#rrggbb\" or \"rgb(r, g, b)\" color with white (used as the tracked node's fill).\r\n * Other color formats fall back to white.\r\n * @param {string} color - Hex or rgb() color\r\n * @param {number} [amount=0.88] - Share of white, 0..1\r\n * @returns {string} Hex color\r\n */\r\nexport function lightTint(color: string, amount = 0.88): string {\r\n const value = parseColor(color);\r\n if (value === null) {\r\n return '#ffffff';\r\n }\r\n return `#${[16, 8, 0]\r\n .map((shift) => {\r\n const channel = (value >> shift) & 0xff;\r\n return Math.round(channel + (255 - channel) * amount)\r\n .toString(16)\r\n .padStart(2, '0');\r\n })\r\n .join('')}`;\r\n}\r\n\r\n/**\r\n * Highlights the tracked chains (Track node 1 and 2) and dims the rest.\r\n * Elements on one chain get that track's class (track-parents, track2-children, ...);\r\n * elements on both chains also get \"track-mixed\" with the common color in\r\n * data(trackMixColor) and, for the tracked nodes, its light tint in data(trackMixFill).\r\n * @param {cytoscape.Core} cy - Cytoscape instance\r\n * @param {{id: string, direction: 'parents'|'children'}[]} tracks - Track 1 and Track 2 ('' id = off)\r\n * @param {string} [commonColor] - Hex color of elements on both chains\r\n */\r\nexport interface Track {\r\n id: string;\r\n direction: 'parents' | 'children';\r\n}\r\n\r\nexport function applyTracking(\r\n cy: cytoscape.Core,\r\n tracks: Track[],\r\n commonColor: string = DEFAULT_TRACK_COMMON_COLOR\r\n): void {\r\n const PREFIXES = ['track', 'track2'] as const;\r\n const allClasses = PREFIXES.flatMap((p) => [`${p}-parents`, `${p}-children`, `${p}-root`])\r\n .concat('faded', 'track-mixed')\r\n .join(' ');\r\n\r\n cy.batch(() => {\r\n cy.elements().removeClass(allClasses).removeData('trackMixColor trackMixFill');\r\n\r\n const trails = tracks.map(({ id, direction }, i): cytoscape.Collection | null => {\r\n if (!id) {\r\n return null;\r\n }\r\n const root = cy.getElementById(id);\r\n if (root.empty()) {\r\n return null;\r\n }\r\n const trail = getTrackedElements(root, direction);\r\n const prefix = PREFIXES[i] ?? 'track';\r\n trail.addClass(`${prefix}-${direction}`);\r\n root.addClass(`${prefix}-root`);\r\n return trail;\r\n });\r\n\r\n const active = trails.filter((trail): trail is cytoscape.Collection => trail !== null);\r\n if (active.length === 0) {\r\n return;\r\n }\r\n\r\n const [first, second] = trails;\r\n if (first && second) {\r\n first.intersection(second).forEach((ele) => {\r\n ele.data({ trackMixColor: commonColor, trackMixFill: lightTint(commonColor) });\r\n ele.addClass('track-mixed');\r\n });\r\n }\r\n\r\n // Folders stay visible; only reports and edges off every chain are dimmed\r\n const onChains = active.reduce((all, trail) => all.union(trail));\r\n cy.elements().not(onChains).not('node:parent').addClass('faded');\r\n });\r\n}\r\n\r\n/**\r\n * Cytoscape stylesheet; later rules override earlier ones.\r\n * @param {{selectedColor?: string, parentsColor?: string, childrenColor?: string,\r\n * track2ParentsColor?: string, track2ChildrenColor?: string}} [colors]\r\n * Selected node color and Track node 1 / Track node 2 chain colors\r\n * @returns {Object[]} Stylesheet\r\n */\r\nexport interface StyleColors {\r\n selectedColor?: string | undefined;\r\n parentsColor?: string | undefined;\r\n childrenColor?: string | undefined;\r\n track2ParentsColor?: string | undefined;\r\n track2ChildrenColor?: string | undefined;\r\n}\r\n\r\nexport function makeStyles({\r\n selectedColor = DEFAULT_SELECTED_COLOR,\r\n parentsColor = DEFAULT_TRACK1_PARENTS_COLOR,\r\n childrenColor = DEFAULT_TRACK1_CHILDREN_COLOR,\r\n track2ParentsColor = DEFAULT_TRACK2_PARENTS_COLOR,\r\n track2ChildrenColor = DEFAULT_TRACK2_CHILDREN_COLOR,\r\n}: StyleColors = {}): StylesheetJson {\r\n return [\r\n {\r\n selector: 'node',\r\n style: {\r\n 'background-color': REPORT_BACKGROUND_COLOR,\r\n label: 'data(graphLabel)',\r\n shape: 'rectangle',\r\n // The box size comes from the label (set in the graph text effect); groups size to their children\r\n 'font-family': LABEL_FONT_FAMILY,\r\n 'font-size': REPORT_FONT_SIZE,\r\n color: GRAPH_COLOR,\r\n 'border-width': 2,\r\n 'border-color': GRAPH_COLOR,\r\n 'text-valign': 'center',\r\n 'text-halign': 'center',\r\n },\r\n },\r\n {\r\n selector: 'node:parent',\r\n style: {\r\n 'background-color': FOLDER_BACKGROUND_COLOR,\r\n 'background-opacity': 0.5,\r\n 'border-style': 'dashed',\r\n label: 'data(label)',\r\n 'text-valign': 'top',\r\n 'font-size': FOLDER_FONT_SIZE,\r\n 'font-weight': 'bold',\r\n padding: '18px',\r\n },\r\n },\r\n {\r\n selector: '.selected',\r\n style: {\r\n 'overlay-color': selectedColor,\r\n 'overlay-opacity': 0.35,\r\n 'overlay-padding': 8,\r\n },\r\n },\r\n {\r\n selector: 'edge',\r\n style: {\r\n 'line-color': GRAPH_COLOR,\r\n width: 2,\r\n 'curve-style': 'bezier',\r\n 'target-arrow-shape': 'vee',\r\n 'target-arrow-color': GRAPH_COLOR,\r\n 'target-arrow-fill': 'filled',\r\n 'arrow-scale': 2,\r\n },\r\n },\r\n {\r\n // Self connections ([a, a]): draw the loop above the box, large enough\r\n // to stick out of it (the default loop is hidden inside wide nodes)\r\n selector: 'edge:loop',\r\n style: {\r\n 'loop-direction': '0deg',\r\n 'loop-sweep': '-50deg',\r\n 'control-point-step-size': 70,\r\n },\r\n },\r\n {\r\n selector: 'edge.bidirectional',\r\n style: {\r\n 'source-arrow-shape': 'vee',\r\n 'source-arrow-color': GRAPH_COLOR,\r\n 'source-arrow-fill': 'filled',\r\n },\r\n },\r\n {\r\n // Track node: everything off the traced chain is dimmed\r\n selector: '.faded',\r\n style: { opacity: 0.15 },\r\n },\r\n ...(\r\n [\r\n ['track-parents', 'track-root', parentsColor],\r\n ['track-children', 'track-root', childrenColor],\r\n ['track2-parents', 'track2-root', track2ParentsColor],\r\n ['track2-children', 'track2-root', track2ChildrenColor],\r\n ] as const\r\n ).flatMap(([cls, root, color]) => [\r\n {\r\n selector: `edge.${cls}`,\r\n style: {\r\n 'line-color': color,\r\n 'target-arrow-color': color,\r\n 'source-arrow-color': color,\r\n width: 4,\r\n },\r\n },\r\n {\r\n selector: `node.${cls}`,\r\n style: { 'border-color': color, 'border-width': 3 },\r\n },\r\n {\r\n selector: `node.${cls}.${root}`,\r\n style: { 'border-width': 5, 'background-color': lightTint(color) },\r\n },\r\n ]),\r\n // On both tracked chains: the common color (set per element by applyTracking)\r\n {\r\n selector: 'edge.track-mixed',\r\n style: {\r\n 'line-color': 'data(trackMixColor)',\r\n 'target-arrow-color': 'data(trackMixColor)',\r\n 'source-arrow-color': 'data(trackMixColor)',\r\n width: 5,\r\n },\r\n },\r\n {\r\n selector: 'node.track-mixed',\r\n style: { 'border-color': 'data(trackMixColor)', 'border-width': 4 },\r\n },\r\n {\r\n selector: 'node.track-mixed.track-root, node.track-mixed.track2-root',\r\n style: { 'border-width': 6, 'background-color': 'data(trackMixFill)' },\r\n },\r\n {\r\n // Unchecked in \"Node to Display\": not drawn (nor are its edges)\r\n selector: '.hidden',\r\n style: { display: 'none' },\r\n },\r\n ];\r\n}\r\n\r\n// Default label of the first entry of the node search list (selects no node)\r\nconst NO_NODE_LABEL = 'ALL';\r\n\r\n/**\r\n * Searchable node picker: typing any part of a name (case-insensitive) filters the list.\r\n * Arrow keys move through the matches, Enter picks one, Escape closes the list.\r\n * @param {string} [id] - id for the input (to link a <label>)\r\n * @param {{id: string, label: string}[]} options - Nodes to choose from\r\n * @param {string} selectedId - Currently selected node id ('' for none)\r\n * @param {(id: string) => void} onSelect - Called with the chosen node id\r\n * @param {string} [noneLabel] - Label of the first entry, which selects no node\r\n * @param {Set<string>} [hiddenIds] - With onToggleVisible: nodes whose checkbox is off\r\n * @param {(id: string) => void} [onToggleVisible] - When given, each node gets a checkbox\r\n * (on = shown); called with the node id when it is clicked\r\n */\r\nexport interface NodeSearchProps {\r\n id?: string;\r\n options: { id: string; label: string }[];\r\n selectedId: string;\r\n onSelect: (id: string) => void;\r\n noneLabel?: string;\r\n hiddenIds?: Set<string>;\r\n onToggleVisible?: (id: string) => void;\r\n}\r\n\r\nexport function NodeSearch({\r\n id,\r\n options,\r\n selectedId,\r\n onSelect,\r\n noneLabel = NO_NODE_LABEL,\r\n hiddenIds,\r\n onToggleVisible,\r\n}: NodeSearchProps): React.JSX.Element {\r\n const [query, setQuery] = useState(''); // text shown in the box\r\n const [filter, setFilter] = useState(''); // text typed since the list opened\r\n const [open, setOpen] = useState(false);\r\n const [active, setActive] = useState(0);\r\n const boxRef = useRef<HTMLDivElement>(null);\r\n const listRef = useRef<HTMLUListElement>(null);\r\n\r\n const selectedLabel = options.find((o) => o.id === selectedId)?.label ?? '';\r\n\r\n // Show the selected node's name (also when it is picked elsewhere, e.g. the side panel)\r\n const [shownLabel, setShownLabel] = useState<string | null>(null); // null: not synced yet\r\n if (shownLabel !== selectedLabel) {\r\n setShownLabel(selectedLabel);\r\n setQuery(selectedLabel);\r\n }\r\n\r\n const needle = filter.trim().toLowerCase();\r\n const nodeMatches = needle ? options.filter((o) => o.label.toLowerCase().includes(needle)) : options;\r\n // The \"no node\" entry (ALL by default) sits at the top of the list\r\n const noneOption: { id: string; label: string } = { id: '', label: noneLabel };\r\n const matches =\r\n !needle || noneLabel.toLowerCase().includes(needle) ? [noneOption, ...nodeMatches] : nodeMatches;\r\n\r\n const openList = () => {\r\n setFilter('');\r\n setActive(0);\r\n setOpen(true);\r\n };\r\n\r\n const close = () => {\r\n setOpen(false);\r\n setFilter('');\r\n setQuery(selectedLabel);\r\n };\r\n\r\n const choose = (opt: { id: string; label: string }): void => {\r\n onSelect(opt.id);\r\n setQuery(opt.id ? opt.label : ''); // the \"no node\" entry leaves the box empty\r\n setFilter('');\r\n setOpen(false);\r\n };\r\n\r\n // Close the list when clicking outside the search box\r\n useEffect(() => {\r\n if (!open) {\r\n return undefined;\r\n }\r\n const onMouseDown = (e: MouseEvent): void => {\r\n if (!boxRef.current?.contains(e.target as Node)) {\r\n close();\r\n }\r\n };\r\n document.addEventListener('mousedown', onMouseDown);\r\n return () => document.removeEventListener('mousedown', onMouseDown);\r\n });\r\n\r\n // Keep the highlighted match visible while using the arrow keys\r\n useEffect(() => {\r\n listRef.current?.children[active]?.scrollIntoView({ block: 'nearest' });\r\n }, [active, open]);\r\n\r\n const onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>): void => {\r\n if (e.key === 'ArrowDown') {\r\n e.preventDefault();\r\n if (!open) {\r\n setOpen(true);\r\n }\r\n setActive((i) => Math.min(i + 1, matches.length - 1));\r\n } else if (e.key === 'ArrowUp') {\r\n e.preventDefault();\r\n setActive((i) => Math.max(i - 1, 0));\r\n } else if (e.key === 'Enter' && open) {\r\n const chosen = matches[active];\r\n if (chosen) {\r\n e.preventDefault();\r\n choose(chosen);\r\n }\r\n } else if (e.key === 'Escape') {\r\n close();\r\n }\r\n };\r\n\r\n return (\r\n <div className=\"node-search\" ref={boxRef}>\r\n <input\r\n id={id}\r\n type=\"text\"\r\n value={query}\r\n placeholder=\"-- type to search --\"\r\n autoComplete=\"off\"\r\n onChange={(e) => {\r\n setQuery(e.target.value);\r\n setFilter(e.target.value);\r\n setActive(0);\r\n setOpen(true);\r\n }}\r\n onFocus={(e) => {\r\n e.target.select();\r\n openList();\r\n }}\r\n onClick={() => {\r\n // Reopen the list when clicking the box again after choosing\r\n if (!open) {\r\n openList();\r\n }\r\n }}\r\n onKeyDown={onKeyDown}\r\n />\r\n {query && (\r\n <button\r\n type=\"button\"\r\n className=\"node-search-clear\"\r\n aria-label=\"Clear\"\r\n onClick={() => {\r\n onSelect('');\r\n setQuery('');\r\n setFilter('');\r\n }}\r\n >\r\n ×\r\n </button>\r\n )}\r\n {open && (\r\n <ul className=\"node-search-list\" ref={listRef} role=\"listbox\">\r\n {matches.length === 0 ? (\r\n <li className=\"node-search-empty\" role=\"option\" aria-selected=\"false\" aria-disabled=\"true\">\r\n no match\r\n </li>\r\n ) : (\r\n matches.map((opt, i) => (\r\n <li\r\n key={opt.id || '__none__'}\r\n role=\"option\"\r\n aria-selected={i === active}\r\n className={\r\n [i === active && 'active', hiddenIds?.has(opt.id) && 'off'].filter(Boolean).join(' ') ||\r\n undefined\r\n }\r\n onMouseDown={(e) => {\r\n e.preventDefault(); // keep focus in the input\r\n choose(opt);\r\n }}\r\n onMouseEnter={() => setActive(i)}\r\n >\r\n {onToggleVisible && opt.id && (\r\n <input\r\n type=\"checkbox\"\r\n checked={!hiddenIds?.has(opt.id)}\r\n aria-label={`Show ${opt.label}`}\r\n // Toggles the node only: does not pick it, and keeps the list open\r\n onMouseDown={(e) => {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n }}\r\n onClick={(e) => e.stopPropagation()}\r\n onChange={() => onToggleVisible(opt.id)}\r\n />\r\n )}\r\n {opt.label}\r\n </li>\r\n ))\r\n )}\r\n </ul>\r\n )}\r\n </div>\r\n );\r\n}\r\n\r\n/** Options of create_graph: grid placement, colors (\"#rrggbb\" or \"rgb(r, g, b)\") and text sizes (CSS lengths) */\r\nexport interface GraphOptions extends LayoutOptions {\r\n /** Reports per row when the layout is built from the groups */\r\n layoutColumns?: number | undefined;\r\n selectedColor?: string | undefined;\r\n parentsColor?: string | undefined;\r\n childrenColor?: string | undefined;\r\n track2ParentsColor?: string | undefined;\r\n track2ChildrenColor?: string | undefined;\r\n commonColor?: string | undefined;\r\n topControlsFontSize?: string | undefined;\r\n rightDisplayFontSize?: string | undefined;\r\n}\r\n\r\n/**\r\n * Factory function that creates a React Graph component.\r\n * Initializes Cytoscape instance, manages graph state, and handles node selection.\r\n * @param {Object<string, string[]>} node_groups - Group (group box) names mapped to their report names\r\n * @param {Array<[string, string]>} direct_node_connections - Edge definitions ([source, target] pairs)\r\n * @param {string[][]|Object} [layoutOrOptions] - Layout grid (one row per layer). Optional: leave it out\r\n * and pass the options in its place, e.g. create_graph(node_groups, direct_node_connections, options),\r\n * to build the layout from the groups\r\n * @param {Object} [optionsArg] - Layout rendering options (startX, startY, offsetX, offsetY, base,\r\n * layoutColumns = reports per row when the layout is built from the groups)\r\n * plus the colors (\"#rrggbb\"): selectedColor (Node to Display), parentsColor and\r\n * childrenColor (Track node 1), track2ParentsColor and track2ChildrenColor (Track node 2;\r\n * commonColor = elements on both chains, default rgb(239, 108, 0)), topControlsFontSize (text size of the top controls row, e.g. '16px')\r\n * and rightDisplayFontSize (text size of the right details panel)\r\n * @returns {React.FC} Graph component\r\n */\r\nexport function create_graph(\r\n node_groups: NodeGroups = {},\r\n direct_node_connections: Connections = [],\r\n layoutOrOptions: Layout | GraphOptions | null = null,\r\n optionsArg: GraphOptions = {}\r\n): FC {\r\n // The layout can be left out: create_graph(node_groups, direct_node_connections, options)\r\n const layout: Layout | null = Array.isArray(layoutOrOptions) ? layoutOrOptions : null;\r\n const options: GraphOptions =\r\n layoutOrOptions !== null && !Array.isArray(layoutOrOptions) ? layoutOrOptions : optionsArg;\r\n const hasLayout = layout !== null && layout.length > 0;\r\n const {\r\n selectedColor = DEFAULT_SELECTED_COLOR,\r\n parentsColor = DEFAULT_TRACK1_PARENTS_COLOR,\r\n childrenColor = DEFAULT_TRACK1_CHILDREN_COLOR,\r\n track2ParentsColor = DEFAULT_TRACK2_PARENTS_COLOR,\r\n track2ChildrenColor = DEFAULT_TRACK2_CHILDREN_COLOR,\r\n commonColor = DEFAULT_TRACK_COMMON_COLOR,\r\n topControlsFontSize = DEFAULT_TOP_CONTROLS_FONT_SIZE,\r\n rightDisplayFontSize = DEFAULT_RIGHT_DISPLAY_FONT_SIZE,\r\n layoutColumns = DEFAULT_LAYOUT_COLUMNS,\r\n } = options;\r\n const GRAPH_STYLES = makeStyles({\r\n selectedColor,\r\n parentsColor,\r\n childrenColor,\r\n track2ParentsColor,\r\n track2ChildrenColor,\r\n });\r\n // Colors for react-beauti-graph.css, set as CSS variables (the text sizes are added in the component)\r\n const CSS_VARS = {\r\n '--rbg-parents-color': parentsColor,\r\n '--rbg-children-color': childrenColor,\r\n };\r\n // The Track node 2 switch reuses the same CSS with its own colors\r\n const TRACK2_CSS_VARS = {\r\n '--rbg-parents-color': track2ParentsColor,\r\n '--rbg-children-color': track2ChildrenColor,\r\n };\r\n // Nodes listed only in the layout (no group, no edges) are still shown\r\n const ALL_ELEMENTS = buildGraph(\r\n node_groups,\r\n direct_node_connections,\r\n layout !== null && hasLayout ? layout.flat() : []\r\n );\r\n // No layout given: one block of rows per group\r\n const LAYOUT =\r\n layout !== null && hasLayout ? uniquifyLayout(layout) : layoutFromFolders(ALL_ELEMENTS, layoutColumns);\r\n\r\n function Graph(): React.JSX.Element {\r\n const containerRef = useRef<HTMLDivElement>(null);\r\n const cyRef = useRef<cytoscape.Core | null>(null);\r\n const uid = useId(); // unique ids, so several graphs can share a page\r\n const [nodeOptions, setNodeOptions] = useState<{ id: string; label: string }[]>([]);\r\n const [selectedId, setSelectedId] = useState('');\r\n const [fitRequest, setFitRequest] = useState(0); // bumped to zoom out to the full graph\r\n const [trackedId, setTrackedId] = useState(''); // '' = Show Nothing\r\n const [trackDirection, setTrackDirection] = useState<Track['direction']>('parents');\r\n const [tracked2Id, setTracked2Id] = useState(''); // Track node 2, '' = Show Nothing\r\n const [track2Direction, setTrack2Direction] = useState<Track['direction']>('parents');\r\n const [showFolders, setShowFolders] = useState(true);\r\n const [showGroupInLabel, setShowGroupInLabel] = useState(true);\r\n const [contextMenu, setContextMenu] = useState<{ x: number; y: number; copyText: string } | null>(null);\r\n const [nodeInfo, setNodeInfo] = useState<NodeInfo | null>(null); // details panel for a double-clicked node\r\n const [fontArea, setFontArea] = useState<FontArea>('top'); // area the + / - buttons change\r\n const [fontDeltas, setFontDeltas] = useState<Record<FontArea, number>>({ top: 0, right: 0, graph: 0 }); // px added per area\r\n const [allLevels, setAllLevels] = useState(false); // panel: one level (default) or all levels\r\n const allLevelsRef = useRef(false); // read by the Cytoscape event handlers\r\n useEffect(() => {\r\n allLevelsRef.current = allLevels;\r\n }, [allLevels]);\r\n const [hiddenIds, setHiddenIds] = useState<Set<string>>(() => new Set()); // reports unchecked in \"Node to Display\"\r\n\r\n // Initialize Cytoscape graph (rebuilt when a toggle changes)\r\n useEffect(() => {\r\n const containerEl = containerRef.current;\r\n if (!containerEl) {\r\n return undefined;\r\n }\r\n\r\n const cy = cytoscape({\r\n container: containerEl,\r\n elements: getVisibleElements(\r\n ALL_ELEMENTS,\r\n showFolders,\r\n showGroupInLabel\r\n ) as cytoscape.ElementDefinition[],\r\n style: GRAPH_STYLES,\r\n layout: { name: 'preset', fit: false },\r\n });\r\n\r\n // Right click on a report node opens the \"Save to clipboard\" menu\r\n // (copies the same information the side panel shows)\r\n cy.on('cxttap', REPORT_NODE, (evt) => {\r\n const copyText = formatNodeInfo(\r\n getNodeInfo(evt.target as cytoscape.NodeSingular, allLevelsRef.current)\r\n );\r\n const { clientX, clientY } = evt.originalEvent;\r\n setContextMenu({ x: clientX, y: clientY, copyText });\r\n });\r\n\r\n // Double click on a report node shows its details in the side panel\r\n cy.on('dbltap', REPORT_NODE, (evt) => {\r\n setNodeInfo(getNodeInfo(evt.target as cytoscape.NodeSingular, allLevelsRef.current));\r\n });\r\n\r\n // Close the menu on any other interaction with the graph\r\n cy.on('tap cxttap', (evt) => {\r\n if (evt.target === cy) {\r\n setContextMenu(null);\r\n }\r\n });\r\n cy.on('tap', 'node', () => setContextMenu(null));\r\n cy.on('viewport', () => setContextMenu(null));\r\n\r\n // Suppress the browser's native context menu over the graph\r\n const preventNativeMenu = (e: Event): void => e.preventDefault();\r\n containerEl.addEventListener('contextmenu', preventNativeMenu);\r\n\r\n applyLayout(cy, LAYOUT, options);\r\n\r\n requestAnimationFrame(() => cy.resize());\r\n\r\n setNodeOptions(\r\n cy\r\n .nodes(REPORT_NODE)\r\n .map((n) => ({ id: n.id(), label: n.data('displayLabel') as string }))\r\n .sort((a, b) => compareNames(a.label, b.label))\r\n );\r\n\r\n cyRef.current = cy;\r\n\r\n return () => {\r\n containerEl.removeEventListener('contextmenu', preventNativeMenu);\r\n setContextMenu(null);\r\n cy.destroy();\r\n cyRef.current = null;\r\n };\r\n }, [showFolders, showGroupInLabel]);\r\n\r\n // Graph text size, and every report box sized to its label (so the + / - buttons resize both).\r\n // Declared after the effect above, so it also runs when that one rebuilds the graph.\r\n useEffect(() => {\r\n const cy = cyRef.current;\r\n if (!cy) {\r\n return;\r\n }\r\n const delta = fontDeltas.graph;\r\n cy.batch(() => {\r\n cy.nodes(REPORT_NODE).forEach((n) => {\r\n const fontSize = REPORT_FONT_SIZE + delta;\r\n const { width, height } = getNodeSize(n.data('graphLabel') as string | undefined, fontSize);\r\n n.style({ 'font-size': fontSize, width, height });\r\n });\r\n cy.nodes(':parent').style('font-size', FOLDER_FONT_SIZE + delta);\r\n });\r\n }, [fontDeltas.graph, showFolders, showGroupInLabel]);\r\n\r\n // Close the context menu on outside click or Escape\r\n useEffect(() => {\r\n if (!contextMenu) {\r\n return undefined;\r\n }\r\n const onMouseDown = (e: MouseEvent): void => {\r\n if (!(e.target as Element).closest('.node-context-menu')) {\r\n setContextMenu(null);\r\n }\r\n };\r\n const onKeyDown = (e: KeyboardEvent): void => {\r\n if (e.key === 'Escape') {\r\n setContextMenu(null);\r\n }\r\n };\r\n document.addEventListener('mousedown', onMouseDown);\r\n document.addEventListener('keydown', onKeyDown);\r\n return () => {\r\n document.removeEventListener('mousedown', onMouseDown);\r\n document.removeEventListener('keydown', onKeyDown);\r\n };\r\n }, [contextMenu]);\r\n\r\n const copyToClipboard = (): void => {\r\n if (contextMenu === null) {\r\n return;\r\n }\r\n const { copyText } = contextMenu;\r\n setContextMenu(null);\r\n navigator.clipboard.writeText(copyText).catch((err: unknown) => {\r\n console.error('Failed to copy:', err);\r\n });\r\n };\r\n\r\n const exportSvg = (): void => {\r\n if (cyRef.current) {\r\n downloadSvg(cyRef.current);\r\n }\r\n };\r\n\r\n // Checkbox next to a report in \"Node to Display\": show or hide it\r\n const toggleVisible = (id: string): void => {\r\n setHiddenIds((prev) => {\r\n const next = new Set(prev);\r\n if (next.has(id)) {\r\n next.delete(id);\r\n } else {\r\n next.add(id);\r\n }\r\n return next;\r\n });\r\n };\r\n\r\n // Selects a node (search box or side panel): jumps to it and shows its details.\r\n // A hidden node is shown again, so there is something to jump to.\r\n const showNode = (id: string): void => {\r\n const node = cyRef.current?.getElementById(id);\r\n if (!node || node.empty()) {\r\n return;\r\n }\r\n if (hiddenIds.has(id)) {\r\n toggleVisible(id);\r\n }\r\n setSelectedId(id);\r\n setNodeInfo(getNodeInfo(node, allLevels));\r\n };\r\n\r\n // ALL (or clearing the search): no selection, no details panel, whole graph in view\r\n const showAll = () => {\r\n setSelectedId('');\r\n setNodeInfo(null);\r\n setFitRequest((n) => n + 1);\r\n };\r\n\r\n // Hide the reports unchecked in \"Node to Display\".\r\n // Also re-applied when a toggle rebuilds the graph. Runs before the zoom effects\r\n // below, so a hidden node picked in the list is shown before zooming to it.\r\n useEffect(() => {\r\n const cy = cyRef.current;\r\n if (!cy) {\r\n return;\r\n }\r\n cy.batch(() => {\r\n cy.elements().removeClass('hidden');\r\n getHiddenElements(cy, hiddenIds).addClass('hidden');\r\n });\r\n }, [hiddenIds, showFolders, showGroupInLabel]);\r\n\r\n // Zoom out to the full graph. Runs after the re-render, so the closed\r\n // details panel has already given its width back to the graph.\r\n useEffect(() => {\r\n const cy = cyRef.current;\r\n if (!fitRequest || !cy) {\r\n return;\r\n }\r\n cy.stop(); // cancel a running zoom to a node\r\n cy.resize();\r\n cy.animate({ fit: { eles: cy.elements().not('.hidden'), padding: 30 } }, { duration: 400 });\r\n }, [fitRequest]);\r\n\r\n // Highlight the selected node and center on it.\r\n // Also re-applied when a toggle rebuilds the graph, so the selection is kept.\r\n useEffect(() => {\r\n const cy = cyRef.current;\r\n if (!cy) {\r\n return;\r\n }\r\n\r\n cy.nodes().removeClass('selected');\r\n if (!selectedId) {\r\n return;\r\n }\r\n\r\n const node = cy.getElementById(selectedId);\r\n if (node.nonempty()) {\r\n node.addClass('selected');\r\n cy.animate({ center: { eles: node }, zoom: 1.8 }, { duration: 400 });\r\n }\r\n }, [selectedId, showFolders, showGroupInLabel]);\r\n\r\n // Track node 1 and 2: highlight each chain of parents/children (common color where\r\n // they meet), dim the rest. Also re-applied when a toggle rebuilds the graph.\r\n useEffect(() => {\r\n const cy = cyRef.current;\r\n if (!cy) {\r\n return;\r\n }\r\n applyTracking(\r\n cy,\r\n [\r\n { id: trackedId, direction: trackDirection },\r\n { id: tracked2Id, direction: track2Direction },\r\n ],\r\n commonColor\r\n );\r\n }, [trackedId, trackDirection, tracked2Id, track2Direction, showFolders, showGroupInLabel]);\r\n\r\n // Opening/closing the side panel changes the graph width\r\n const panelOpen = nodeInfo !== null;\r\n useEffect(() => {\r\n cyRef.current?.resize();\r\n }, [panelOpen]);\r\n\r\n // Handle window resize\r\n useEffect(() => {\r\n const onResize = () => cyRef.current?.resize();\r\n window.addEventListener('resize', onResize);\r\n return () => window.removeEventListener('resize', onResize);\r\n }, []);\r\n\r\n const TRACK_CONTROLS: {\r\n n: 1 | 2;\r\n id: string;\r\n setId: (id: string) => void;\r\n direction: Track['direction'];\r\n setDirection: React.Dispatch<React.SetStateAction<Track['direction']>>;\r\n style: CSSProperties | undefined;\r\n }[] = [\r\n {\r\n n: 1,\r\n id: trackedId,\r\n setId: setTrackedId,\r\n direction: trackDirection,\r\n setDirection: setTrackDirection,\r\n style: undefined,\r\n },\r\n {\r\n n: 2,\r\n id: tracked2Id,\r\n setId: setTracked2Id,\r\n direction: track2Direction,\r\n setDirection: setTrack2Direction,\r\n style: TRACK2_CSS_VARS as CSSProperties,\r\n },\r\n ];\r\n\r\n const fontButtons: { label: string; symbol: string; step: number; atLimit: boolean }[] = [\r\n {\r\n label: 'Smaller text',\r\n symbol: '−',\r\n step: -FONT_STEP,\r\n atLimit: fontDeltas[fontArea] <= MIN_FONT_DELTA,\r\n },\r\n { label: 'Larger text', symbol: '+', step: FONT_STEP, atLimit: fontDeltas[fontArea] >= MAX_FONT_DELTA },\r\n ];\r\n\r\n return (\r\n <div\r\n className=\"rbg-graph\"\r\n style={\r\n {\r\n ...CSS_VARS,\r\n // The + / - buttons add px to the configured sizes\r\n '--rbg-top-controls-font-size': `calc(${topControlsFontSize} + ${fontDeltas.top}px)`,\r\n '--rbg-right-display-font-size': `calc(${rightDisplayFontSize} + ${fontDeltas.right}px)`,\r\n } as CSSProperties\r\n }\r\n >\r\n <div className=\"controls\">\r\n {/* Each label stays next to its box when the row wraps */}\r\n <span className=\"control-group\">\r\n <label htmlFor={`${uid}-search`}>Node to Display:</label>\r\n <NodeSearch\r\n id={`${uid}-search`}\r\n options={nodeOptions}\r\n selectedId={selectedId}\r\n // Picking a node also opens its details panel, like a double click\r\n onSelect={(id) => (id ? showNode(id) : showAll())}\r\n // Checkbox next to each node: show it in the graph or not\r\n hiddenIds={hiddenIds}\r\n onToggleVisible={toggleVisible}\r\n />\r\n </span>\r\n <label>\r\n <input type=\"checkbox\" checked={showFolders} onChange={(e) => setShowFolders(e.target.checked)} />\r\n Show Groups\r\n </label>\r\n <label>\r\n <input\r\n type=\"checkbox\"\r\n checked={showGroupInLabel}\r\n onChange={(e) => setShowGroupInLabel(e.target.checked)}\r\n />\r\n Add Group to Node Name\r\n </label>\r\n {TRACK_CONTROLS.map(({ n, id, setId, direction, setDirection, style }) => (\r\n <span className=\"control-group\" key={n} style={style}>\r\n <label htmlFor={`${uid}-track${n}`}>Show Dependency {n}:</label>\r\n <NodeSearch\r\n id={`${uid}-track${n}`}\r\n options={nodeOptions}\r\n selectedId={id}\r\n onSelect={setId}\r\n noneLabel=\"Show Nothing\"\r\n />\r\n {/* Nothing to track on \"Show Nothing\", so the switch is disabled */}\r\n <span className={id ? 'track-switch' : 'track-switch disabled'}>\r\n <span className={direction === 'parents' ? 'parents on' : 'parents'}>Parents</span>\r\n <button\r\n type=\"button\"\r\n role=\"switch\"\r\n aria-checked={direction === 'children'}\r\n aria-label={`Show Dependency ${n}: track children instead of parents`}\r\n className=\"switch\"\r\n disabled={!id}\r\n onClick={() => setDirection((d) => (d === 'parents' ? 'children' : 'parents'))}\r\n >\r\n <span className=\"switch-knob\" />\r\n </button>\r\n <span className={direction === 'children' ? 'children on' : 'children'}>Children</span>\r\n </span>\r\n </span>\r\n ))}\r\n <span className=\"help\">\r\n <button type=\"button\" className=\"help-icon\" aria-label=\"Help\" aria-describedby={`${uid}-help`}>\r\n ?\r\n </button>\r\n <span id={`${uid}-help`} role=\"tooltip\" className=\"help-tooltip\">\r\n <strong>Double click</strong> on a node to see its information, parents and children.\r\n <br />\r\n <strong>Right click</strong> on a node to save the same information to the clipboard.\r\n </span>\r\n </span>\r\n <span className=\"font-size-buttons\">\r\n <select\r\n aria-label=\"Area whose text size the + and - buttons change\"\r\n value={fontArea}\r\n onChange={(e) => setFontArea(e.target.value as FontArea)}\r\n >\r\n {FONT_AREAS.map(([area, label]) => (\r\n <option key={area} value={area}>\r\n {label}\r\n </option>\r\n ))}\r\n </select>\r\n {fontButtons.map(({ label, symbol, step, atLimit }) => (\r\n <button\r\n key={label}\r\n type=\"button\"\r\n className=\"help-icon\"\r\n aria-label={label}\r\n disabled={atLimit}\r\n onClick={() => setFontDeltas((d) => ({ ...d, [fontArea]: d[fontArea] + step }))}\r\n >\r\n {symbol}\r\n </button>\r\n ))}\r\n </span>\r\n <button type=\"button\" className=\"export-svg\" onClick={exportSvg}>\r\n Export SVG\r\n </button>\r\n </div>\r\n <div className=\"graph-area\">\r\n <div ref={containerRef} className=\"cy-container\" />\r\n {nodeInfo && (\r\n <aside className=\"node-info-panel\">\r\n <div className=\"node-info-header\">\r\n <h2>\r\n <strong>Node:</strong> {nodeInfo.name}\r\n </h2>\r\n <button type=\"button\" onClick={() => setNodeInfo(null)} aria-label=\"Close\">\r\n ×\r\n </button>\r\n </div>\r\n {nodeInfo.group && (\r\n <p>\r\n <strong>Group:</strong> {nodeInfo.group}\r\n </p>\r\n )}\r\n <div className=\"track-switch level-switch\">\r\n <span className={allLevels ? '' : 'parents on'}>One level</span>\r\n <button\r\n type=\"button\"\r\n role=\"switch\"\r\n aria-checked={allLevels}\r\n aria-label=\"Show all levels of parents and children\"\r\n className=\"switch\"\r\n onClick={() => {\r\n const next = !allLevels;\r\n setAllLevels(next);\r\n const node = cyRef.current?.getElementById(nodeInfo.id);\r\n if (node && !node.empty()) {\r\n setNodeInfo(getNodeInfo(node, next));\r\n }\r\n }}\r\n >\r\n <span className=\"switch-knob\" />\r\n </button>\r\n <span className={allLevels ? 'children on' : ''}>All levels</span>\r\n </div>\r\n {infoSections(nodeInfo).map(([title, list]) => (\r\n <section key={title}>\r\n <h3>\r\n {title} ({list.length}):\r\n </h3>\r\n <ul>\r\n {list.map((item) => (\r\n <li key={item.id}>\r\n <button type=\"button\" onClick={() => showNode(item.id)}>\r\n {item.name}\r\n {item.group && <span> ({item.group})</span>}\r\n </button>\r\n </li>\r\n ))}\r\n </ul>\r\n </section>\r\n ))}\r\n </aside>\r\n )}\r\n </div>\r\n {contextMenu && (\r\n <div\r\n className=\"node-context-menu\"\r\n style={{ left: contextMenu.x, top: contextMenu.y }}\r\n onContextMenu={(e) => e.preventDefault()}\r\n >\r\n <button type=\"button\" onClick={copyToClipboard}>\r\n Save to clipboard\r\n </button>\r\n </div>\r\n )}\r\n </div>\r\n );\r\n }\r\n\r\n Graph.displayName = 'CytoscapeGraph';\r\n return Graph;\r\n}\r\n"],"mappings":"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"}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* react-beauti-graph by Antonio Ramasco
|
|
3
|
+
* Public entry point of the library. Styles: import 'react-beauti-graph/style.css'.
|
|
4
|
+
*/
|
|
5
|
+
export { create_graph, downloadSvg, formatNodeInfo, getNodeInfo, NodeSearch } from './graphFactory.cjs';
|
|
6
|
+
export type { Connections, GraphOptions, Layout, NodeGroups, NodeInfo, NodeRef, NodeSearchProps, } from './graphFactory.cjs';
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* react-beauti-graph by Antonio Ramasco
|
|
3
|
+
* Public entry point of the library. Styles: import 'react-beauti-graph/style.css'.
|
|
4
|
+
*/
|
|
5
|
+
export { create_graph, downloadSvg, formatNodeInfo, getNodeInfo, NodeSearch } from './graphFactory.js';
|
|
6
|
+
export type { Connections, GraphOptions, Layout, NodeGroups, NodeInfo, NodeRef, NodeSearchProps, } from './graphFactory.js';
|