UNPKG

cytoscape-angular

Version:
995 lines (983 loc) 97.7 kB
import * as i0 from '@angular/core'; import { Component, ViewChild, Input, EventEmitter, Output, NgModule } from '@angular/core'; import * as i2 from '@angular/common'; import { CommonModule } from '@angular/common'; import * as i2$1 from 'primeng/progressspinner'; import { ProgressSpinnerModule } from 'primeng/progressspinner'; import { __classPrivateFieldGet, __classPrivateFieldSet } from 'tslib'; import * as i1$1 from 'primeng/overlaypanel'; import { OverlayPanelModule } from 'primeng/overlaypanel'; import * as i2$3 from 'primeng/api'; import * as i7 from 'primeng/dropdown'; import { DropdownModule } from 'primeng/dropdown'; import * as i1 from '@angular/forms'; import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; import * as i8 from 'primeng/button'; import { ButtonModule } from 'primeng/button'; import * as i3 from 'primeng/fieldset'; import { FieldsetModule } from 'primeng/fieldset'; import * as i4 from 'primeng/inputswitch'; import { InputSwitchModule } from 'primeng/inputswitch'; import * as i5 from 'primeng/tooltip'; import { TooltipModule } from 'primeng/tooltip'; import * as i6 from 'primeng/inputtext'; import { InputTextModule } from 'primeng/inputtext'; import * as i2$2 from 'primeng/autocomplete'; import { AutoCompleteModule } from 'primeng/autocomplete'; import { ProgressBarModule } from 'primeng/progressbar'; import { SpinnerModule } from 'primeng/spinner'; const _c0$2 = ["cyGraph"]; function CytoscapeGraphComponent_p_progressSpinner_0_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelement(0, "p-progressSpinner", 3); } } /** * The API is a little odd to provide flexibility. * EITHER bind to cyOptions (type CytoscapeOptions), to control the options yourself * OR this component will build a CytoscapeOptions internally by using all the other inputs. * If cyOptions is supplied, all other inputs are ignored. * The cyOptions container (HTML element) is always ignored and set internally. */ class CytoscapeGraphComponent { constructor() { this.debug = false; this.showToolbar = true; this.loading = false; } ngOnChanges(changes) { console.log('cytoscape graph component ngOnChanges. changes:', JSON.stringify(changes)); if (changes["style"]) { console.log('changes["style"]:', JSON.stringify(changes["style"])); this.runWhileLoading(this.updateStyles.bind(this)); } } centerElements(selector) { if (!this.cy) { return; } const elems = this.cy.$(selector); this.cy.center(elems); } zoomToElement(selector, level = 3) { var _a, _b; let position = (_b = (_a = this.cy) === null || _a === void 0 ? void 0 : _a.$(selector)) === null || _b === void 0 ? void 0 : _b.position(); if (!position) { console.warn(`Cannot zoom to ${selector}`); } this.cy.zoom({ level: level, position: position }); } render() { this.runWhileLoading(this.rerender.bind(this)); } runWhileLoading(f) { this.loading = true; setTimeout(() => { f(); setTimeout(() => { this.loading = false; }, 30); }, 0); } updateStyles() { if (this.cy && this.style) { this.cy.style(this.style); } } rerender() { //TODO : this takes a heavy-handed approach, refine for performance if (!this.cyGraph) { console.warn(`No cyGraph found`); return; } const cyOptions = this.cyOptions || { // ignored, use nodes and edges // elements: this.elements, autolock: this.autolock, autoungrabify: this.autoungrabify, autounselectify: this.autounselectify, boxSelectionEnabled: this.boxSelectionEnabled, container: this.cyGraph.nativeElement, desktopTapThreshold: this.desktopTapThreshold, hideEdgesOnViewport: this.hideEdgesOnViewport, hideLabelsOnViewport: this.hideLabelsOnViewport, layout: this.layoutOptions, maxZoom: this.maxZoom, minZoom: this.minZoom, motionBlur: this.motionBlur, motionBlurOpacity: this.motionBlurOpacity, pan: this.pan, panningEnabled: this.panningEnabled, pixelRatio: this.pixelRatio, selectionType: this.selectionType, style: this.style, styleEnabled: this.styleEnabled, textureOnViewport: this.textureOnViewport, touchTapThreshold: this.touchTapThreshold, userPanningEnabled: this.userPanningEnabled, userZoomingEnabled: this.userZoomingEnabled, wheelSensitivity: this.wheelSensitivity, zoomingEnabled: this.zoomingEnabled, zoom: this.zoom, }; // TODO do reset() instead? this.cy = cytoscape(cyOptions); this.cy.startBatch(); this.cy.boxSelectionEnabled(this.boxSelectionEnabled); this.cy.nodes().remove(); this.cy.edges().remove(); if (this.nodes) { this.cy.add(this.nodes); } if (this.edges) { this.cy.add(this.edges); } this.cy.endBatch(); if (this.layoutOptions) { this.cy.layout(this.layoutOptions).run(); } } } CytoscapeGraphComponent.ɵfac = function CytoscapeGraphComponent_Factory(t) { return new (t || CytoscapeGraphComponent)(); }; CytoscapeGraphComponent.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: CytoscapeGraphComponent, selectors: [["cytoscape-graph"]], viewQuery: function CytoscapeGraphComponent_Query(rf, ctx) { if (rf & 1) { i0.ɵɵviewQuery(_c0$2, 5); } if (rf & 2) { let _t; i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.cyGraph = _t.first); } }, inputs: { debug: "debug", nodes: "nodes", edges: "edges", autolock: "autolock", autoungrabify: "autoungrabify", autounselectify: "autounselectify", boxSelectionEnabled: "boxSelectionEnabled", desktopTapThreshold: "desktopTapThreshold", hideEdgesOnViewport: "hideEdgesOnViewport", hideLabelsOnViewport: "hideLabelsOnViewport", layoutOptions: "layoutOptions", maxZoom: "maxZoom", minZoom: "minZoom", motionBlur: "motionBlur", motionBlurOpacity: "motionBlurOpacity", pan: "pan", panningEnabled: "panningEnabled", pixelRatio: "pixelRatio", selectionType: "selectionType", style: "style", styleEnabled: "styleEnabled", textureOnViewport: "textureOnViewport", touchTapThreshold: "touchTapThreshold", userPanningEnabled: "userPanningEnabled", userZoomingEnabled: "userZoomingEnabled", wheelSensitivity: "wheelSensitivity", zoom: "zoom", zoomingEnabled: "zoomingEnabled", showToolbar: "showToolbar" }, features: [i0.ɵɵNgOnChangesFeature], decls: 3, vars: 1, consts: [["class", "spinner", "strokeWidth", "4", "fill", "#EEEEEE", "animationDuration", ".5s", 4, "ngIf"], [1, "graphWrapper"], ["cyGraph", ""], ["strokeWidth", "4", "fill", "#EEEEEE", "animationDuration", ".5s", 1, "spinner"]], template: function CytoscapeGraphComponent_Template(rf, ctx) { if (rf & 1) { i0.ɵɵtemplate(0, CytoscapeGraphComponent_p_progressSpinner_0_Template, 1, 0, "p-progressSpinner", 0); i0.ɵɵelement(1, "div", 1, 2); } if (rf & 2) { i0.ɵɵproperty("ngIf", ctx.loading); } }, directives: [i2.NgIf, i2$1.ProgressSpinner], styles: [".spinner[_ngcontent-%COMP%] {\n position: absolute;\n left: '350px';\n z-index: 10;\n width: '250px';\n height: '250px';\n }\n @keyframes ui-progress-spinner-color {\n 100%,\n 0% {\n stroke: #d62d20;\n }\n 40% {\n stroke: #0057e7;\n }\n 66% {\n stroke: #008744;\n }\n 80%,\n 90% {\n stroke: #ffa700;\n }\n }\n .graphWrapper[_ngcontent-%COMP%] {\n height: 100%;\n width: 100%;\n }"] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(CytoscapeGraphComponent, [{ type: Component, args: [{ selector: 'cytoscape-graph', template: ` <p-progressSpinner *ngIf="loading" class="spinner" strokeWidth="4" fill="#EEEEEE" animationDuration=".5s"></p-progressSpinner> <div #cyGraph class="graphWrapper"> </div> `, styles: [` .spinner { position: absolute; left: '350px'; z-index: 10; width: '250px'; height: '250px'; } @keyframes ui-progress-spinner-color { 100%, 0% { stroke: #d62d20; } 40% { stroke: #0057e7; } 66% { stroke: #008744; } 80%, 90% { stroke: #ffa700; } } .graphWrapper { height: 100%; width: 100%; }` ] }] }], function () { return []; }, { cyGraph: [{ type: ViewChild, args: ['cyGraph'] }], debug: [{ type: Input }], nodes: [{ type: Input }], edges: [{ type: Input }], autolock: [{ type: Input }], autoungrabify: [{ type: Input }], autounselectify: [{ type: Input }], boxSelectionEnabled: [{ type: Input }], desktopTapThreshold: [{ type: Input }], hideEdgesOnViewport: [{ type: Input }], hideLabelsOnViewport: [{ type: Input }], layoutOptions: [{ type: Input }], maxZoom: [{ type: Input }], minZoom: [{ type: Input }], motionBlur: [{ type: Input }], motionBlurOpacity: [{ type: Input }], pan: [{ type: Input }], panningEnabled: [{ type: Input }], pixelRatio: [{ type: Input }], selectionType: [{ type: Input }], style: [{ type: Input }], styleEnabled: [{ type: Input }], textureOnViewport: [{ type: Input }], touchTapThreshold: [{ type: Input }], userPanningEnabled: [{ type: Input }], userZoomingEnabled: [{ type: Input }], wheelSensitivity: [{ type: Input }], zoom: [{ type: Input }], zoomingEnabled: [{ type: Input }], showToolbar: [{ type: Input }] }); })(); class BaseLayoutOptionsImpl { ready(e) { // tslint:disable-next-line:no-console console.debug(`layout ready, cytoscape.LayoutEventObject: ${JSON.stringify(e)}`); // on layoutready } stop(e) { // tslint:disable-next-line:no-console console.debug(`layout stop, cytoscape.LayoutEventObject: ${JSON.stringify(e)}`); // on layoutstop } } class NullLayoutOptionsImpl extends BaseLayoutOptionsImpl { constructor() { super(...arguments); this.name = 'null'; } } class AnimateLayoutOptionsImpl extends BaseLayoutOptionsImpl { constructor() { super(...arguments); // the zoom level to set (prob want fit = false if set) this.zoom = null; // the pan level to set (prob want fit = false if set) this.pan = null; // whether to transition the node positions this.animate = false; // duration of animation in ms if enabled this.animationDuration = 500; // easing of animation if enabled this.animationEasing = undefined; // a function that determines whether the node should be animated. // All nodes animated by default on animate enabled. Non-animated nodes are // positioned immediately when the layout starts this.animateFilter = (node, i) => true; } } class PresetLayoutOptionsImpl extends AnimateLayoutOptionsImpl { constructor() { super(...arguments); this.name = 'preset'; // transform a given node position. Useful for changing flow direction in discrete layouts this.transform = (node, position) => position; } } class ShapedLayoutOptionsImpl extends AnimateLayoutOptionsImpl { constructor() { super(...arguments); // whether to fit to viewport this.fit = true; // fit padding this.padding = 30; // constrain layout bounds this.boundingBox = null; // prevents node overlap, may overflow boundingBox if not enough space this.avoidOverlap = true; // Excludes the label when calculating node bounding boxes for the layout algorithm this.nodeDimensionsIncludeLabels = false; // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up this.spacingFactor = 1.75; // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') } this.sort = null; // transform a given node position. Useful for changing flow direction in discrete layouts this.transform = (node, position) => position; } } class GridLayoutOptionsImpl extends ShapedLayoutOptionsImpl { constructor() { super(...arguments); this.name = 'grid'; // extra spacing around nodes when avoidOverlap: true this.avoidOverlapPadding = 10; // uses all available space on false, uses minimal space on true this.condense = false; // force num of rows in the grid this.rows = null; // force num of columns in the grid this.cols = null; // returns { row, col } for element // (node: NodeSingular) => return { row: number; col: number; } this.position = null; } } class RandomLayoutOptionsImpl extends AnimateLayoutOptionsImpl { constructor() { super(...arguments); this.name = 'random'; this.fit = true; this.padding = 20; // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h } this.boundingBox = null; // transform a given node position. Useful for changing flow direction in discrete layouts this.transform = (node, position) => position; } } class CircleLayoutOptionsImpl extends ShapedLayoutOptionsImpl { constructor() { super(...arguments); this.name = 'circle'; this.startAngle = 3 / 2 * Math.PI; // where nodes start in radians this.sweep = null; // how many radians should be between the first and last node (defaults to full circle) } } // Note: "radius" is not part of concentric, imperfect extension class ConcentricLayoutOptionsImpl { constructor() { this.name = 'concentric'; // where nodes start in radians, e.g. 3 / 2 * Math.PI, this.startAngle = 3 / 2 * Math.PI; // height of layout area (overrides container height) this.height = null; // width of layout area (overrides container width) this.width = null; } concentric(node) { return 0; } levelWidth(node) { return 0; } } class BreadthFirstLayoutOptionsImpl extends ShapedLayoutOptionsImpl { constructor() { super(...arguments); this.name = 'breadthfirst'; // whether the tree is directed downwards (or edges can point in any direction if false) this.directed = false; // put depths in concentric circles if true, put depths top down if false this.circle = false; // whether to shift nodes down their natural BFS depths in order to avoid upwards edges (DAGS only) this.maximal = false; this.grid = false; // whether to create an even grid into which the DAG is placed (circle:false only) } } class CoseLayoutOptionsImpl extends ShapedLayoutOptionsImpl { constructor() { super(...arguments); this.name = 'cose'; // Number of iterations between consecutive screen positions update this.refresh = 20; // Randomize the initial positions of the nodes (true) or use existing positions (false) this.randomize = false; // Extra spacing between components in non-compound graphs this.componentSpacing = 40; // Node repulsion (overlapping) multiplier this.nodeOverlap = 4; // Nesting factor (multiplier) to compute ideal edge length for nested edges this.nestingFactor = 1.2; // Gravity force (constant) this.gravity = 1; // Maximum number of iterations to perform this.numIter = 1000; // Initial temperature (maximum node displacement) this.initialTemp = 1000; // Cooling factor (how the temperature is reduced between consecutive iterations this.coolingFactor = 0.99; // Lower temperature threshold (below this point the layout will end) this.minTemp = 1.0; // Node repulsion (non overlapping) multiplier this.nodeRepulsion = (node) => 2048; // Ideal edge (non nested) length this.idealEdgeLength = (edge) => 32; // Divisor to compute edge forces this.edgeElasticity = (edge) => 32; } } class DagreLayoutOptionsImpl extends ShapedLayoutOptionsImpl { constructor() { super(); this.name = 'dagre'; this.nodeSep = null; // the separation between adjacent nodes in the same rank this.edgeSep = null; // the separation between adjacent edges in the same rank this.rankSep = null; // the separation between each rank in the layout // TB for top to bottom flow, 'LR' for left to right this.rankDir = 'TB'; // Type of algorithm to assign a rank to each node in the input graph. // Possible values: 'network-simplex', 'tight-tree' or 'longest-path' this.ranker = null; // number of ranks to keep between the source and target of the edge this.minLen = (edge) => 1; this.edgeWeight = (edge) => 1; // higher weight edges are generally made shorter and straighter than lower weight edges } } class FormInfo { constructor(title, fieldsets, showSubmitButton = false, submitText = 'Submit', disableSubmitOnFormInvalid = false, /* if the model has a property that isn't in a fieldset, but it in an fieldset created by the form */ otherFieldsetTitle = null) { this.title = title; this.fieldsets = fieldsets; this.showSubmitButton = showSubmitButton; this.submitText = submitText; this.disableSubmitOnFormInvalid = disableSubmitOnFormInvalid; this.otherFieldsetTitle = otherFieldsetTitle; } } class FieldsetInfo { constructor(legend, fieldInfos, displayOnlyIfProperties) { this.legend = legend; this.fieldInfos = fieldInfos; this.displayOnlyIfProperties = displayOnlyIfProperties; } showFieldsetForModel(model) { if (!this.displayOnlyIfProperties) { return true; } for (const fieldInfo of this.fieldInfos) { for (const modelProperty of Object.keys(model)) { if (fieldInfo.modelProperty === modelProperty) { return true; } } } return false; } } class FieldInfo { constructor(/* label to show the user next to the field, can be a function for i18n/dynamic labels */ label, /* The form has a model, this is the name of the property on the form's model object that this field */ modelProperty, /* computed from the model property type by default */ type, /* The tooltip to display on hover */ tooltip, /* The list of Angular Form Validators for the control or a function that returns such an array */ validators, /* disable the field if it's not valid */ disableWhenInvalid = false, /* If true and model[modelProperty] is undefined, don't create a field.*/ hideWhenNoModelProperty = true, /* Input only - by default the label is used as a placeholder and floats (how to downcast in a template?) */ placeholder, /* Input only - same as HTML input (how to downcast in a template?) */ inputType = 'text', /* Input only - same as HTML input (how to downcast in a template?) */ inputSize = 8, /* Select only either an array of object or the name of a model property or function that is/returns an array of objects */ options, /* In an options object, what field to display to the user (or function that returns a string given the option object and the model) */ optionArrayLabelField, /* In an options object, what field to return for the value of the option (or function that returns a string given the option object and the model) */ optionArrayValueField) { this.label = label; this.modelProperty = modelProperty; this.type = type; this.tooltip = tooltip; this.validators = validators; this.disableWhenInvalid = disableWhenInvalid; this.hideWhenNoModelProperty = hideWhenNoModelProperty; this.placeholder = placeholder; this.inputType = inputType; this.inputSize = inputSize; this.options = options; this.optionArrayLabelField = optionArrayLabelField; this.optionArrayValueField = optionArrayValueField; this.fieldTypes = {}; } fieldType(model) { const cached = this.fieldTypes[this.modelProperty]; if (cached) { return cached; } else { const fieldValueType = typeof model[this.modelProperty]; const result = this.type ? this.type : (this.options ? 'options' : fieldValueType); this.fieldTypes[this.modelProperty] = result; return result; } } setValue(newValue, model, modelChange) { model[this.modelProperty] = newValue; modelChange.emit({ property: this.modelProperty, value: newValue }); } } function FluidFormComponent_ng_container_1_p_fieldset_1_div_2_ng_container_2_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelementContainerStart(0); i0.ɵɵelementStart(1, "span", 10); i0.ɵɵtext(2); i0.ɵɵelementEnd(); i0.ɵɵelement(3, "p-inputSwitch", 11); i0.ɵɵelementContainerEnd(); } if (rf & 2) { const fieldInfo_r5 = i0.ɵɵnextContext().$implicit; i0.ɵɵadvance(2); i0.ɵɵtextInterpolate1(" ", fieldInfo_r5.label, " "); i0.ɵɵadvance(1); i0.ɵɵpropertyInterpolate("name", fieldInfo_r5.modelProperty); i0.ɵɵpropertyInterpolate("pTooltip", fieldInfo_r5.tooltip); i0.ɵɵpropertyInterpolate("formControlName", fieldInfo_r5.modelProperty); } } function FluidFormComponent_ng_container_1_p_fieldset_1_div_2_ng_container_3_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelementContainerStart(0); i0.ɵɵelementStart(1, "span", 12); i0.ɵɵelement(2, "input", 13); i0.ɵɵelementStart(3, "label", 14); i0.ɵɵtext(4); i0.ɵɵelementEnd(); i0.ɵɵelementEnd(); i0.ɵɵelementContainerEnd(); } if (rf & 2) { const fieldInfo_r5 = i0.ɵɵnextContext().$implicit; i0.ɵɵadvance(2); i0.ɵɵpropertyInterpolate("id", fieldInfo_r5.modelProperty); i0.ɵɵpropertyInterpolate("name", fieldInfo_r5.modelProperty); i0.ɵɵpropertyInterpolate("formControlName", fieldInfo_r5.modelProperty); i0.ɵɵproperty("pTooltip", fieldInfo_r5.tooltip)("type", fieldInfo_r5.inputType)("size", fieldInfo_r5.inputSize); i0.ɵɵadvance(1); i0.ɵɵpropertyInterpolate("for", fieldInfo_r5.modelProperty); i0.ɵɵadvance(1); i0.ɵɵtextInterpolate(fieldInfo_r5.label); } } function FluidFormComponent_ng_container_1_p_fieldset_1_div_2_ng_container_4_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelementContainerStart(0); i0.ɵɵelementStart(1, "span", 12); i0.ɵɵelement(2, "p-dropdown", 15); i0.ɵɵelementStart(3, "label", 14); i0.ɵɵtext(4); i0.ɵɵelementEnd(); i0.ɵɵelementEnd(); i0.ɵɵelementContainerEnd(); } if (rf & 2) { const fieldInfo_r5 = i0.ɵɵnextContext().$implicit; i0.ɵɵadvance(2); i0.ɵɵpropertyInterpolate("formControlName", fieldInfo_r5.modelProperty); i0.ɵɵproperty("name", fieldInfo_r5.modelProperty)("options", fieldInfo_r5.options)("optionLabel", fieldInfo_r5.optionArrayLabelField)("pTooltip", fieldInfo_r5.tooltip); i0.ɵɵadvance(1); i0.ɵɵpropertyInterpolate("for", fieldInfo_r5.modelProperty); i0.ɵɵadvance(1); i0.ɵɵtextInterpolate(fieldInfo_r5.label); } } function FluidFormComponent_ng_container_1_p_fieldset_1_div_2_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelementStart(0, "div", 7); i0.ɵɵelementStart(1, "div", 8); i0.ɵɵtemplate(2, FluidFormComponent_ng_container_1_p_fieldset_1_div_2_ng_container_2_Template, 4, 4, "ng-container", 9); i0.ɵɵtemplate(3, FluidFormComponent_ng_container_1_p_fieldset_1_div_2_ng_container_3_Template, 5, 8, "ng-container", 9); i0.ɵɵtemplate(4, FluidFormComponent_ng_container_1_p_fieldset_1_div_2_ng_container_4_Template, 5, 7, "ng-container", 9); i0.ɵɵelementEnd(); i0.ɵɵelementEnd(); } if (rf & 2) { const fieldInfo_r5 = ctx.$implicit; const ctx_r4 = i0.ɵɵnextContext(3); i0.ɵɵadvance(2); i0.ɵɵproperty("ngIf", fieldInfo_r5.fieldType(ctx_r4.model) === "boolean"); i0.ɵɵadvance(1); i0.ɵɵproperty("ngIf", fieldInfo_r5.fieldType(ctx_r4.model) === "string" || fieldInfo_r5.fieldType(ctx_r4.model) === "number"); i0.ɵɵadvance(1); i0.ɵɵproperty("ngIf", fieldInfo_r5.fieldType(ctx_r4.model) === "options"); } } function FluidFormComponent_ng_container_1_p_fieldset_1_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelementStart(0, "p-fieldset", 4); i0.ɵɵelementStart(1, "div", 5); i0.ɵɵtemplate(2, FluidFormComponent_ng_container_1_p_fieldset_1_div_2_Template, 5, 3, "div", 6); i0.ɵɵelementEnd(); i0.ɵɵelementEnd(); } if (rf & 2) { const fieldSetInfo_r2 = i0.ɵɵnextContext().$implicit; i0.ɵɵpropertyInterpolate("legend", fieldSetInfo_r2.legend); i0.ɵɵadvance(2); i0.ɵɵproperty("ngForOf", fieldSetInfo_r2.fieldInfos); } } function FluidFormComponent_ng_container_1_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelementContainerStart(0); i0.ɵɵtemplate(1, FluidFormComponent_ng_container_1_p_fieldset_1_Template, 3, 2, "p-fieldset", 3); i0.ɵɵelementContainerEnd(); } if (rf & 2) { const fieldSetInfo_r2 = ctx.$implicit; const ctx_r0 = i0.ɵɵnextContext(); i0.ɵɵadvance(1); i0.ɵɵproperty("ngIf", fieldSetInfo_r2.showFieldsetForModel(ctx_r0.model)); } } function FluidFormComponent_button_2_Template(rf, ctx) { if (rf & 1) { const _r14 = i0.ɵɵgetCurrentView(); i0.ɵɵelementStart(0, "button", 16); i0.ɵɵlistener("submit", function FluidFormComponent_button_2_Template_button_submit_0_listener() { i0.ɵɵrestoreView(_r14); const ctx_r13 = i0.ɵɵnextContext(); return ctx_r13.onSubmit(); }); i0.ɵɵtext(1); i0.ɵɵelementEnd(); } if (rf & 2) { const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵproperty("disabled", ctx_r1.formInfo.disableSubmitOnFormInvalid && !ctx_r1.formGroup.valid); i0.ɵɵadvance(1); i0.ɵɵtextInterpolate(ctx_r1.formInfo.submitText || "Submit"); } } class FluidFormComponent { constructor() { this.modelChange = new EventEmitter(); } ngOnInit() { console.debug('FluidFormComponent this.formInfo:', JSON.stringify(this.formInfo)); let controls = {}; this.formInfo.fieldsets.forEach(fieldsetInfo => { fieldsetInfo.fieldInfos.forEach(fieldInfo => { let modelValue = this.model[fieldInfo.modelProperty]; // console.log('fieldInfo.modelProperty:', fieldInfo.modelProperty, ', modelValue:', modelValue) const validators = typeof fieldInfo.validators === 'function' ? fieldInfo.validators() : fieldInfo.validators; const asyncValidators = typeof fieldInfo.asyncValidators === 'function' ? fieldInfo.asyncValidators() : fieldInfo.asyncValidators; const { updateOn } = fieldInfo; let formControl = new FormControl(modelValue, { validators, asyncValidators, updateOn }); formControl.valueChanges.subscribe((change) => { console.debug('form control change ', JSON.stringify(change), ' for prop ', fieldInfo.modelProperty, ', changing current model value ', this.model[fieldInfo.modelProperty], ' to ', change); fieldInfo.setValue(change, this.model, this.modelChange); }); controls[fieldInfo.modelProperty] = formControl; }); }); this.formGroup = new FormGroup(controls); } ngOnChanges(changes) { var _a; console.debug('ngOnChanges fluid-form changes:', JSON.stringify(changes)); if (changes['model']) { const model = changes['model'].currentValue; for (let key of Object.keys(model)) { console.debug('ngOnChanges model key copying to form:', key); const control = (_a = this.formGroup) === null || _a === void 0 ? void 0 : _a.controls[key]; control ? control.setValue(model[key], { emitEvent: false }) : console.warn('no control for model key ', key); } } } ngAfterViewInit() { // console.debug("ngAfterViewInit") } ngAfterViewChecked() { // console.debug("ngAfterViewChecked") } onSubmit() { console.log(`Form submitted`); } } FluidFormComponent.ɵfac = function FluidFormComponent_Factory(t) { return new (t || FluidFormComponent)(); }; FluidFormComponent.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: FluidFormComponent, selectors: [["cyng-fluid-form"]], inputs: { model: "model", modelProperty: "modelProperty", formInfo: "formInfo" }, outputs: { modelChange: "modelChange" }, features: [i0.ɵɵNgOnChangesFeature], decls: 3, vars: 4, consts: [[3, "formGroup", "title", "ngSubmit"], [4, "ngFor", "ngForOf"], ["pButton", "", 3, "disabled", "submit", 4, "ngIf"], ["class", "fieldset", 3, "legend", 4, "ngIf"], [1, "fieldset", 3, "legend"], [1, "ui-g", "ui-fluid"], ["class", "ui-g-12 ui-md-4 field", 4, "ngFor", "ngForOf"], [1, "ui-g-12", "ui-md-4", "field"], [1, "ui-inputgroup"], [4, "ngIf"], [1, "ui-chkbox-label"], [3, "name", "pTooltip", "formControlName"], [1, "ui-float-label"], ["pInputText", "", 3, "id", "name", "formControlName", "pTooltip", "type", "size"], [3, "for"], [3, "formControlName", "name", "options", "optionLabel", "pTooltip"], ["pButton", "", 3, "disabled", "submit"]], template: function FluidFormComponent_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelementStart(0, "form", 0); i0.ɵɵlistener("ngSubmit", function FluidFormComponent_Template_form_ngSubmit_0_listener() { return ctx.onSubmit(); }); i0.ɵɵtemplate(1, FluidFormComponent_ng_container_1_Template, 2, 1, "ng-container", 1); i0.ɵɵelementEnd(); i0.ɵɵtemplate(2, FluidFormComponent_button_2_Template, 2, 2, "button", 2); } if (rf & 2) { i0.ɵɵproperty("formGroup", ctx.formGroup)("title", ctx.formInfo == null ? null : ctx.formInfo.title); i0.ɵɵadvance(1); i0.ɵɵproperty("ngForOf", ctx.formInfo == null ? null : ctx.formInfo.fieldsets); i0.ɵɵadvance(1); i0.ɵɵproperty("ngIf", ctx.formInfo.showSubmitButton); } }, directives: [i1.ɵNgNoValidate, i1.NgControlStatusGroup, i1.FormGroupDirective, i2.NgForOf, i2.NgIf, i3.Fieldset, i4.InputSwitch, i5.Tooltip, i1.NgControlStatus, i1.FormControlName, i1.DefaultValueAccessor, i6.InputText, i7.Dropdown, i8.ButtonDirective], styles: [".ui-chkbox-label[_ngcontent-%COMP%] {\n padding-right: 0.5em;\n }\n\n .ui-dropdown-label[_ngcontent-%COMP%] {\n align-self: center;\n padding-right: 0.5em\n }\n\n .field[_ngcontent-%COMP%]:nth-child(n+4) {\n margin-top: 1em; // otherwise overlap betwen a field and a floating label of the field below it\n }\n\n .fieldset[_ngcontent-%COMP%] {\n }"] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FluidFormComponent, [{ type: Component, args: [{ selector: 'cyng-fluid-form', template: ` <form [formGroup]="formGroup" [title]="formInfo?.title" (ngSubmit)="onSubmit()"> <ng-container *ngFor="let fieldSetInfo of formInfo?.fieldsets"> <p-fieldset *ngIf="fieldSetInfo.showFieldsetForModel(model)" class="fieldset" legend="{{fieldSetInfo.legend}}"> <div class="ui-g ui-fluid"> <div class="ui-g-12 ui-md-4 field" *ngFor="let fieldInfo of fieldSetInfo.fieldInfos"> <div class="ui-inputgroup"> <ng-container *ngIf="fieldInfo.fieldType(model) === 'boolean'"> <span class="ui-chkbox-label"> {{fieldInfo.label}} </span> <p-inputSwitch name="{{fieldInfo.modelProperty}}" pTooltip="{{fieldInfo.tooltip}}" formControlName="{{fieldInfo.modelProperty}}" > </p-inputSwitch> </ng-container> <ng-container *ngIf="fieldInfo.fieldType(model) === 'string' || fieldInfo.fieldType(model) === 'number'"> <span class="ui-float-label"> <input pInputText id="{{fieldInfo.modelProperty}}" name="{{fieldInfo.modelProperty}}" formControlName="{{fieldInfo.modelProperty}}" [pTooltip]="fieldInfo.tooltip" [type]="fieldInfo.inputType" [size]="fieldInfo.inputSize" /> <label for="{{fieldInfo.modelProperty}}">{{fieldInfo.label}}</label> </span> </ng-container> <ng-container *ngIf="fieldInfo.fieldType(model) === 'options'"> <span class="ui-float-label"> <p-dropdown formControlName="{{fieldInfo.modelProperty}}" [name]="fieldInfo.modelProperty" [options]="fieldInfo.options" [optionLabel]="fieldInfo.optionArrayLabelField" [pTooltip]="fieldInfo.tooltip" ></p-dropdown> <label for="{{fieldInfo.modelProperty}}">{{fieldInfo.label}}</label> </span> </ng-container> </div> </div> </div> </p-fieldset> </ng-container> </form> <button *ngIf="formInfo.showSubmitButton" pButton [disabled]="formInfo.disableSubmitOnFormInvalid && !formGroup.valid" (submit)="onSubmit()">{{formInfo.submitText || 'Submit' }}</button> `, styles: [` .ui-chkbox-label { padding-right: 0.5em; } .ui-dropdown-label { align-self: center; padding-right: 0.5em } .field:nth-child(n+4) { margin-top: 1em; // otherwise overlap betwen a field and a floating label of the field below it } .fieldset { } `] }] }], function () { return []; }, { model: [{ type: Input }], modelChange: [{ type: Output }], modelProperty: [{ type: Input }], formInfo: [{ type: Input }] }); })(); const _c0$1 = ["layoutForm"]; class CytoscapeLayoutToolComponent { constructor() { this.changed = false; this.layoutOptionsChange = new EventEmitter(); this.layoutOptionsList = [ new BreadthFirstLayoutOptionsImpl(), new CoseLayoutOptionsImpl(), new DagreLayoutOptionsImpl(), new CircleLayoutOptionsImpl(), new ConcentricLayoutOptionsImpl(), new GridLayoutOptionsImpl(), new PresetLayoutOptionsImpl(), new RandomLayoutOptionsImpl(), new NullLayoutOptionsImpl(), ]; } get layoutOptions() { return this._layoutOptions; } set layoutOptions(value) { console.log(`set layoutOptions: ${value === null || value === void 0 ? void 0 : value.name}`); this._layoutOptions = value; } ngOnInit() { this.formInfo = CytoscapeLayoutToolComponent.createLayoutFormInfo(); let layoutOptionsSelect = this.layoutOptionsList[5]; console.log('setting the initial selected layout, default: ', layoutOptionsSelect.name); if (this.layoutOptions) { console.log(`setting the initial selected layout based on input/output layout ${JSON.stringify(this.layoutOptions)}`); this.addOrReplaceInLayoutOptionsList(this.layoutOptions); } console.log('Initializing this.selectedLayoutInfo with layoutOptionsSelect ', JSON.stringify(layoutOptionsSelect)); } ngOnChanges(changes) { console.log('ngOnChanges layout changes:', JSON.stringify(changes)); if (changes['layoutOptions']) { } } onLayoutModelChange() { console.log('Layout model change: ', JSON.stringify(this.layoutOptions)); this.changed = true; } onFormModelChange() { console.log('onFormModelChange'); this.changed = true; } onApplyLayout() { this.changed = false; this.layoutOptionsChange.emit(this.layoutOptions); } addOrReplaceInLayoutOptionsList(layoutOptions) { let matchingOptions = this.layoutOptionsList.find(selectOption => selectOption.name === layoutOptions.name); if (matchingOptions) { console.log('got matching layoutOptions: ', JSON.stringify(matchingOptions)); this.layoutOptionsList.splice(this.layoutOptionsList.indexOf(matchingOptions), 1, layoutOptions); } else { console.info(`Did you pass a new kind of layout? The layout name ${name} was not found, adding a new one to the top of the list.`); this.layoutOptionsList.unshift(layoutOptions); } } static createLayoutFormInfo() { let fit = new FieldInfo('Fit', 'fit', 'boolean', 'Whether to fit to viewport'); let padding = new FieldInfo('Padding', 'padding', 'number', 'When fit to viewport, padding inside the viewport.'); let fitFieldset = new FieldsetInfo('Fit', [ fit, padding ], ['fit']); const zoom = new FieldInfo('Zoom', 'zoom', 'number', 'the zoom level to set (likely want fit = false if set)'); const pan = new FieldInfo('Pan', 'pan', 'number', 'the pan level to set (likely want fit = false if set)'); const animate = new FieldInfo('Animate', "animate", 'boolean', "whether to transition the node positions"); const animationDuration = new FieldInfo("Animation Duration", 'animationDuration', 'number', "duration of animation in ms if enabled"); const animationEasing = new FieldInfo("Animation Easing", 'animationEasing', 'number', "easing of animation if enabled"); let animationFieldset = new FieldsetInfo('Animation', [ zoom, pan, animate, animationDuration, animationEasing ], ['animate']); let avoidOverlap = new FieldInfo('Avoid Overlap', 'avoidOverlap', 'boolean', 'prevents node overlap, may overflow boundingBox if not enough space'); let spacingFactor = new FieldInfo('Spacing Factor', 'spacingFactor', 'number', 'Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up'); let nodeDimensionsIncludeLabels = new FieldInfo('Node Dimensions Include Labels', 'nodeDimensionsIncludeLabels', 'boolean', 'Excludes the label when calculating node bounding boxes for the layout algorithm'); let shapedFieldset = new FieldsetInfo('Shaped', [ avoidOverlap, spacingFactor, nodeDimensionsIncludeLabels ], ['avoidOverlap']); let directed = new FieldInfo('Directed', 'breadthFirst', 'boolean', 'whether the tree is breadthFirst downwards (or edges can point in any direction if false)'); let circle = new FieldInfo('Circle', 'circle', 'boolean', 'put depths in concentric circles if true, put depths top down if false'); let maximalAdjustments = new FieldInfo('Maximal Adjustments', 'maximalAdjustments', 'number', 'how many times to try to position the nodes in a maximal way (i.e. no backtracking)'); let maximal = new FieldInfo('Maximal', 'maximal', 'boolean', 'whether to shift nodes down their natural BFS depths in order to avoid upwards edges (DAGS only)'); let grid = new FieldInfo('Grid', 'grid', 'boolean', 'whether to shift nodes down their natural BFS depths in order to avoid upwards edges (DAGS only)'); let roots = new FieldInfo('Roots', 'roots', 'string', 'the roots of the trees'); let breadthFirstFieldset = new FieldsetInfo('Breadth First', [ directed, circle, maximalAdjustments, maximal, grid, roots ], ['breadthFirst']); let nodeSep = new FieldInfo('Node Separation', 'nodeSep', 'number', 'the separation between adjacent nodes in the same rank'); let edgeSep = new FieldInfo('Edge Separation', 'edgeSep', 'number', 'the separation between adjacent edges in the same rank'); let rankSep = new FieldInfo('Rank Separation', 'rankSep', 'number', 'the separation between each rank in the layout'); let ranker = new FieldInfo('Ranker', 'ranker', 'options', 'Type of algorithm to assign a rank to each node in the input graph.'); ranker.options = [ { name: '', label: '' }, { name: 'network-simplex', label: 'network-simplex' }, { name: 'tight-tree', label: 'tight-tree' }, { name: 'longest-path', label: 'longest-path' } ]; let dagreFieldset = new FieldsetInfo('Dagre', [ nodeSep, edgeSep, rankSep, ranker ], ['nodeSep']); let animationThreshold = new FieldInfo('Animation Threshold', 'animationThreshold', 'number', 'The layout animates only after this many milliseconds when animate is true (prevents flashing on fast runs)'); let refresh = new FieldInfo('Refresh', 'refresh', 'number', 'Number of iterations between consecutive screen positions update'); let randomize = new FieldInfo('Randomize', 'randomize', 'boolean', 'Randomize the initial positions of the nodes (true) or use existing positions (false)'); let componentSpacing = new FieldInfo('Component Spacing', 'componentSpacing', 'number', 'Extra spacing between components in non-compound graphs'); let nodeOverlap = new FieldInfo('Node Overlap', 'nodeOverlap', 'number', 'Node repulsion (overlapping) multiplier'); let nestingFactor = new FieldInfo('Nesting Factor', 'nestingFactor', 'number', 'Nesting factor (multiplier) to compute ideal edge length for nested edges'); let gravity = new FieldInfo('Gravity', 'gravity', 'number', 'Gravity force (constant)'); let numIter = new FieldInfo('Max Iterations', 'numIter', 'number', 'Maximum number of iterations to perform'); let initialTemp = new FieldInfo('Initial Temp', 'initialTemp', 'number', 'Initial temperature (maximum node displacement)'); let coolingFactor = new FieldInfo('Cooling Factor', 'coolingFactor', 'number', 'Cooling factor (how the temperature is reduced between consecutive iterations'); let minTemp = new FieldInfo('Min. Temp', 'minTemp', 'number', 'Lower temperature threshold (below this point the layout will end)'); let coseFieldset = new FieldsetInfo('COSE', [ animationThreshold, refresh, randomize, componentSpacing, nodeOverlap, nestingFactor, gravity, numIter, initialTemp, coolingFactor, minTemp ], ['coolingFactor']); let avoidOverlapPadding = new FieldInfo('avoidOverlapPadding', 'avoidOverlapPadding', 'number', 'extra spacing around nodes when avoidOverlap: true'); let condense = new FieldInfo('condense', 'condense', 'boolean', 'uses all available space on false, uses minimal space on true'); let rows = new FieldInfo('Rows', 'rows', 'number', 'force num of rows in the grid'); let cols = new FieldInfo('Columns', 'cols', 'number', 'force num of columns in the grid'); let gridFieldset = new FieldsetInfo('Grid', [ avoidOverlapPadding, condense, rows, cols ], ['cols']); let radius = new FieldInfo('Radius', 'radius', 'number', 'the radius of the circle'); let startAngle = new FieldInfo('Start Angle', 'startAngle', 'number', 'where nodes start in radians (default:3 / 2 * Math.PI)'); let sweep = new FieldInfo('Sweep', 'sweep', 'number', 'how many radians should be between the first and last node (defaults to full circle)'); let clockwise = new FieldInfo('Clockwise', 'clockwise', 'number', 'whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)'); let circularFieldSet = new FieldsetInfo('Circular', [ radius, startAngle, sweep, clockwise ], ['clockwise']); let equidistant = new FieldInfo('Equidistant', 'equidistant', 'boolean', 'whether levels have an equal radial distance betwen them, may cause bounding box overflow'); let minNodeSpacing = new FieldInfo('Min. Node Spacing', 'minNodeSpacing', 'number', 'min spacing between outside of nodes (used for radius adjustment)'); let height = new FieldInfo('Height', 'height', 'number', ''); let width = new FieldInfo('Width', 'width', 'number', ''); let concentricFieldSet = new FieldsetInfo('Concentric', [ equidistant, minNodeSpacing, startAngle, height, width ], ['equidistant']); //boundingBox: undefined // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h } return new FormInfo('Layout', [ breadthFirstFieldset, coseFieldset, dagreFieldset, gridFieldset, circularFieldSet, concentricFieldSet, fitFieldset, animationFieldset, shapedFieldset ], false); } } CytoscapeLayoutToolComponent.LAYOUT_FORM_INFO = CytoscapeLayoutToolComponent.createLayoutFormInfo(); CytoscapeLayoutToolComponent.ɵfac = function CytoscapeLayoutToolComponent_Factory(t) { return new (t || CytoscapeLayoutToolComponent)(); }; CytoscapeLayoutToolComponent.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: CytoscapeLayoutToolComponent, selectors: [["cytoscape-layout-tool"]], viewQuery: function CytoscapeLayoutToolComponent_Query(rf, ctx) { if (rf & 1) { i0.ɵɵviewQuery(_c0$1, 5); } if (rf & 2) { let _t; i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.layoutForm = _t.first); } }, inputs: { layoutOptions: "layoutOptions" }, outputs: { layoutOptionsChange: "layoutOptionsChange" }, features: [i0.ɵɵNgOnChangesFeature], decls: 8, vars: 5, consts: [[2, "display", "flex"], [1, "layout-header"], ["name", "selectedLayoutInfo", "optionLabel", "name", 1, "layout-dropdown", 3, "options", "ngModel", "ngModelChange"], ["pButton", "", "label", "Apply", 1, "apply-button", 3, "disabled", "click"], [3, "model", "formInfo", "modelChange"]], template: function CytoscapeLayoutToolComponent_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelementStart(0, "div"); i0.ɵɵelementStart(1, "div", 0); i0.ɵɵelementStart(2, "div", 1); i0.ɵɵtext(3, "Edit Layout"); i0.ɵɵelementEnd(); i0.ɵɵelementEnd(); i0.ɵɵelementStart(4, "p-dropdown", 2); i0.ɵɵlistener("ngModelChange", function CytoscapeLayoutToolComponent_Template_p_dropdown_ngModelChange_4_listener($event) { return ctx.layoutOptions = $event; })("ngModelChange", function CytoscapeLayoutToolComponent_Template_p_dropdown_ngModelChange_4_listener() { return ctx.onLayoutModelChange(); }); i0.ɵɵtext(5, " >"); i0.ɵɵelementEnd(); i0.ɵɵelementStart(6, "button", 3); i0.ɵɵlistener("click", function CytoscapeLayoutToolComponent_Template_button_click_6_listener() { return ctx.onApplyLayout(); }); i0.ɵɵelementEnd(); i0.ɵɵelementEnd(); i0.ɵɵelementStart(7, "cyng-fluid-form", 4); i0.ɵɵlistener("modelChange", function CytoscapeLayoutToolComponent_Template_cyng_fluid_form_modelChange_7_listener() { return ctx.onFormModelChange(); }); i0.ɵɵelementEnd(); } if (rf & 2) { i0.ɵɵadvance(4); i0.ɵɵproperty("options", ctx.layoutOptionsList)("ngModel", ctx.layoutOptions); i0.ɵɵadvance(2); i0.ɵɵproperty("disabled", !ctx.changed); i0.ɵɵadvance(1); i0.ɵɵproperty("model", ctx.layoutOptions)("formInfo", ctx.formInfo); } }, directives: [i7.Dropdown, i1.NgControlStatus, i1.NgModel, i8.ButtonDirective, FluidFormComponent], styles: ["[_nghost-%COMP%] {\n width: 400px;\n height: 2em;\n }\n\n .layout-header[_ngcontent-%COMP%] {\n width: 100%;\n height: 20px;\n }\n\n .layout-dropdown[_ngcontent-%COMP%] {\n padding-right: 10px;\n }\n\n input[_ngcontent-%COMP%]:disabled {\n background-color: rgba(204, 204, 204, .33);\n }"] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(CytoscapeLayoutToolComponent, [{ type: Component, args: [{ selector: 'cytoscape-layout-tool', styles: [ ` :host { width: 400px; height: 2em; } .layout-header { width: 100%; height: 20px; } .layout-dropdown { padding-right: 10px; } input:disabled { background-color: rgba(204, 204, 204, .33); } ` ], template: ` <div> <div style="display: flex;"> <div class="layout-header">Edit Layout</div>