ngx-cytoscapejs
Version:
Cytoscape.js Angular Wrapper
304 lines (297 loc) • 12.4 kB
JavaScript
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