UNPKG

ngx-cytoscapejs

Version:
304 lines (297 loc) 12.4 kB
import * as i0 from '@angular/core'; import { Injectable, EventEmitter, inject, Component, Input, Output, ViewChild } from '@angular/core'; import cytoscape from 'cytoscape'; import { Subject, fromEvent, takeUntil, debounceTime } from 'rxjs'; import { convert } from '@js4cytoscape/cx-viz-converter'; import { CyNetworkUtils, CxToJs } from 'cytoscape-cx2js'; /** * Available libraries to convert from CX to Cytoscape.js. */ var CxConverter; (function (CxConverter) { CxConverter["cx2js"] = "cx2js"; CxConverter["cxVizConverter"] = "cxVizConverter"; })(CxConverter || (CxConverter = {})); /* eslint-disable class-methods-use-this */ /** * This service handles the conversion from CX to Cytoscape.js graphs. */ class CxService { /** * Tries to convert the incoming CX object into CytoscapeOptions using the list of {@link CxConverter}s in sequence. * * @param {any} cxData CX object * @param {CxConverter[]} converters List of {@link CxConverter}s * @returns {CxConversion | null} On success the resulting CxConversion object is returned, otherwise null is returned. */ convert(cxData, converters) { if (cxData && converters) { for (let i = 0; i < converters.length; i += 1) { let conversion = null; switch (converters[i]) { case CxConverter.cx2js: conversion = this.convertWithCx2JS(cxData); break; case CxConverter.cxVizConverter: conversion = this.convertWithCxVizConverter(cxData); break; default: break; } if (conversion) { return conversion; } } } return null; } /** * Tries to convert the input CX object using [cx2js]{@link https://github.com/cytoscape/cx2js}. * * @param {any} cxData CX object * @returns {CxConversion | null} On success the converted CytoscapeOptions object as well as the attributeNameMap object is returned, otherwise null is returned. */ convertWithCx2JS(cxData) { try { const utils = new CyNetworkUtils(); const niceCX = utils.rawCXtoNiceCX(cxData); const cx2Js = new CxToJs(utils); const attributeNameMap = {}; const elements = cx2Js.cyElementsFromNiceCX(niceCX, attributeNameMap); const style = cx2Js.cyStyleFromNiceCX(niceCX, attributeNameMap); const layout = cx2Js.getDefaultLayout(); const zoom = cx2Js.cyZoomFromNiceCX(niceCX); const pan = cx2Js.cyPanFromNiceCX(niceCX); const backgroundColor = cx2Js.cyBackgroundColorFromNiceCX(niceCX); const options = { elements, style, layout, zoom, pan }; const conversion = { options, attributeNameMap, backgroundColor, }; return conversion; } catch (error) { console.error(error); } return null; } /** * Tries to convert the input CX object using [cx-viz-converter]{@link https://github.com/cytoscape/cx-viz-converter}. * * @param {any} cxData CX object * @returns {CytoscapeOptions | null} On success the converted CytoscapeOptions object is returned, otherwise null is returned. */ convertWithCxVizConverter(cxData) { try { const options = convert(cxData, 'cytoscapeJS'); const conversion = { options }; return conversion; } catch (error) { console.error(error); } return null; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: CxService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: CxService, providedIn: 'root' }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: CxService, decorators: [{ type: Injectable, args: [{ providedIn: 'root', }] }] }); /** * This component handles rendering the Cytoscape.js graph. */ class CytoscapejsComponent { /** * Object containing information about a graph. Must conform to [CytoscapeOptions]{@link https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/cytoscape/index.d.ts}. A specified container is ignored. * Should not be defined, if you are using {@link cxData} to build a graph. */ cytoscapeOptions; /** * Responsible for automatically resizing the graph when the browser window changes its size. * If true, resizing the graph is triggered. * If false, changes in window size are ignored. */ autoFit = true; /** * Responsible for applying the background color specified in the CX file to the canvas. * If true, the network's background color will be set to the color specified in the CX. * If false, the network's background color will be white. */ applyCxBackgroundColor = false; /** * Object containing information about a graph. Must conform to [CX data model]{@link https://home.ndexbio.org/data-model/}. * Should not be defined, if you are using {@link cytoscapeOptions} to build a graph. */ cxData; /** * List of {@link CxConverter}s you wish to use for conversion of your {@link cxData}. * Arrange the converters in the order, in which they are to be executed, e.g. the first converter you specify will be tried first. * The first successful conversion result will be rendered. */ cxConverters = [ CxConverter.cx2js, CxConverter.cxVizConverter, ]; /** * Each time a new [Cytoscape.js core]{@link https://js.cytoscape.org/#core} is built, this output is firing an event containing the recent core. */ coreChanged = new EventEmitter(); /** * Each time a new [Cytoscape.js core]{@link https://js.cytoscape.org/#core} is built using [cx2js]{@link https://github.com/cytoscape/cx2js} the * resulting attributeNameMap object is emitted as well. */ cxAttributeNameMapChanged = new EventEmitter(); /** * Reference to the HTMLElement that is used as a [container]{@link https://js.cytoscape.org/#core/initialisation} for the graph. * * @internal */ cyElementRef; /** * Represents a state in the [Angular lifecycle]{@link https://angular.io/guide/lifecycle-hooks} indicating, that the view has been initialized. * * @internal */ isViewInitialized = false; /** * [Cytoscape.js core]{@link https://js.cytoscape.org/#core} that is rendered. * * @internal */ core; /** * Serves as an indicator, when the component has been destroyed. * * @internal */ destroy$ = new Subject(); /** * Service responsible for conversion * * @internal */ cxService = inject(CxService); /** * [Angular lifecycle]{@link https://angular.io/guide/lifecycle-hooks} which is called as soon as the view has been initialized. * Used to subscribe to any window resize event. Triggers initial rendering. */ ngAfterViewInit() { fromEvent(window, 'resize') .pipe(takeUntil(this.destroy$), debounceTime(300)) .subscribe(() => this.fit()); this.isViewInitialized = true; this.preRender(); } /** * [Angular lifecycle]{@link https://angular.io/guide/lifecycle-hooks} which is called every time, an input property changes its value. * Used to update the graph's visualization. * * @param {SimpleChanges} changes Object containing all changes */ ngOnChanges(changes) { const { cytoscapeOptions, cxData, autoFit } = changes; if (cytoscapeOptions || cxData) { this.preRender(); } if (autoFit) { this.fit(); } } /** * [Angular lifecycle]{@link https://angular.io/guide/lifecycle-hooks} which is called as soon as the component is destroyed. * Used to unsubscribe from window resize events. */ ngOnDestroy() { this.destroy$.next(true); this.destroy$.complete(); } /** * Based on the user's input, this method decides, if rendering can happen and which input is to be used. */ preRender() { if (!this.isViewInitialized || (!this.cytoscapeOptions && !this.cxData)) { return; } if (this.cytoscapeOptions) { this.render(this.cytoscapeOptions); } else if (this.cxData) { const conversion = this.cxService.convert(this.cxData, this.cxConverters); if (conversion) { const { options, attributeNameMap, backgroundColor } = conversion; if (this.cyElementRef && this.cyElementRef.nativeElement) { const htmlElement = this.cyElementRef.nativeElement; if (this.applyCxBackgroundColor && backgroundColor) { htmlElement.style.backgroundColor = backgroundColor; } else { htmlElement.style.backgroundColor = ''; } } this.cxAttributeNameMapChanged.emit(attributeNameMap); if (options) { this.render(options); } } } } /** * Renders the graph using [CytoscapeOptions]{@link https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/cytoscape/index.d.ts}. * * @param {CytoscapeOptions} options Object containing graph information */ render(options) { if (options) { this.core = cytoscape({ ...options, container: this.cyElementRef.nativeElement, }); this.core.fit(); this.coreChanged.emit(this.core); } } /** * Resizes and repositions the graph to fit the current window size. */ fit() { if (this.autoFit && this.core) { this.core.fit(); } } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: CytoscapejsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.1.3", type: CytoscapejsComponent, isStandalone: true, selector: "cytoscapejs", inputs: { cytoscapeOptions: "cytoscapeOptions", autoFit: "autoFit", applyCxBackgroundColor: "applyCxBackgroundColor", cxData: "cxData", cxConverters: "cxConverters" }, outputs: { coreChanged: "coreChanged", cxAttributeNameMapChanged: "cxAttributeNameMapChanged" }, viewQueries: [{ propertyName: "cyElementRef", first: true, predicate: ["cy"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div id=\"cy\" #cy></div>\n", styles: ["#cy{height:100%;width:100%}\n"] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: CytoscapejsComponent, decorators: [{ type: Component, args: [{ selector: 'cytoscapejs', standalone: true, template: "<div id=\"cy\" #cy></div>\n", styles: ["#cy{height:100%;width:100%}\n"] }] }], propDecorators: { cytoscapeOptions: [{ type: Input }], autoFit: [{ type: Input }], applyCxBackgroundColor: [{ type: Input }], cxData: [{ type: Input }], cxConverters: [{ type: Input }], coreChanged: [{ type: Output }], cxAttributeNameMapChanged: [{ type: Output }], cyElementRef: [{ type: ViewChild, args: ['cy'] }] } }); /* * Public API Surface of ngx-cytoscapejs */ /** * Generated bundle index. Do not edit. */ export { CxConverter, CytoscapejsComponent }; //# sourceMappingURL=ngx-cytoscapejs.mjs.map