[nad-viewer] Add edge info components in SVG creation from metadata#418
[nad-viewer] Add edge info components in SVG creation from metadata#418massimo-ferraro wants to merge 5 commits into
Conversation
Signed-off-by: massimo.ferraro <massimo.ferraro@soft.it>
| this.svgDraw = SVG().addTo(this.svgDiv).size(this.width, this.height).viewbox(viewBox); | ||
| this.innerSvg = <SVGElement>this.svgDraw.svg(this.svgContent).node.firstElementChild; | ||
| if (this.nadViewerParameters.getCreateSvgFromMetadata()) { | ||
| this.svgWriter?.addSvgContent(<SVGSVGElement>this.innerSvg); |
There was a problem hiding this comment.
| this.svgWriter?.addSvgContent(<SVGSVGElement>this.innerSvg); | |
| if (!this.svgWriter) { | |
| console.warn( | |
| '[NAD] createSvgFromMetadata is true but no SvgWriter was created (diagramMetadata may be null).' | |
| ); | |
| } | |
| this.svgWriter?.addSvgContent(<SVGSVGElement>this.innerSvg); |
Or maybe add the check on the metadata?
There was a problem hiding this comment.
Check on svgWriter added
| } | ||
| } | ||
|
|
||
| private addEvents(svgDraw: Svg, drawnSvg: SVGElement, hasMetadata: boolean) { |
There was a problem hiding this comment.
drawnSvg is not used in the method?
There was a problem hiding this comment.
You are right, it was used before aligning the branch to the main branch. Removed
| this.svgDraw = SVG().addTo(this.svgDiv).size(this.width, this.height).viewbox(viewBox); | ||
| this.innerSvg = <SVGElement>this.svgDraw.svg(this.svgContent).node.firstElementChild; | ||
| if (this.nadViewerParameters.getCreateSvgFromMetadata()) { | ||
| this.svgWriter?.addSvgContent(<SVGSVGElement>this.innerSvg); |
There was a problem hiding this comment.
Maybe also add a check on the cast?
| this.svgWriter?.addSvgContent(<SVGSVGElement>this.innerSvg); | |
| const svgEl = this.innerSvg; | |
| if (!(svgEl instanceof SVGSVGElement)) { | |
| console.error('[NAD] Expected SVGSVGElement for addSvgContent, got:', svgEl); | |
| return; | |
| } | |
| this.svgWriter?.addSvgContent(svgEl); |
There was a problem hiding this comment.
Check added
| // If not provided, the default library SVG files are used. | ||
| svgUrlResolver?: (fileName: string) => string; | ||
|
|
||
| // Whether create the SVG from diagram metadata, instead of using the SVG content provided as input |
There was a problem hiding this comment.
| // Whether create the SVG from diagram metadata, instead of using the SVG content provided as input | |
| // Whether to create the SVG from diagram metadata, instead of using the SVG content provided as input |
Signed-off-by: massimo.ferraro <massimo.ferraro@soft.it>
| if (this.nadViewerParameters.getCreateSvgFromMetadata() && this.diagramMetadata != null) { | ||
| this.svgWriter = new SvgWriter(this.diagramMetadata); |
There was a problem hiding this comment.
| if (this.nadViewerParameters.getCreateSvgFromMetadata() && this.diagramMetadata != null) { | |
| this.svgWriter = new SvgWriter(this.diagramMetadata); | |
| this.componentLibrary = this.nadViewerParameters.getComponentLibrary() ?? DefaultLibraryComponents; | |
| if (this.nadViewerParameters.getCreateSvgFromMetadata() && this.diagramMetadata != null) { | |
| this.svgWriter = new SvgWriter( | |
| this.diagramMetadata, | |
| this.componentLibrary, | |
| this.nadViewerParameters.getSvgUrlResolver() | |
| ); |
Otherwise the SvgWriter always use the default values (library + svg url resolver)
| if (this.nadViewerParameters.getCreateSvgFromMetadata()) { | ||
| this.addSvgContent(this.svgWriter, this.innerSvg); |
There was a problem hiding this comment.
| if (this.nadViewerParameters.getCreateSvgFromMetadata()) { | |
| this.addSvgContent(this.svgWriter, this.innerSvg); | |
| if (this.svgWriter != null) { | |
| this.svgWriter.addSvgContent(<SVGSVGElement>this.innerSvg); |
| private addSvgContent(svgWriter: SvgWriter | undefined, svgElement: SVGElement) { | ||
| if (svgWriter === undefined) { | ||
| console.warn( | ||
| '[NAD] createSvgFromMetadata is true but no SvgWriter was created (diagramMetadata may be null).' | ||
| ); | ||
| return; | ||
| } | ||
| if (!(svgElement instanceof SVGSVGElement)) { | ||
| console.error('[NAD] Expected SVGSVGElement for addSvgContent, got:', svgElement); | ||
| return; | ||
| } | ||
| svgWriter.addSvgContent(svgElement); | ||
| } |
There was a problem hiding this comment.
method removed
| return new XMLSerializer().serializeToString(baseSvg.xmlDoc); | ||
| } | ||
|
|
||
| public createBaseSvg(textBoxSize?: { width: number; height: number }): { xmlDoc: XMLDocument; svg: SVGSVGElement } { |
There was a problem hiding this comment.
| public createBaseSvg(textBoxSize?: { width: number; height: number }): { xmlDoc: XMLDocument; svg: SVGSVGElement } { | |
| private createBaseSvg(textBoxSize?: { width: number; height: number }): { | |
| xmlDoc: XMLDocument; | |
| svg: SVGSVGElement; | |
| } { |
| const baseSvg = this.createBaseSvg(); | ||
| return new XMLSerializer().serializeToString(baseSvg.xmlDoc); |
There was a problem hiding this comment.
| const baseSvg = this.createBaseSvg(); | |
| return new XMLSerializer().serializeToString(baseSvg.xmlDoc); | |
| const vb = MetadataUtils.getViewBox( | |
| this.diagramMetadata.nodes, | |
| this.diagramMetadata.textNodes, | |
| this.svgParameters | |
| ); | |
| return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="${vb.x} ${vb.y} ${vb.width} ${vb.height}"></svg>`; |
The serialization/deserialisation can probably be avoided
There was a problem hiding this comment.
you are right, used suggested code
| 3: 'THREE', | ||
| }; | ||
| componentLibrary: LibraryComponent[] = DefaultLibraryComponents; | ||
| urlResolver: ((fileName: string) => string) | undefined = undefined; |
There was a problem hiding this comment.
| urlResolver: ((fileName: string) => string) | undefined = undefined; | |
| urlResolver: ((fileName: string) => string) | undefined = undefined; | |
| private abortController: AbortController | undefined = undefined; |
add abortController so that each addSvgContent call aborts the previous contrtoller, cancelling in-flight detchs from any prior init() invocation
There was a problem hiding this comment.
The getComponentPath method in component-utils.ts should change to:
export async function getComponentPath(
componentFilename: string,
svgUrlResolver?: (fileName: string) => string,
signal?: AbortSignal
): Promise<SVGPathElement> {
const url = svgUrlResolver ? svgUrlResolver(componentFilename) : DefaultComponentSvgMapping[componentFilename];
if (!url) throw new Error(`Unknown component file: ${componentFilename}`);
const response = await fetch(url, { signal });
const svgContent = await response.text();
const path = SVG().svg(svgContent).node.firstElementChild?.firstElementChild;
if (!path) throw new Error(`No path found in component SVG: ${componentFilename}`);
return path as SVGPathElement;
}There was a problem hiding this comment.
As far as I understood, the AbortController is used to correctly handle multiple calls to the addSvgContent method. This method is called in the init method of the viewer, in turn called by the constructor. So there should not be multiple calls to the addSvgContent method, at least not for the same SvgWriter. Should be the controller added if, for some reason, the call to the addSvgContent method is put also in other point of the viewer code? Take also into account that the addSvgContent method, right now, assumes that the input SVG is empty, otherwise the resulting svg would have duplicated elements, if you call it multiple times. So, if you want, I can anyway add the controller (it should not create issues) but I'm not sure it is really necessary and useful here.
| return { xmlDoc, svg }; | ||
| } | ||
|
|
||
| public addSvgContent(svg: SVGSVGElement) { |
There was a problem hiding this comment.
| public addSvgContent(svg: SVGSVGElement) { | |
| public addSvgContent(svg: SVGSVGElement) { | |
| this.abortController?.abort(); | |
| this.abortController = new AbortController(); | |
| this.threeWindingsTransformers = []; | |
| this.threeWindingsTransformerEdges = []; |
reset the arrays to not duplicate 3WT nodes
| void ComponentUtils.getComponentPath(subComponent.fileName, this.urlResolver).then((path) => { | ||
| edgeInfoComponent.appendChild(path); | ||
| }); |
There was a problem hiding this comment.
| void ComponentUtils.getComponentPath(subComponent.fileName, this.urlResolver).then((path) => { | |
| edgeInfoComponent.appendChild(path); | |
| }); | |
| ComponentUtils.getComponentPath(subComponent.fileName, this.urlResolver, this.abortController?.signal) | |
| .then((path) => { | |
| edgeInfoComponent.appendChild(path); | |
| }) | |
| .catch((err: unknown) => { | |
| if (err instanceof DOMException && err.name === 'AbortError') return; | |
| console.error('[NAD] Failed to load component', subComponent.fileName, err); | |
| }); |
AbortError is silently ignored while other errors log to console
Signed-off-by: massimo.ferraro <massimo.ferraro@soft.it>
Signed-off-by: massimo.ferraro <massimo.ferraro@soft.it>
Signed-off-by: massimo.ferraro <massimo.ferraro@soft.it>
|



Please check if the PR fulfills these requirements
Does this PR already have an issue describing the problem?
no
What kind of change does this PR introduce?
feature
What is the current behavior?
In the NAD viewer, the SVG writer that creates the SVG starting from diagram metadata does not handle components in edge infos
What is the new behavior (if this is a feature change)?
In the NAD viewer, the SVG writer that creates the SVG starting from diagram metadata handles components in edge infos
Does this PR introduce a breaking change or deprecate an API?
If yes, please check if the following requirements are fulfilled
What changes might users need to make in their application due to this PR? (migration steps)
The creation of the SVG from metadata was previously done in a single step: the SVG was created and provided to the viewer, in the constructor, for the parsing and visualization.
The creation is now done in 2 steps:
So the SVG writer methods has been changed for implementing these 2 steps, even if the old single step method is still available.
This changes were done for handling the asynchronous loading of the components.
A new parameter for choosing the creation of the SVG from the diagram metadata has been added to the NAD parameters