├── .editorconfig ├── .eslintrc.json ├── .gitignore ├── LICENSE ├── README.md ├── angular.json ├── dist └── ngx-flowchart │ ├── .npmignore │ ├── README.md │ ├── esm2022 │ ├── lib │ │ ├── connector.directive.mjs │ │ ├── default-node.component.mjs │ │ ├── edge-dragging.service.mjs │ │ ├── edge-drawing.service.mjs │ │ ├── magnet.directive.mjs │ │ ├── model.service.mjs │ │ ├── modelvalidation.service.mjs │ │ ├── mouseover.service.mjs │ │ ├── ngx-flowchart.component.mjs │ │ ├── ngx-flowchart.models.mjs │ │ ├── ngx-flowchart.module.mjs │ │ ├── node-dragging.service.mjs │ │ ├── node.component.mjs │ │ ├── rectangleselect.service.mjs │ │ └── scrollparent.mjs │ ├── ngx-flowchart.mjs │ └── public-api.mjs │ ├── fesm2022 │ ├── ngx-flowchart.mjs │ └── ngx-flowchart.mjs.map │ ├── index.d.ts │ ├── lib │ ├── connector.directive.d.ts │ ├── default-node.component.d.ts │ ├── edge-dragging.service.d.ts │ ├── edge-drawing.service.d.ts │ ├── magnet.directive.d.ts │ ├── model.service.d.ts │ ├── modelvalidation.service.d.ts │ ├── mouseover.service.d.ts │ ├── ngx-flowchart.component.d.ts │ ├── ngx-flowchart.models.d.ts │ ├── ngx-flowchart.module.d.ts │ ├── node-dragging.service.d.ts │ ├── node.component.d.ts │ ├── rectangleselect.service.d.ts │ └── scrollparent.d.ts │ ├── package.json │ └── public-api.d.ts ├── package.json ├── projects └── ngx-flowchart │ ├── .eslintrc.json │ ├── README.md │ ├── ng-package.json │ ├── package.json │ ├── src │ ├── lib │ │ ├── connector.directive.ts │ │ ├── default-node.component.html │ │ ├── default-node.component.scss │ │ ├── default-node.component.ts │ │ ├── edge-dragging.service.ts │ │ ├── edge-drawing.service.ts │ │ ├── magnet.directive.ts │ │ ├── model.service.ts │ │ ├── modelvalidation.service.ts │ │ ├── mouseover.service.ts │ │ ├── ngx-flowchart.component.html │ │ ├── ngx-flowchart.component.scss │ │ ├── ngx-flowchart.component.ts │ │ ├── ngx-flowchart.models.ts │ │ ├── ngx-flowchart.module.ts │ │ ├── node-dragging.service.ts │ │ ├── node.component.scss │ │ ├── node.component.ts │ │ ├── rectangleselect.service.ts │ │ └── scrollparent.ts │ └── public-api.ts │ ├── tsconfig.lib.json │ └── tsconfig.lib.prod.json ├── src ├── app │ ├── app.component.html │ ├── app.component.scss │ ├── app.component.ts │ ├── app.module.ts │ ├── test-node.component.html │ └── test-node.component.ts ├── assets │ └── .gitkeep ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── main.ts ├── polyfills.ts └── styles.scss ├── tsconfig.app.json ├── tsconfig.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "ignorePatterns": [ 4 | "projects/**/*" 5 | ], 6 | "overrides": [ 7 | { 8 | "files": [ 9 | "*.ts" 10 | ], 11 | "parserOptions": { 12 | "project": [ 13 | "tsconfig.json" 14 | ], 15 | "createDefaultProgram": true 16 | }, 17 | "extends": [ 18 | "plugin:@typescript-eslint/recommended", 19 | "plugin:@angular-eslint/recommended", 20 | "plugin:@angular-eslint/template/process-inline-templates" 21 | ], 22 | "rules": { 23 | "@angular-eslint/component-selector": [ 24 | "error", 25 | { 26 | "type": "element", 27 | "prefix": "app", 28 | "style": "kebab-case" 29 | } 30 | ], 31 | "@angular-eslint/directive-selector": [ 32 | "error", 33 | { 34 | "type": "attribute", 35 | "prefix": "app", 36 | "style": "camelCase" 37 | } 38 | ], 39 | "@typescript-eslint/explicit-member-accessibility": [ 40 | "off", 41 | { 42 | "accessibility": "explicit" 43 | } 44 | ], 45 | "@typescript-eslint/no-use-before-define": "error", 46 | "arrow-parens": [ 47 | "off", 48 | "always" 49 | ], 50 | "import/order": "off", 51 | "@typescript-eslint/member-ordering": "off" 52 | } 53 | }, 54 | { 55 | "files": [ 56 | "*.html" 57 | ], 58 | "extends": [ 59 | "plugin:@angular-eslint/template/recommended" 60 | ], 61 | "rules": {} 62 | } 63 | ] 64 | } 65 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /tmp 5 | /out-tsc 6 | # Only exists if Bazel was run 7 | /bazel-out 8 | 9 | # dependencies 10 | /node_modules 11 | 12 | # profiling files 13 | chrome-profiler-events.json 14 | speed-measure-plugin.json 15 | 16 | # IDEs and editors 17 | /.idea 18 | .project 19 | .classpath 20 | .c9/ 21 | *.launch 22 | .settings/ 23 | *.sublime-workspace 24 | 25 | # IDE - VSCode 26 | .vscode/* 27 | !.vscode/settings.json 28 | !.vscode/tasks.json 29 | !.vscode/launch.json 30 | !.vscode/extensions.json 31 | .history/* 32 | 33 | # misc 34 | /.angular/cache 35 | /.sass-cache 36 | /connect.lock 37 | /coverage 38 | /libpeerconnection.log 39 | npm-debug.log 40 | yarn-error.log 41 | testem.log 42 | /typings 43 | 44 | # System Files 45 | .DS_Store 46 | Thumbs.db 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2016 The Thingsboard Authors 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NgxFlowchart 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.0.1. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. 16 | 17 | ## Further help 18 | 19 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 20 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "ngx-flowchart-demo": { 7 | "root": "", 8 | "sourceRoot": "src", 9 | "projectType": "application", 10 | "prefix": "app", 11 | "schematics": { 12 | "@schematics/angular:component": { 13 | "style": "scss" 14 | }, 15 | "@schematics/angular:application": { 16 | "strict": true 17 | } 18 | }, 19 | "architect": { 20 | "build": { 21 | "builder": "@angular-devkit/build-angular:application", 22 | "options": { 23 | "outputPath": { 24 | "base": "dist/ngx-flowchart-demo" 25 | }, 26 | "index": "src/index.html", 27 | "polyfills": [ 28 | "src/polyfills.ts" 29 | ], 30 | "tsConfig": "tsconfig.app.json", 31 | "aot": true, 32 | "assets": [ 33 | "src/favicon.ico", 34 | "src/assets" 35 | ], 36 | "styles": [ 37 | "src/styles.scss" 38 | ], 39 | "scripts": [ 40 | "node_modules/jquery/dist/jquery.min.js" 41 | ], 42 | "browser": "src/main.ts" 43 | }, 44 | "configurations": { 45 | "production": { 46 | "fileReplacements": [ 47 | { 48 | "replace": "src/environments/environment.ts", 49 | "with": "src/environments/environment.prod.ts" 50 | } 51 | ], 52 | "optimization": true, 53 | "outputHashing": "all", 54 | "sourceMap": false, 55 | "namedChunks": false, 56 | "aot": true, 57 | "extractLicenses": true, 58 | "budgets": [ 59 | { 60 | "type": "initial", 61 | "maximumWarning": "2mb", 62 | "maximumError": "5mb" 63 | }, 64 | { 65 | "type": "anyComponentStyle", 66 | "maximumWarning": "6kb" 67 | } 68 | ] 69 | } 70 | } 71 | }, 72 | "serve": { 73 | "builder": "@angular-devkit/build-angular:dev-server", 74 | "options": { 75 | "buildTarget": "ngx-flowchart-demo:build" 76 | }, 77 | "configurations": { 78 | "production": { 79 | "buildTarget": "ngx-flowchart-demo:build:production" 80 | } 81 | } 82 | }, 83 | "extract-i18n": { 84 | "builder": "@angular-devkit/build-angular:extract-i18n", 85 | "options": { 86 | "buildTarget": "ngx-flowchart-demo:build" 87 | } 88 | }, 89 | "lint": { 90 | "builder": "@angular-eslint/builder:lint", 91 | "options": { 92 | "lintFilePatterns": [ 93 | "src/**/*.ts", 94 | "src/**/*.html" 95 | ] 96 | } 97 | } 98 | } 99 | }, 100 | "ngx-flowchart": { 101 | "projectType": "library", 102 | "root": "projects/ngx-flowchart", 103 | "sourceRoot": "projects/ngx-flowchart/src", 104 | "prefix": "fc", 105 | "schematics": { 106 | "@schematics/angular:component": { 107 | "style": "scss" 108 | } 109 | }, 110 | "architect": { 111 | "build": { 112 | "builder": "@angular-devkit/build-angular:ng-packagr", 113 | "options": { 114 | "project": "projects/ngx-flowchart/ng-package.json" 115 | }, 116 | "configurations": { 117 | "production": { 118 | "tsConfig": "projects/ngx-flowchart/tsconfig.lib.prod.json" 119 | }, 120 | "development": { 121 | "tsConfig": "projects/ngx-flowchart/tsconfig.lib.json" 122 | } 123 | }, 124 | "defaultConfiguration": "production" 125 | }, 126 | "test": { 127 | "builder": "@angular-devkit/build-angular:karma", 128 | "options": { 129 | "main": "projects/ngx-flowchart/src/test.ts", 130 | "tsConfig": "projects/ngx-flowchart/tsconfig.spec.json", 131 | "karmaConfig": "projects/ngx-flowchart/karma.conf.js" 132 | } 133 | }, 134 | "lint": { 135 | "builder": "@angular-eslint/builder:lint", 136 | "options": { 137 | "lintFilePatterns": [ 138 | "projects/ngx-flowchart/**/*.ts", 139 | "projects/ngx-flowchart/**/*.html" 140 | ] 141 | } 142 | } 143 | } 144 | } 145 | }, 146 | "cli": { 147 | "packageManager": "yarn", 148 | "analytics": false, 149 | "schematicCollections": [ 150 | "@angular-eslint/schematics" 151 | ] 152 | }, 153 | "schematics": { 154 | "@angular-eslint/schematics:application": { 155 | "setParserOptionsProject": true 156 | }, 157 | "@angular-eslint/schematics:library": { 158 | "setParserOptionsProject": true 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /dist/ngx-flowchart/.npmignore: -------------------------------------------------------------------------------- 1 | # Nested package.json's are only needed for development. 2 | **/package.json -------------------------------------------------------------------------------- /dist/ngx-flowchart/README.md: -------------------------------------------------------------------------------- 1 | # NgxFlowchart 2 | 3 | This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 14.2.0. 4 | 5 | ## Code scaffolding 6 | 7 | Run `ng generate component component-name --project ngx-flowchart` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project ngx-flowchart`. 8 | > Note: Don't forget to add `--project ngx-flowchart` or else it will be added to the default project in your `angular.json` file. 9 | 10 | ## Build 11 | 12 | Run `ng build ngx-flowchart` to build the project. The build artifacts will be stored in the `dist/` directory. 13 | 14 | ## Publishing 15 | 16 | After building your library with `ng build ngx-flowchart`, go to the dist folder `cd dist/ngx-flowchart` and run `npm publish`. 17 | 18 | ## Further help 19 | 20 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 21 | -------------------------------------------------------------------------------- /dist/ngx-flowchart/esm2022/lib/connector.directive.mjs: -------------------------------------------------------------------------------- 1 | import { Directive, ElementRef, HostListener, Input } from '@angular/core'; 2 | import { FlowchartConstants } from './ngx-flowchart.models'; 3 | import { FcModelService } from './model.service'; 4 | import * as i0 from "@angular/core"; 5 | export class FcConnectorDirective { 6 | constructor(elementRef) { 7 | this.elementRef = elementRef; 8 | } 9 | ngOnInit() { 10 | const element = $(this.elementRef.nativeElement); 11 | element.addClass(FlowchartConstants.connectorClass); 12 | if (this.modelservice.isEditable()) { 13 | element.attr('draggable', 'true'); 14 | this.updateConnectorClass(); 15 | } 16 | const connectorRectInfo = { 17 | type: this.connector.type, 18 | width: this.elementRef.nativeElement.offsetWidth, 19 | height: this.elementRef.nativeElement.offsetHeight, 20 | nodeRectInfo: this.nodeRectInfo 21 | }; 22 | this.modelservice.connectors.setConnectorRectInfo(this.connector.id, connectorRectInfo); 23 | } 24 | ngOnChanges(changes) { 25 | let updateConnector = false; 26 | for (const propName of Object.keys(changes)) { 27 | const change = changes[propName]; 28 | if (!change.firstChange && change.currentValue !== change.previousValue) { 29 | if (propName === 'mouseOverConnector') { 30 | updateConnector = true; 31 | } 32 | } 33 | } 34 | if (updateConnector && this.modelservice.isEditable()) { 35 | this.updateConnectorClass(); 36 | } 37 | } 38 | updateConnectorClass() { 39 | const element = $(this.elementRef.nativeElement); 40 | if (this.connector === this.mouseOverConnector) { 41 | element.addClass(FlowchartConstants.hoverClass); 42 | } 43 | else { 44 | element.removeClass(FlowchartConstants.hoverClass); 45 | } 46 | } 47 | dragover(event) { 48 | // Skip - conflict with magnet 49 | /* if (this.modelservice.isEditable()) { 50 | return this.callbacks.edgeDragoverConnector(event, this.connector); 51 | }*/ 52 | } 53 | drop(event) { 54 | if (this.modelservice.isEditable()) { 55 | return this.callbacks.edgeDrop(event, this.connector); 56 | } 57 | } 58 | dragend(event) { 59 | if (this.modelservice.isEditable()) { 60 | this.callbacks.edgeDragend(event); 61 | } 62 | } 63 | dragstart(event) { 64 | if (this.modelservice.isEditable()) { 65 | this.callbacks.edgeDragstart(event, this.connector); 66 | } 67 | } 68 | mouseenter(event) { 69 | if (this.modelservice.isEditable()) { 70 | this.callbacks.connectorMouseEnter(event, this.connector); 71 | } 72 | } 73 | mouseleave(event) { 74 | if (this.modelservice.isEditable()) { 75 | this.callbacks.connectorMouseLeave(event, this.connector); 76 | } 77 | } 78 | static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.6", ngImport: i0, type: FcConnectorDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); } 79 | static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.6", type: FcConnectorDirective, selector: "[fc-connector]", inputs: { callbacks: "callbacks", modelservice: "modelservice", connector: "connector", nodeRectInfo: "nodeRectInfo", mouseOverConnector: "mouseOverConnector" }, host: { listeners: { "dragover": "dragover($event)", "drop": "drop($event)", "dragend": "dragend($event)", "dragstart": "dragstart($event)", "mouseenter": "mouseenter($event)", "mouseleave": "mouseleave($event)" } }, usesOnChanges: true, ngImport: i0 }); } 80 | } 81 | i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.6", ngImport: i0, type: FcConnectorDirective, decorators: [{ 82 | type: Directive, 83 | args: [{ 84 | // eslint-disable-next-line @angular-eslint/directive-selector 85 | selector: '[fc-connector]' 86 | }] 87 | }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { callbacks: [{ 88 | type: Input 89 | }], modelservice: [{ 90 | type: Input 91 | }], connector: [{ 92 | type: Input 93 | }], nodeRectInfo: [{ 94 | type: Input 95 | }], mouseOverConnector: [{ 96 | type: Input 97 | }], dragover: [{ 98 | type: HostListener, 99 | args: ['dragover', ['$event']] 100 | }], drop: [{ 101 | type: HostListener, 102 | args: ['drop', ['$event']] 103 | }], dragend: [{ 104 | type: HostListener, 105 | args: ['dragend', ['$event']] 106 | }], dragstart: [{ 107 | type: HostListener, 108 | args: ['dragstart', ['$event']] 109 | }], mouseenter: [{ 110 | type: HostListener, 111 | args: ['mouseenter', ['$event']] 112 | }], mouseleave: [{ 113 | type: HostListener, 114 | args: ['mouseleave', ['$event']] 115 | }] } }); 116 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29ubmVjdG9yLmRpcmVjdGl2ZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3Byb2plY3RzL25neC1mbG93Y2hhcnQvc3JjL2xpYi9jb25uZWN0b3IuZGlyZWN0aXZlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQUUsVUFBVSxFQUFFLFlBQVksRUFBRSxLQUFLLEVBQW9DLE1BQU0sZUFBZSxDQUFDO0FBQzdHLE9BQU8sRUFBaUUsa0JBQWtCLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUMzSCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0saUJBQWlCLENBQUM7O0FBTWpELE1BQU0sT0FBTyxvQkFBb0I7SUFpQi9CLFlBQW1CLFVBQW1DO1FBQW5DLGVBQVUsR0FBVixVQUFVLENBQXlCO0lBQ3RELENBQUM7SUFFRCxRQUFRO1FBQ04sTUFBTSxPQUFPLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsYUFBYSxDQUFDLENBQUM7UUFDakQsT0FBTyxDQUFDLFFBQVEsQ0FBQyxrQkFBa0IsQ0FBQyxjQUFjLENBQUMsQ0FBQztRQUNwRCxJQUFJLElBQUksQ0FBQyxZQUFZLENBQUMsVUFBVSxFQUFFLEVBQUUsQ0FBQztZQUNuQyxPQUFPLENBQUMsSUFBSSxDQUFDLFdBQVcsRUFBRSxNQUFNLENBQUMsQ0FBQztZQUNsQyxJQUFJLENBQUMsb0JBQW9CLEVBQUUsQ0FBQztRQUM5QixDQUFDO1FBQ0QsTUFBTSxpQkFBaUIsR0FBd0I7WUFDN0MsSUFBSSxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSTtZQUN6QixLQUFLLEVBQUUsSUFBSSxDQUFDLFVBQVUsQ0FBQyxhQUFhLENBQUMsV0FBVztZQUNoRCxNQUFNLEVBQUUsSUFBSSxDQUFDLFVBQVUsQ0FBQyxhQUFhLENBQUMsWUFBWTtZQUNsRCxZQUFZLEVBQUUsSUFBSSxDQUFDLFlBQVk7U0FDaEMsQ0FBQztRQUNGLElBQUksQ0FBQyxZQUFZLENBQUMsVUFBVSxDQUFDLG9CQUFvQixDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsRUFBRSxFQUFFLGlCQUFpQixDQUFDLENBQUM7SUFDMUYsQ0FBQztJQUVELFdBQVcsQ0FBQyxPQUFzQjtRQUNoQyxJQUFJLGVBQWUsR0FBRyxLQUFLLENBQUM7UUFDNUIsS0FBSyxNQUFNLFFBQVEsSUFBSSxNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7WUFDNUMsTUFBTSxNQUFNLEdBQUcsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFDO1lBQ2pDLElBQUksQ0FBQyxNQUFNLENBQUMsV0FBVyxJQUFJLE1BQU0sQ0FBQyxZQUFZLEtBQUssTUFBTSxDQUFDLGFBQWEsRUFBRSxDQUFDO2dCQUN4RSxJQUFJLFFBQVEsS0FBSyxvQkFBb0IsRUFBRSxDQUFDO29CQUN0QyxlQUFlLEdBQUcsSUFBSSxDQUFDO2dCQUN6QixDQUFDO1lBQ0gsQ0FBQztRQUNILENBQUM7UUFDRCxJQUFJLGVBQWUsSUFBSSxJQUFJLENBQUMsWUFBWSxDQUFDLFVBQVUsRUFBRSxFQUFFLENBQUM7WUFDdEQsSUFBSSxDQUFDLG9CQUFvQixFQUFFLENBQUM7UUFDOUIsQ0FBQztJQUNILENBQUM7SUFFTyxvQkFBb0I7UUFDMUIsTUFBTSxPQUFPLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsYUFBYSxDQUFDLENBQUM7UUFDakQsSUFBSSxJQUFJLENBQUMsU0FBUyxLQUFLLElBQUksQ0FBQyxrQkFBa0IsRUFBRSxDQUFDO1lBQy9DLE9BQU8sQ0FBQyxRQUFRLENBQUMsa0JBQWtCLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDbEQsQ0FBQzthQUFNLENBQUM7WUFDTixPQUFPLENBQUMsV0FBVyxDQUFDLGtCQUFrQixDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQ3JELENBQUM7SUFDSCxDQUFDO0lBR0QsUUFBUSxDQUFDLEtBQWtCO1FBQ3pCLDhCQUE4QjtRQUM5Qjs7V0FFRztJQUNMLENBQUM7SUFHRCxJQUFJLENBQUMsS0FBa0I7UUFDckIsSUFBSSxJQUFJLENBQUMsWUFBWSxDQUFDLFVBQVUsRUFBRSxFQUFFLENBQUM7WUFDbkMsT0FBTyxJQUFJLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQ3hELENBQUM7SUFDSCxDQUFDO0lBR0QsT0FBTyxDQUFDLEtBQWtCO1FBQ3hCLElBQUksSUFBSSxDQUFDLFlBQVksQ0FBQyxVQUFVLEVBQUUsRUFBRSxDQUFDO1lBQ25DLElBQUksQ0FBQyxTQUFTLENBQUMsV0FBVyxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQ3BDLENBQUM7SUFDSCxDQUFDO0lBR0QsU0FBUyxDQUFDLEtBQWtCO1FBQzFCLElBQUksSUFBSSxDQUFDLFlBQVksQ0FBQyxVQUFVLEVBQUUsRUFBRSxDQUFDO1lBQ25DLElBQUksQ0FBQyxTQUFTLENBQUMsYUFBYSxDQUFDLEtBQUssRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDdEQsQ0FBQztJQUNILENBQUM7SUFHRCxVQUFVLENBQUMsS0FBaUI7UUFDMUIsSUFBSSxJQUFJLENBQUMsWUFBWSxDQUFDLFVBQVUsRUFBRSxFQUFFLENBQUM7WUFDbkMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxtQkFBbUIsQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQzVELENBQUM7SUFDSCxDQUFDO0lBR0QsVUFBVSxDQUFDLEtBQWlCO1FBQzFCLElBQUksSUFBSSxDQUFDLFlBQVksQ0FBQyxVQUFVLEVBQUUsRUFBRSxDQUFDO1lBQ25DLElBQUksQ0FBQyxTQUFTLENBQUMsbUJBQW1CLENBQUMsS0FBSyxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUM1RCxDQUFDO0lBQ0gsQ0FBQzs4R0FyR1Usb0JBQW9CO2tHQUFwQixvQkFBb0I7OzJGQUFwQixvQkFBb0I7a0JBSmhDLFNBQVM7bUJBQUM7b0JBQ1QsOERBQThEO29CQUM5RCxRQUFRLEVBQUUsZ0JBQWdCO2lCQUMzQjsrRUFJQyxTQUFTO3NCQURSLEtBQUs7Z0JBSU4sWUFBWTtzQkFEWCxLQUFLO2dCQUlOLFNBQVM7c0JBRFIsS0FBSztnQkFJTixZQUFZO3NCQURYLEtBQUs7Z0JBSU4sa0JBQWtCO3NCQURqQixLQUFLO2dCQStDTixRQUFRO3NCQURQLFlBQVk7dUJBQUMsVUFBVSxFQUFFLENBQUMsUUFBUSxDQUFDO2dCQVNwQyxJQUFJO3NCQURILFlBQVk7dUJBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxDQUFDO2dCQVFoQyxPQUFPO3NCQUROLFlBQVk7dUJBQUMsU0FBUyxFQUFFLENBQUMsUUFBUSxDQUFDO2dCQVFuQyxTQUFTO3NCQURSLFlBQVk7dUJBQUMsV0FBVyxFQUFFLENBQUMsUUFBUSxDQUFDO2dCQVFyQyxVQUFVO3NCQURULFlBQVk7dUJBQUMsWUFBWSxFQUFFLENBQUMsUUFBUSxDQUFDO2dCQVF0QyxVQUFVO3NCQURULFlBQVk7dUJBQUMsWUFBWSxFQUFFLENBQUMsUUFBUSxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgRGlyZWN0aXZlLCBFbGVtZW50UmVmLCBIb3N0TGlzdGVuZXIsIElucHV0LCBPbkNoYW5nZXMsIE9uSW5pdCwgU2ltcGxlQ2hhbmdlcyB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgRmNDYWxsYmFja3MsIEZjQ29ubmVjdG9yLCBGY0Nvbm5lY3RvclJlY3RJbmZvLCBGY05vZGVSZWN0SW5mbywgRmxvd2NoYXJ0Q29uc3RhbnRzIH0gZnJvbSAnLi9uZ3gtZmxvd2NoYXJ0Lm1vZGVscyc7XG5pbXBvcnQgeyBGY01vZGVsU2VydmljZSB9IGZyb20gJy4vbW9kZWwuc2VydmljZSc7XG5cbkBEaXJlY3RpdmUoe1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQGFuZ3VsYXItZXNsaW50L2RpcmVjdGl2ZS1zZWxlY3RvclxuICBzZWxlY3RvcjogJ1tmYy1jb25uZWN0b3JdJ1xufSlcbmV4cG9ydCBjbGFzcyBGY0Nvbm5lY3RvckRpcmVjdGl2ZSBpbXBsZW1lbnRzIE9uSW5pdCwgT25DaGFuZ2VzIHtcblxuICBASW5wdXQoKVxuICBjYWxsYmFja3M6IEZjQ2FsbGJhY2tzO1xuXG4gIEBJbnB1dCgpXG4gIG1vZGVsc2VydmljZTogRmNNb2RlbFNlcnZpY2U7XG5cbiAgQElucHV0KClcbiAgY29ubmVjdG9yOiBGY0Nvbm5lY3RvcjtcblxuICBASW5wdXQoKVxuICBub2RlUmVjdEluZm86IEZjTm9kZVJlY3RJbmZvO1xuXG4gIEBJbnB1dCgpXG4gIG1vdXNlT3ZlckNvbm5lY3RvcjogRmNDb25uZWN0b3I7XG5cbiAgY29uc3RydWN0b3IocHVibGljIGVsZW1lbnRSZWY6IEVsZW1lbnRSZWY8SFRNTEVsZW1lbnQ+KSB7XG4gIH1cblxuICBuZ09uSW5pdCgpOiB2b2lkIHtcbiAgICBjb25zdCBlbGVtZW50ID0gJCh0aGlzLmVsZW1lbnRSZWYubmF0aXZlRWxlbWVudCk7XG4gICAgZWxlbWVudC5hZGRDbGFzcyhGbG93Y2hhcnRDb25zdGFudHMuY29ubmVjdG9yQ2xhc3MpO1xuICAgIGlmICh0aGlzLm1vZGVsc2VydmljZS5pc0VkaXRhYmxlKCkpIHtcbiAgICAgIGVsZW1lbnQuYXR0cignZHJhZ2dhYmxlJywgJ3RydWUnKTtcbiAgICAgIHRoaXMudXBkYXRlQ29ubmVjdG9yQ2xhc3MoKTtcbiAgICB9XG4gICAgY29uc3QgY29ubmVjdG9yUmVjdEluZm86IEZjQ29ubmVjdG9yUmVjdEluZm8gPSB7XG4gICAgICB0eXBlOiB0aGlzLmNvbm5lY3Rvci50eXBlLFxuICAgICAgd2lkdGg6IHRoaXMuZWxlbWVudFJlZi5uYXRpdmVFbGVtZW50Lm9mZnNldFdpZHRoLFxuICAgICAgaGVpZ2h0OiB0aGlzLmVsZW1lbnRSZWYubmF0aXZlRWxlbWVudC5vZmZzZXRIZWlnaHQsXG4gICAgICBub2RlUmVjdEluZm86IHRoaXMubm9kZVJlY3RJbmZvXG4gICAgfTtcbiAgICB0aGlzLm1vZGVsc2VydmljZS5jb25uZWN0b3JzLnNldENvbm5lY3RvclJlY3RJbmZvKHRoaXMuY29ubmVjdG9yLmlkLCBjb25uZWN0b3JSZWN0SW5mbyk7XG4gIH1cblxuICBuZ09uQ2hhbmdlcyhjaGFuZ2VzOiBTaW1wbGVDaGFuZ2VzKTogdm9pZCB7XG4gICAgbGV0IHVwZGF0ZUNvbm5lY3RvciA9IGZhbHNlO1xuICAgIGZvciAoY29uc3QgcHJvcE5hbWUgb2YgT2JqZWN0LmtleXMoY2hhbmdlcykpIHtcbiAgICAgIGNvbnN0IGNoYW5nZSA9IGNoYW5nZXNbcHJvcE5hbWVdO1xuICAgICAgaWYgKCFjaGFuZ2UuZmlyc3RDaGFuZ2UgJiYgY2hhbmdlLmN1cnJlbnRWYWx1ZSAhPT0gY2hhbmdlLnByZXZpb3VzVmFsdWUpIHtcbiAgICAgICAgaWYgKHByb3BOYW1lID09PSAnbW91c2VPdmVyQ29ubmVjdG9yJykge1xuICAgICAgICAgIHVwZGF0ZUNvbm5lY3RvciA9IHRydWU7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKHVwZGF0ZUNvbm5lY3RvciAmJiB0aGlzLm1vZGVsc2VydmljZS5pc0VkaXRhYmxlKCkpIHtcbiAgICAgIHRoaXMudXBkYXRlQ29ubmVjdG9yQ2xhc3MoKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIHVwZGF0ZUNvbm5lY3RvckNsYXNzKCkge1xuICAgIGNvbnN0IGVsZW1lbnQgPSAkKHRoaXMuZWxlbWVudFJlZi5uYXRpdmVFbGVtZW50KTtcbiAgICBpZiAodGhpcy5jb25uZWN0b3IgPT09IHRoaXMubW91c2VPdmVyQ29ubmVjdG9yKSB7XG4gICAgICBlbGVtZW50LmFkZENsYXNzKEZsb3djaGFydENvbnN0YW50cy5ob3ZlckNsYXNzKTtcbiAgICB9IGVsc2Uge1xuICAgICAgZWxlbWVudC5yZW1vdmVDbGFzcyhGbG93Y2hhcnRDb25zdGFudHMuaG92ZXJDbGFzcyk7XG4gICAgfVxuICB9XG5cbiAgQEhvc3RMaXN0ZW5lcignZHJhZ292ZXInLCBbJyRldmVudCddKVxuICBkcmFnb3ZlcihldmVudDogRXZlbnQgfCBhbnkpIHtcbiAgICAvLyBTa2lwIC0gY29uZmxpY3Qgd2l0aCBtYWduZXRcbiAgICAvKiBpZiAodGhpcy5tb2RlbHNlcnZpY2UuaXNFZGl0YWJsZSgpKSB7XG4gICAgICByZXR1cm4gdGhpcy5jYWxsYmFja3MuZWRnZURyYWdvdmVyQ29ubmVjdG9yKGV2ZW50LCB0aGlzLmNvbm5lY3Rvcik7XG4gICAgfSovXG4gIH1cblxuICBASG9zdExpc3RlbmVyKCdkcm9wJywgWyckZXZlbnQnXSlcbiAgZHJvcChldmVudDogRXZlbnQgfCBhbnkpIHtcbiAgICBpZiAodGhpcy5tb2RlbHNlcnZpY2UuaXNFZGl0YWJsZSgpKSB7XG4gICAgICByZXR1cm4gdGhpcy5jYWxsYmFja3MuZWRnZURyb3AoZXZlbnQsIHRoaXMuY29ubmVjdG9yKTtcbiAgICB9XG4gIH1cblxuICBASG9zdExpc3RlbmVyKCdkcmFnZW5kJywgWyckZXZlbnQnXSlcbiAgZHJhZ2VuZChldmVudDogRXZlbnQgfCBhbnkpIHtcbiAgICBpZiAodGhpcy5tb2RlbHNlcnZpY2UuaXNFZGl0YWJsZSgpKSB7XG4gICAgICB0aGlzLmNhbGxiYWNrcy5lZGdlRHJhZ2VuZChldmVudCk7XG4gICAgfVxuICB9XG5cbiAgQEhvc3RMaXN0ZW5lcignZHJhZ3N0YXJ0JywgWyckZXZlbnQnXSlcbiAgZHJhZ3N0YXJ0KGV2ZW50OiBFdmVudCB8IGFueSkge1xuICAgIGlmICh0aGlzLm1vZGVsc2VydmljZS5pc0VkaXRhYmxlKCkpIHtcbiAgICAgIHRoaXMuY2FsbGJhY2tzLmVkZ2VEcmFnc3RhcnQoZXZlbnQsIHRoaXMuY29ubmVjdG9yKTtcbiAgICB9XG4gIH1cblxuICBASG9zdExpc3RlbmVyKCdtb3VzZWVudGVyJywgWyckZXZlbnQnXSlcbiAgbW91c2VlbnRlcihldmVudDogTW91c2VFdmVudCkge1xuICAgIGlmICh0aGlzLm1vZGVsc2VydmljZS5pc0VkaXRhYmxlKCkpIHtcbiAgICAgIHRoaXMuY2FsbGJhY2tzLmNvbm5lY3Rvck1vdXNlRW50ZXIoZXZlbnQsIHRoaXMuY29ubmVjdG9yKTtcbiAgICB9XG4gIH1cblxuICBASG9zdExpc3RlbmVyKCdtb3VzZWxlYXZlJywgWyckZXZlbnQnXSlcbiAgbW91c2VsZWF2ZShldmVudDogTW91c2VFdmVudCkge1xuICAgIGlmICh0aGlzLm1vZGVsc2VydmljZS5pc0VkaXRhYmxlKCkpIHtcbiAgICAgIHRoaXMuY2FsbGJhY2tzLmNvbm5lY3Rvck1vdXNlTGVhdmUoZXZlbnQsIHRoaXMuY29ubmVjdG9yKTtcbiAgICB9XG4gIH1cblxufVxuIl19 -------------------------------------------------------------------------------- /dist/ngx-flowchart/esm2022/lib/default-node.component.mjs: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { FcNodeComponent } from './node.component'; 3 | import * as i0 from "@angular/core"; 4 | import * as i1 from "@angular/common"; 5 | import * as i2 from "./magnet.directive"; 6 | import * as i3 from "./connector.directive"; 7 | export class DefaultFcNodeComponent extends FcNodeComponent { 8 | constructor() { 9 | super(); 10 | } 11 | static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.6", ngImport: i0, type: DefaultFcNodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } 12 | static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.6", type: DefaultFcNodeComponent, selector: "fc-default-node", usesInheritance: true, ngImport: i0, template: "\n
\n
\n

{{ node.name }}

\n\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n
\n
\n ×\n
\n\n", styles: [":host .fc-node-overlay{position:absolute;pointer-events:none;inset:0;background-color:#000;opacity:0}:host :host-context(.fc-hover) .fc-node-overlay{opacity:.25;transition:opacity .2s}:host :host-context(.fc-selected) .fc-node-overlay{opacity:.25}:host .innerNode{display:flex;justify-content:center;align-items:center;min-width:100px;border-radius:5px;background-color:#f15b26;color:#fff;font-size:16px;pointer-events:none}:host .innerNode p{padding:0 15px;text-align:center}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.FcMagnetDirective, selector: "[fc-magnet]", inputs: ["callbacks", "connector"] }, { kind: "directive", type: i3.FcConnectorDirective, selector: "[fc-connector]", inputs: ["callbacks", "modelservice", "connector", "nodeRectInfo", "mouseOverConnector"] }] }); } 13 | } 14 | i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.6", ngImport: i0, type: DefaultFcNodeComponent, decorators: [{ 15 | type: Component, 16 | args: [{ selector: 'fc-default-node', template: "\n
\n
\n

{{ node.name }}

\n\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n
\n
\n ×\n
\n\n", styles: [":host .fc-node-overlay{position:absolute;pointer-events:none;inset:0;background-color:#000;opacity:0}:host :host-context(.fc-hover) .fc-node-overlay{opacity:.25;transition:opacity .2s}:host :host-context(.fc-selected) .fc-node-overlay{opacity:.25}:host .innerNode{display:flex;justify-content:center;align-items:center;min-width:100px;border-radius:5px;background-color:#f15b26;color:#fff;font-size:16px;pointer-events:none}:host .innerNode p{padding:0 15px;text-align:center}\n"] }] 17 | }], ctorParameters: () => [] }); 18 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVmYXVsdC1ub2RlLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3Byb2plY3RzL25neC1mbG93Y2hhcnQvc3JjL2xpYi9kZWZhdWx0LW5vZGUuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vcHJvamVjdHMvbmd4LWZsb3djaGFydC9zcmMvbGliL2RlZmF1bHQtbm9kZS5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQzFDLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQzs7Ozs7QUFRbkQsTUFBTSxPQUFPLHNCQUF1QixTQUFRLGVBQWU7SUFFekQ7UUFDRSxLQUFLLEVBQUUsQ0FBQztJQUNWLENBQUM7OEdBSlUsc0JBQXNCO2tHQUF0QixzQkFBc0IsOEVDVG5DLGduREFrQ0E7OzJGRHpCYSxzQkFBc0I7a0JBTmxDLFNBQVM7K0JBRUUsaUJBQWlCIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tcG9uZW50IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBGY05vZGVDb21wb25lbnQgfSBmcm9tICcuL25vZGUuY29tcG9uZW50JztcblxuQENvbXBvbmVudCh7XG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAYW5ndWxhci1lc2xpbnQvY29tcG9uZW50LXNlbGVjdG9yXG4gIHNlbGVjdG9yOiAnZmMtZGVmYXVsdC1ub2RlJyxcbiAgdGVtcGxhdGVVcmw6ICcuL2RlZmF1bHQtbm9kZS5jb21wb25lbnQuaHRtbCcsXG4gIHN0eWxlVXJsczogWycuL2RlZmF1bHQtbm9kZS5jb21wb25lbnQuc2NzcyddXG59KVxuZXhwb3J0IGNsYXNzIERlZmF1bHRGY05vZGVDb21wb25lbnQgZXh0ZW5kcyBGY05vZGVDb21wb25lbnQge1xuXG4gIGNvbnN0cnVjdG9yKCkge1xuICAgIHN1cGVyKCk7XG4gIH1cblxufVxuIiwiPGRpdlxuICAoZGJsY2xpY2spPVwidXNlck5vZGVDYWxsYmFja3MuZG91YmxlQ2xpY2soJGV2ZW50LCBub2RlKVwiPlxuICA8ZGl2IGNsYXNzPVwie3tmbG93Y2hhcnRDb25zdGFudHMubm9kZU92ZXJsYXlDbGFzc319XCI+PC9kaXY+XG4gIDxkaXYgY2xhc3M9XCJpbm5lck5vZGVcIj5cbiAgICA8cD57eyBub2RlLm5hbWUgfX08L3A+XG5cbiAgICA8ZGl2IGNsYXNzPVwie3tmbG93Y2hhcnRDb25zdGFudHMubGVmdENvbm5lY3RvckNsYXNzfX1cIj5cbiAgICAgIDxkaXYgZmMtbWFnbmV0IFtjb25uZWN0b3JdPVwiY29ubmVjdG9yXCIgW2NhbGxiYWNrc109XCJjYWxsYmFja3NcIlxuICAgICAgICAgICAqbmdGb3I9XCJsZXQgY29ubmVjdG9yIG9mIG1vZGVsc2VydmljZS5ub2Rlcy5nZXRDb25uZWN0b3JzQnlUeXBlKG5vZGUsIGZsb3djaGFydENvbnN0YW50cy5sZWZ0Q29ubmVjdG9yVHlwZSlcIj5cbiAgICAgICAgPGRpdiBmYy1jb25uZWN0b3IgW2Nvbm5lY3Rvcl09XCJjb25uZWN0b3JcIlxuICAgICAgICAgICAgIFtub2RlUmVjdEluZm9dPVwibm9kZVJlY3RJbmZvXCJcbiAgICAgICAgICAgICBbbW91c2VPdmVyQ29ubmVjdG9yXT1cIm1vdXNlT3ZlckNvbm5lY3RvclwiXG4gICAgICAgICAgICAgW2NhbGxiYWNrc109XCJjYWxsYmFja3NcIlxuICAgICAgICAgICAgIFttb2RlbHNlcnZpY2VdPVwibW9kZWxzZXJ2aWNlXCI+PC9kaXY+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cbiAgICA8ZGl2IGNsYXNzPVwie3tmbG93Y2hhcnRDb25zdGFudHMucmlnaHRDb25uZWN0b3JDbGFzc319XCI+XG4gICAgICA8ZGl2IGZjLW1hZ25ldCBbY29ubmVjdG9yXT1cImNvbm5lY3RvclwiIFtjYWxsYmFja3NdPVwiY2FsbGJhY2tzXCJcbiAgICAgICAgICAgKm5nRm9yPVwibGV0IGNvbm5lY3RvciBvZiBtb2RlbHNlcnZpY2Uubm9kZXMuZ2V0Q29ubmVjdG9yc0J5VHlwZShub2RlLCBmbG93Y2hhcnRDb25zdGFudHMucmlnaHRDb25uZWN0b3JUeXBlKVwiPlxuICAgICAgICA8ZGl2IGZjLWNvbm5lY3RvciBbY29ubmVjdG9yXT1cImNvbm5lY3RvclwiXG4gICAgICAgICAgICAgW25vZGVSZWN0SW5mb109XCJub2RlUmVjdEluZm9cIlxuICAgICAgICAgICAgIFttb3VzZU92ZXJDb25uZWN0b3JdPVwibW91c2VPdmVyQ29ubmVjdG9yXCJcbiAgICAgICAgICAgICBbY2FsbGJhY2tzXT1cImNhbGxiYWNrc1wiXG4gICAgICAgICAgICAgW21vZGVsc2VydmljZV09XCJtb2RlbHNlcnZpY2VcIj48L2Rpdj5cbiAgICAgIDwvZGl2PlxuICAgIDwvZGl2PlxuICA8L2Rpdj5cbiAgPGRpdiAqbmdJZj1cIm1vZGVsc2VydmljZS5pc0VkaXRhYmxlKCkgJiYgIW5vZGUucmVhZG9ubHlcIiBjbGFzcz1cImZjLW5vZGVlZGl0XCIgKGNsaWNrKT1cInVzZXJOb2RlQ2FsbGJhY2tzLm5vZGVFZGl0KCRldmVudCwgbm9kZSlcIj5cbiAgICA8aSBjbGFzcz1cImZhIGZhLXBlbmNpbFwiIGFyaWEtaGlkZGVuPVwidHJ1ZVwiPjwvaT5cbiAgPC9kaXY+XG4gIDxkaXYgKm5nSWY9XCJtb2RlbHNlcnZpY2UuaXNFZGl0YWJsZSgpICYmICFub2RlLnJlYWRvbmx5XCIgY2xhc3M9XCJmYy1ub2RlZGVsZXRlXCIgKGNsaWNrKT1cIm1vZGVsc2VydmljZS5ub2Rlcy5kZWxldGUobm9kZSlcIj5cbiAgICAmdGltZXM7XG4gIDwvZGl2PlxuPC9kaXY+XG4iXX0= -------------------------------------------------------------------------------- /dist/ngx-flowchart/esm2022/lib/edge-drawing.service.mjs: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { FlowchartConstants } from './ngx-flowchart.models'; 3 | import * as i0 from "@angular/core"; 4 | export class FcEdgeDrawingService { 5 | constructor() { 6 | } 7 | getEdgeDAttribute(pt1, pt2, style) { 8 | let dAddribute = `M ${pt1.x}, ${pt1.y} `; 9 | if (style === FlowchartConstants.curvedStyle) { 10 | const sourceTangent = this.computeEdgeSourceTangent(pt1, pt2); 11 | const destinationTangent = this.computeEdgeDestinationTangent(pt1, pt2); 12 | dAddribute += `C ${sourceTangent.x}, ${sourceTangent.y} ${(destinationTangent.x - 50)}, ${destinationTangent.y} ${pt2.x}, ${pt2.y}`; 13 | } 14 | else { 15 | dAddribute += `L ${pt2.x}, ${pt2.y}`; 16 | } 17 | return dAddribute; 18 | } 19 | getEdgeCenter(pt1, pt2) { 20 | return { 21 | x: (pt1.x + pt2.x) / 2, 22 | y: (pt1.y + pt2.y) / 2 23 | }; 24 | } 25 | computeEdgeTangentOffset(pt1, pt2) { 26 | return (pt2.y - pt1.y) / 2; 27 | } 28 | computeEdgeSourceTangent(pt1, pt2) { 29 | return { 30 | x: pt1.x, 31 | y: pt1.y + this.computeEdgeTangentOffset(pt1, pt2) 32 | }; 33 | } 34 | computeEdgeDestinationTangent(pt1, pt2) { 35 | return { 36 | x: pt2.x, 37 | y: pt2.y - this.computeEdgeTangentOffset(pt1, pt2) 38 | }; 39 | } 40 | static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.6", ngImport: i0, type: FcEdgeDrawingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } 41 | static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.6", ngImport: i0, type: FcEdgeDrawingService }); } 42 | } 43 | i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.6", ngImport: i0, type: FcEdgeDrawingService, decorators: [{ 44 | type: Injectable 45 | }], ctorParameters: () => [] }); 46 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZWRnZS1kcmF3aW5nLnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9wcm9qZWN0cy9uZ3gtZmxvd2NoYXJ0L3NyYy9saWIvZWRnZS1kcmF3aW5nLnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUMzQyxPQUFPLEVBQVksa0JBQWtCLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQzs7QUFHdEUsTUFBTSxPQUFPLG9CQUFvQjtJQUUvQjtJQUNBLENBQUM7SUFFTSxpQkFBaUIsQ0FBQyxHQUFhLEVBQUUsR0FBYSxFQUFFLEtBQWE7UUFDbEUsSUFBSSxVQUFVLEdBQUcsS0FBSyxHQUFHLENBQUMsQ0FBQyxLQUFLLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQztRQUN6QyxJQUFJLEtBQUssS0FBSyxrQkFBa0IsQ0FBQyxXQUFXLEVBQUUsQ0FBQztZQUM3QyxNQUFNLGFBQWEsR0FBRyxJQUFJLENBQUMsd0JBQXdCLENBQUMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxDQUFDO1lBQzlELE1BQU0sa0JBQWtCLEdBQUcsSUFBSSxDQUFDLDZCQUE2QixDQUFDLEdBQUcsRUFBRSxHQUFHLENBQUMsQ0FBQztZQUN4RSxVQUFVLElBQUksS0FBSyxhQUFhLENBQUMsQ0FBQyxLQUFLLGFBQWEsQ0FBQyxDQUFDLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDLEdBQUcsRUFBRSxDQUFDLEtBQUssa0JBQWtCLENBQUMsQ0FBQyxJQUFJLEdBQUcsQ0FBQyxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQ3RJLENBQUM7YUFBTSxDQUFDO1lBQ04sVUFBVSxJQUFJLEtBQUssR0FBRyxDQUFDLENBQUMsS0FBSyxHQUFHLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDdkMsQ0FBQztRQUNELE9BQU8sVUFBVSxDQUFDO0lBQ3BCLENBQUM7SUFFTSxhQUFhLENBQUMsR0FBYSxFQUFFLEdBQWE7UUFDL0MsT0FBTztZQUNMLENBQUMsRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsR0FBRyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUM7WUFDdEIsQ0FBQyxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxHQUFHLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQztTQUN2QixDQUFDO0lBQ0osQ0FBQztJQUVPLHdCQUF3QixDQUFDLEdBQWEsRUFBRSxHQUFhO1FBQzNELE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLEdBQUcsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDN0IsQ0FBQztJQUVPLHdCQUF3QixDQUFDLEdBQWEsRUFBRSxHQUFhO1FBQzNELE9BQU87WUFDTCxDQUFDLEVBQUUsR0FBRyxDQUFDLENBQUM7WUFDUixDQUFDLEVBQUUsR0FBRyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsd0JBQXdCLENBQUMsR0FBRyxFQUFFLEdBQUcsQ0FBQztTQUNuRCxDQUFDO0lBQ0osQ0FBQztJQUVPLDZCQUE2QixDQUFDLEdBQWEsRUFBRSxHQUFhO1FBQ2hFLE9BQU87WUFDTCxDQUFDLEVBQUUsR0FBRyxDQUFDLENBQUM7WUFDUixDQUFDLEVBQUUsR0FBRyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsd0JBQXdCLENBQUMsR0FBRyxFQUFFLEdBQUcsQ0FBQztTQUNuRCxDQUFDO0lBQ0osQ0FBQzs4R0F4Q1Usb0JBQW9CO2tIQUFwQixvQkFBb0I7OzJGQUFwQixvQkFBb0I7a0JBRGhDLFVBQVUiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBJbmplY3RhYmxlIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBGY0Nvb3JkcywgRmxvd2NoYXJ0Q29uc3RhbnRzIH0gZnJvbSAnLi9uZ3gtZmxvd2NoYXJ0Lm1vZGVscyc7XG5cbkBJbmplY3RhYmxlKClcbmV4cG9ydCBjbGFzcyBGY0VkZ2VEcmF3aW5nU2VydmljZSB7XG5cbiAgY29uc3RydWN0b3IoKSB7XG4gIH1cblxuICBwdWJsaWMgZ2V0RWRnZURBdHRyaWJ1dGUocHQxOiBGY0Nvb3JkcywgcHQyOiBGY0Nvb3Jkcywgc3R5bGU6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgbGV0IGRBZGRyaWJ1dGUgPSBgTSAke3B0MS54fSwgJHtwdDEueX0gYDtcbiAgICBpZiAoc3R5bGUgPT09IEZsb3djaGFydENvbnN0YW50cy5jdXJ2ZWRTdHlsZSkge1xuICAgICAgY29uc3Qgc291cmNlVGFuZ2VudCA9IHRoaXMuY29tcHV0ZUVkZ2VTb3VyY2VUYW5nZW50KHB0MSwgcHQyKTtcbiAgICAgIGNvbnN0IGRlc3RpbmF0aW9uVGFuZ2VudCA9IHRoaXMuY29tcHV0ZUVkZ2VEZXN0aW5hdGlvblRhbmdlbnQocHQxLCBwdDIpO1xuICAgICAgZEFkZHJpYnV0ZSArPSBgQyAke3NvdXJjZVRhbmdlbnQueH0sICR7c291cmNlVGFuZ2VudC55fSAkeyhkZXN0aW5hdGlvblRhbmdlbnQueCAtIDUwKX0sICR7ZGVzdGluYXRpb25UYW5nZW50Lnl9ICR7cHQyLnh9LCAke3B0Mi55fWA7XG4gICAgfSBlbHNlIHtcbiAgICAgIGRBZGRyaWJ1dGUgKz0gYEwgJHtwdDIueH0sICR7cHQyLnl9YDtcbiAgICB9XG4gICAgcmV0dXJuIGRBZGRyaWJ1dGU7XG4gIH1cblxuICBwdWJsaWMgZ2V0RWRnZUNlbnRlcihwdDE6IEZjQ29vcmRzLCBwdDI6IEZjQ29vcmRzKTogRmNDb29yZHMge1xuICAgIHJldHVybiB7XG4gICAgICB4OiAocHQxLnggKyBwdDIueCkgLyAyLFxuICAgICAgeTogKHB0MS55ICsgcHQyLnkpIC8gMlxuICAgIH07XG4gIH1cblxuICBwcml2YXRlIGNvbXB1dGVFZGdlVGFuZ2VudE9mZnNldChwdDE6IEZjQ29vcmRzLCBwdDI6IEZjQ29vcmRzKTogbnVtYmVyIHtcbiAgICByZXR1cm4gKHB0Mi55IC0gcHQxLnkpIC8gMjtcbiAgfVxuXG4gIHByaXZhdGUgY29tcHV0ZUVkZ2VTb3VyY2VUYW5nZW50KHB0MTogRmNDb29yZHMsIHB0MjogRmNDb29yZHMpOiBGY0Nvb3JkcyB7XG4gICAgcmV0dXJuIHtcbiAgICAgIHg6IHB0MS54LFxuICAgICAgeTogcHQxLnkgKyB0aGlzLmNvbXB1dGVFZGdlVGFuZ2VudE9mZnNldChwdDEsIHB0MilcbiAgICB9O1xuICB9XG5cbiAgcHJpdmF0ZSBjb21wdXRlRWRnZURlc3RpbmF0aW9uVGFuZ2VudChwdDE6IEZjQ29vcmRzLCBwdDI6IEZjQ29vcmRzKTogRmNDb29yZHMge1xuICAgIHJldHVybiB7XG4gICAgICB4OiBwdDIueCxcbiAgICAgIHk6IHB0Mi55IC0gdGhpcy5jb21wdXRlRWRnZVRhbmdlbnRPZmZzZXQocHQxLCBwdDIpXG4gICAgfTtcbiAgfVxuXG59XG4iXX0= -------------------------------------------------------------------------------- /dist/ngx-flowchart/esm2022/lib/magnet.directive.mjs: -------------------------------------------------------------------------------- 1 | import { Directive, ElementRef, HostListener, Input } from '@angular/core'; 2 | import { FlowchartConstants } from './ngx-flowchart.models'; 3 | import * as i0 from "@angular/core"; 4 | export class FcMagnetDirective { 5 | constructor(elementRef) { 6 | this.elementRef = elementRef; 7 | } 8 | ngOnInit() { 9 | const element = $(this.elementRef.nativeElement); 10 | element.addClass(FlowchartConstants.magnetClass); 11 | } 12 | dragover(event) { 13 | return this.callbacks.edgeDragoverMagnet(event, this.connector); 14 | } 15 | dragleave(event) { 16 | this.callbacks.edgeDragleaveMagnet(event); 17 | } 18 | drop(event) { 19 | return this.callbacks.edgeDrop(event, this.connector); 20 | } 21 | dragend(event) { 22 | this.callbacks.edgeDragend(event); 23 | } 24 | static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.6", ngImport: i0, type: FcMagnetDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); } 25 | static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.6", type: FcMagnetDirective, selector: "[fc-magnet]", inputs: { callbacks: "callbacks", connector: "connector" }, host: { listeners: { "dragover": "dragover($event)", "dragleave": "dragleave($event)", "drop": "drop($event)", "dragend": "dragend($event)" } }, ngImport: i0 }); } 26 | } 27 | i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.6", ngImport: i0, type: FcMagnetDirective, decorators: [{ 28 | type: Directive, 29 | args: [{ 30 | // eslint-disable-next-line @angular-eslint/directive-selector 31 | selector: '[fc-magnet]' 32 | }] 33 | }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { callbacks: [{ 34 | type: Input 35 | }], connector: [{ 36 | type: Input 37 | }], dragover: [{ 38 | type: HostListener, 39 | args: ['dragover', ['$event']] 40 | }], dragleave: [{ 41 | type: HostListener, 42 | args: ['dragleave', ['$event']] 43 | }], drop: [{ 44 | type: HostListener, 45 | args: ['drop', ['$event']] 46 | }], dragend: [{ 47 | type: HostListener, 48 | args: ['dragend', ['$event']] 49 | }] } }); 50 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFnbmV0LmRpcmVjdGl2ZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3Byb2plY3RzL25neC1mbG93Y2hhcnQvc3JjL2xpYi9tYWduZXQuZGlyZWN0aXZlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQUUsVUFBVSxFQUFFLFlBQVksRUFBRSxLQUFLLEVBQVUsTUFBTSxlQUFlLENBQUM7QUFDbkYsT0FBTyxFQUE0QixrQkFBa0IsRUFBRSxNQUFNLHdCQUF3QixDQUFDOztBQU10RixNQUFNLE9BQU8saUJBQWlCO0lBUTVCLFlBQW1CLFVBQW1DO1FBQW5DLGVBQVUsR0FBVixVQUFVLENBQXlCO0lBQ3RELENBQUM7SUFFRCxRQUFRO1FBQ04sTUFBTSxPQUFPLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsYUFBYSxDQUFDLENBQUM7UUFDakQsT0FBTyxDQUFDLFFBQVEsQ0FBQyxrQkFBa0IsQ0FBQyxXQUFXLENBQUMsQ0FBQztJQUNuRCxDQUFDO0lBR0QsUUFBUSxDQUFDLEtBQWtCO1FBQ3pCLE9BQU8sSUFBSSxDQUFDLFNBQVMsQ0FBQyxrQkFBa0IsQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBQ2xFLENBQUM7SUFHRCxTQUFTLENBQUMsS0FBa0I7UUFDMUIsSUFBSSxDQUFDLFNBQVMsQ0FBQyxtQkFBbUIsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUM1QyxDQUFDO0lBR0QsSUFBSSxDQUFDLEtBQWtCO1FBQ3JCLE9BQU8sSUFBSSxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsS0FBSyxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUN4RCxDQUFDO0lBR0QsT0FBTyxDQUFDLEtBQWtCO1FBQ3hCLElBQUksQ0FBQyxTQUFTLENBQUMsV0FBVyxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ3BDLENBQUM7OEdBbENVLGlCQUFpQjtrR0FBakIsaUJBQWlCOzsyRkFBakIsaUJBQWlCO2tCQUo3QixTQUFTO21CQUFDO29CQUNULDhEQUE4RDtvQkFDOUQsUUFBUSxFQUFFLGFBQWE7aUJBQ3hCOytFQUlDLFNBQVM7c0JBRFIsS0FBSztnQkFJTixTQUFTO3NCQURSLEtBQUs7Z0JBWU4sUUFBUTtzQkFEUCxZQUFZO3VCQUFDLFVBQVUsRUFBRSxDQUFDLFFBQVEsQ0FBQztnQkFNcEMsU0FBUztzQkFEUixZQUFZO3VCQUFDLFdBQVcsRUFBRSxDQUFDLFFBQVEsQ0FBQztnQkFNckMsSUFBSTtzQkFESCxZQUFZO3VCQUFDLE1BQU0sRUFBRSxDQUFDLFFBQVEsQ0FBQztnQkFNaEMsT0FBTztzQkFETixZQUFZO3VCQUFDLFNBQVMsRUFBRSxDQUFDLFFBQVEsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IERpcmVjdGl2ZSwgRWxlbWVudFJlZiwgSG9zdExpc3RlbmVyLCBJbnB1dCwgT25Jbml0IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBGY0NhbGxiYWNrcywgRmNDb25uZWN0b3IsIEZsb3djaGFydENvbnN0YW50cyB9IGZyb20gJy4vbmd4LWZsb3djaGFydC5tb2RlbHMnO1xuXG5ARGlyZWN0aXZlKHtcbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEBhbmd1bGFyLWVzbGludC9kaXJlY3RpdmUtc2VsZWN0b3JcbiAgc2VsZWN0b3I6ICdbZmMtbWFnbmV0XSdcbn0pXG5leHBvcnQgY2xhc3MgRmNNYWduZXREaXJlY3RpdmUgaW1wbGVtZW50cyBPbkluaXQge1xuXG4gIEBJbnB1dCgpXG4gIGNhbGxiYWNrczogRmNDYWxsYmFja3M7XG5cbiAgQElucHV0KClcbiAgY29ubmVjdG9yOiBGY0Nvbm5lY3RvcjtcblxuICBjb25zdHJ1Y3RvcihwdWJsaWMgZWxlbWVudFJlZjogRWxlbWVudFJlZjxIVE1MRWxlbWVudD4pIHtcbiAgfVxuXG4gIG5nT25Jbml0KCk6IHZvaWQge1xuICAgIGNvbnN0IGVsZW1lbnQgPSAkKHRoaXMuZWxlbWVudFJlZi5uYXRpdmVFbGVtZW50KTtcbiAgICBlbGVtZW50LmFkZENsYXNzKEZsb3djaGFydENvbnN0YW50cy5tYWduZXRDbGFzcyk7XG4gIH1cblxuICBASG9zdExpc3RlbmVyKCdkcmFnb3ZlcicsIFsnJGV2ZW50J10pXG4gIGRyYWdvdmVyKGV2ZW50OiBFdmVudCB8IGFueSkge1xuICAgIHJldHVybiB0aGlzLmNhbGxiYWNrcy5lZGdlRHJhZ292ZXJNYWduZXQoZXZlbnQsIHRoaXMuY29ubmVjdG9yKTtcbiAgfVxuXG4gIEBIb3N0TGlzdGVuZXIoJ2RyYWdsZWF2ZScsIFsnJGV2ZW50J10pXG4gIGRyYWdsZWF2ZShldmVudDogRXZlbnQgfCBhbnkpIHtcbiAgICB0aGlzLmNhbGxiYWNrcy5lZGdlRHJhZ2xlYXZlTWFnbmV0KGV2ZW50KTtcbiAgfVxuXG4gIEBIb3N0TGlzdGVuZXIoJ2Ryb3AnLCBbJyRldmVudCddKVxuICBkcm9wKGV2ZW50OiBFdmVudCB8IGFueSkge1xuICAgIHJldHVybiB0aGlzLmNhbGxiYWNrcy5lZGdlRHJvcChldmVudCwgdGhpcy5jb25uZWN0b3IpO1xuICB9XG5cbiAgQEhvc3RMaXN0ZW5lcignZHJhZ2VuZCcsIFsnJGV2ZW50J10pXG4gIGRyYWdlbmQoZXZlbnQ6IEV2ZW50IHwgYW55KSB7XG4gICAgdGhpcy5jYWxsYmFja3MuZWRnZURyYWdlbmQoZXZlbnQpO1xuICB9XG5cbn1cbiJdfQ== -------------------------------------------------------------------------------- /dist/ngx-flowchart/esm2022/lib/mouseover.service.mjs: -------------------------------------------------------------------------------- 1 | export class FcMouseOverService { 2 | constructor(applyFunction) { 3 | this.mouseoverscope = { 4 | connector: null, 5 | edge: null, 6 | node: null 7 | }; 8 | this.applyFunction = applyFunction; 9 | } 10 | nodeMouseOver(event, node) { 11 | return this.applyFunction(() => { 12 | this.mouseoverscope.node = node; 13 | }); 14 | } 15 | nodeMouseOut(event, node) { 16 | return this.applyFunction(() => { 17 | this.mouseoverscope.node = null; 18 | }); 19 | } 20 | connectorMouseEnter(event, connector) { 21 | return this.applyFunction(() => { 22 | this.mouseoverscope.connector = connector; 23 | }); 24 | } 25 | connectorMouseLeave(event, connector) { 26 | return this.applyFunction(() => { 27 | this.mouseoverscope.connector = null; 28 | }); 29 | } 30 | edgeMouseEnter(event, edge) { 31 | this.mouseoverscope.edge = edge; 32 | } 33 | edgeMouseLeave(event, edge) { 34 | this.mouseoverscope.edge = null; 35 | } 36 | } 37 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW91c2VvdmVyLnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9wcm9qZWN0cy9uZ3gtZmxvd2NoYXJ0L3NyYy9saWIvbW91c2VvdmVyLnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBRUEsTUFBTSxPQUFPLGtCQUFrQjtJQVU3QixZQUFZLGFBQWtEO1FBUjlELG1CQUFjLEdBQW1CO1lBQy9CLFNBQVMsRUFBRSxJQUFJO1lBQ2YsSUFBSSxFQUFFLElBQUk7WUFDVixJQUFJLEVBQUUsSUFBSTtTQUNYLENBQUM7UUFLQSxJQUFJLENBQUMsYUFBYSxHQUFHLGFBQWEsQ0FBQztJQUNyQyxDQUFDO0lBRU0sYUFBYSxDQUFDLEtBQWlCLEVBQUUsSUFBWTtRQUNsRCxPQUFPLElBQUksQ0FBQyxhQUFhLENBQUMsR0FBRyxFQUFFO1lBQzdCLElBQUksQ0FBQyxjQUFjLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQztRQUNsQyxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFTSxZQUFZLENBQUMsS0FBaUIsRUFBRSxJQUFZO1FBQ2pELE9BQU8sSUFBSSxDQUFDLGFBQWEsQ0FBQyxHQUFHLEVBQUU7WUFDN0IsSUFBSSxDQUFDLGNBQWMsQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDO1FBQ2xDLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVNLG1CQUFtQixDQUFDLEtBQWlCLEVBQUUsU0FBc0I7UUFDbEUsT0FBTyxJQUFJLENBQUMsYUFBYSxDQUFDLEdBQUcsRUFBRTtZQUM3QixJQUFJLENBQUMsY0FBYyxDQUFDLFNBQVMsR0FBRyxTQUFTLENBQUM7UUFDNUMsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDO0lBRU0sbUJBQW1CLENBQUMsS0FBaUIsRUFBRSxTQUFzQjtRQUNsRSxPQUFPLElBQUksQ0FBQyxhQUFhLENBQUMsR0FBRyxFQUFFO1lBQzdCLElBQUksQ0FBQyxjQUFjLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQztRQUN2QyxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFTSxjQUFjLENBQUMsS0FBaUIsRUFBRSxJQUFZO1FBQ25ELElBQUksQ0FBQyxjQUFjLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQztJQUNsQyxDQUFDO0lBRU0sY0FBYyxDQUFDLEtBQWlCLEVBQUUsSUFBWTtRQUNuRCxJQUFJLENBQUMsY0FBYyxDQUFDLElBQUksR0FBRyxJQUFJLENBQUM7SUFDbEMsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgRmNDb25uZWN0b3IsIEZjRWRnZSwgRmNOb2RlIH0gZnJvbSAnLi9uZ3gtZmxvd2NoYXJ0Lm1vZGVscyc7XG5cbmV4cG9ydCBjbGFzcyBGY01vdXNlT3ZlclNlcnZpY2Uge1xuXG4gIG1vdXNlb3ZlcnNjb3BlOiBNb3VzZU92ZXJTY29wZSA9IHtcbiAgICBjb25uZWN0b3I6IG51bGwsXG4gICAgZWRnZTogbnVsbCxcbiAgICBub2RlOiBudWxsXG4gIH07XG5cbiAgcHJpdmF0ZSByZWFkb25seSBhcHBseUZ1bmN0aW9uOiA8VD4oZm46ICguLi5hcmdzOiBhbnlbXSkgPT4gVCkgPT4gVDtcblxuICBjb25zdHJ1Y3RvcihhcHBseUZ1bmN0aW9uOiA8VD4oZm46ICguLi5hcmdzOiBhbnlbXSkgPT4gVCkgPT4gVCkge1xuICAgIHRoaXMuYXBwbHlGdW5jdGlvbiA9IGFwcGx5RnVuY3Rpb247XG4gIH1cblxuICBwdWJsaWMgbm9kZU1vdXNlT3ZlcihldmVudDogTW91c2VFdmVudCwgbm9kZTogRmNOb2RlKSB7XG4gICAgcmV0dXJuIHRoaXMuYXBwbHlGdW5jdGlvbigoKSA9PiB7XG4gICAgICB0aGlzLm1vdXNlb3ZlcnNjb3BlLm5vZGUgPSBub2RlO1xuICAgIH0pO1xuICB9XG5cbiAgcHVibGljIG5vZGVNb3VzZU91dChldmVudDogTW91c2VFdmVudCwgbm9kZTogRmNOb2RlKSB7XG4gICAgcmV0dXJuIHRoaXMuYXBwbHlGdW5jdGlvbigoKSA9PiB7XG4gICAgICB0aGlzLm1vdXNlb3ZlcnNjb3BlLm5vZGUgPSBudWxsO1xuICAgIH0pO1xuICB9XG5cbiAgcHVibGljIGNvbm5lY3Rvck1vdXNlRW50ZXIoZXZlbnQ6IE1vdXNlRXZlbnQsIGNvbm5lY3RvcjogRmNDb25uZWN0b3IpIHtcbiAgICByZXR1cm4gdGhpcy5hcHBseUZ1bmN0aW9uKCgpID0+IHtcbiAgICAgIHRoaXMubW91c2VvdmVyc2NvcGUuY29ubmVjdG9yID0gY29ubmVjdG9yO1xuICAgIH0pO1xuICB9XG5cbiAgcHVibGljIGNvbm5lY3Rvck1vdXNlTGVhdmUoZXZlbnQ6IE1vdXNlRXZlbnQsIGNvbm5lY3RvcjogRmNDb25uZWN0b3IpIHtcbiAgICByZXR1cm4gdGhpcy5hcHBseUZ1bmN0aW9uKCgpID0+IHtcbiAgICAgIHRoaXMubW91c2VvdmVyc2NvcGUuY29ubmVjdG9yID0gbnVsbDtcbiAgICB9KTtcbiAgfVxuXG4gIHB1YmxpYyBlZGdlTW91c2VFbnRlcihldmVudDogTW91c2VFdmVudCwgZWRnZTogRmNFZGdlKSB7XG4gICAgdGhpcy5tb3VzZW92ZXJzY29wZS5lZGdlID0gZWRnZTtcbiAgfVxuXG4gIHB1YmxpYyBlZGdlTW91c2VMZWF2ZShldmVudDogTW91c2VFdmVudCwgZWRnZTogRmNFZGdlKSB7XG4gICAgdGhpcy5tb3VzZW92ZXJzY29wZS5lZGdlID0gbnVsbDtcbiAgfVxufVxuXG5leHBvcnQgaW50ZXJmYWNlIE1vdXNlT3ZlclNjb3BlIHtcbiAgY29ubmVjdG9yOiBGY0Nvbm5lY3RvcjtcbiAgZWRnZTogRmNFZGdlO1xuICBub2RlOiBGY05vZGU7XG59XG4iXX0= -------------------------------------------------------------------------------- /dist/ngx-flowchart/esm2022/lib/ngx-flowchart.module.mjs: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { NgxFlowchartComponent } from './ngx-flowchart.component'; 3 | import { FcModelValidationService } from './modelvalidation.service'; 4 | import { FcEdgeDrawingService } from './edge-drawing.service'; 5 | import { CommonModule } from '@angular/common'; 6 | import { FcMagnetDirective } from './magnet.directive'; 7 | import { FcConnectorDirective } from './connector.directive'; 8 | import { FcNodeContainerComponent } from './node.component'; 9 | import { FC_NODE_COMPONENT_CONFIG } from './ngx-flowchart.models'; 10 | import { DefaultFcNodeComponent } from './default-node.component'; 11 | import * as i0 from "@angular/core"; 12 | export class NgxFlowchartModule { 13 | static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.6", ngImport: i0, type: NgxFlowchartModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); } 14 | static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.6", ngImport: i0, type: NgxFlowchartModule, declarations: [NgxFlowchartComponent, 15 | FcMagnetDirective, 16 | FcConnectorDirective, 17 | FcNodeContainerComponent, 18 | DefaultFcNodeComponent], imports: [CommonModule], exports: [NgxFlowchartComponent, 19 | FcMagnetDirective, 20 | FcConnectorDirective, 21 | DefaultFcNodeComponent] }); } 22 | static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.6", ngImport: i0, type: NgxFlowchartModule, providers: [ 23 | FcModelValidationService, 24 | FcEdgeDrawingService, 25 | { 26 | provide: FC_NODE_COMPONENT_CONFIG, 27 | useValue: { 28 | nodeComponentType: DefaultFcNodeComponent 29 | } 30 | } 31 | ], imports: [CommonModule] }); } 32 | } 33 | i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.6", ngImport: i0, type: NgxFlowchartModule, decorators: [{ 34 | type: NgModule, 35 | args: [{ 36 | declarations: [NgxFlowchartComponent, 37 | FcMagnetDirective, 38 | FcConnectorDirective, 39 | FcNodeContainerComponent, 40 | DefaultFcNodeComponent], 41 | providers: [ 42 | FcModelValidationService, 43 | FcEdgeDrawingService, 44 | { 45 | provide: FC_NODE_COMPONENT_CONFIG, 46 | useValue: { 47 | nodeComponentType: DefaultFcNodeComponent 48 | } 49 | } 50 | ], 51 | imports: [ 52 | CommonModule 53 | ], 54 | exports: [NgxFlowchartComponent, 55 | FcMagnetDirective, 56 | FcConnectorDirective, 57 | DefaultFcNodeComponent] 58 | }] 59 | }] }); 60 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmd4LWZsb3djaGFydC5tb2R1bGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9wcm9qZWN0cy9uZ3gtZmxvd2NoYXJ0L3NyYy9saWIvbmd4LWZsb3djaGFydC5tb2R1bGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUN6QyxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUNsRSxPQUFPLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUNyRSxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUM5RCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDL0MsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDdkQsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDN0QsT0FBTyxFQUFFLHdCQUF3QixFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDNUQsT0FBTyxFQUFFLHdCQUF3QixFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDbEUsT0FBTyxFQUFFLHNCQUFzQixFQUFFLE1BQU0sMEJBQTBCLENBQUM7O0FBMEJsRSxNQUFNLE9BQU8sa0JBQWtCOzhHQUFsQixrQkFBa0I7K0dBQWxCLGtCQUFrQixpQkF2QloscUJBQXFCO1lBQ2hDLGlCQUFpQjtZQUNqQixvQkFBb0I7WUFDcEIsd0JBQXdCO1lBQ3hCLHNCQUFzQixhQVl0QixZQUFZLGFBRU4scUJBQXFCO1lBQzNCLGlCQUFpQjtZQUNqQixvQkFBb0I7WUFDcEIsc0JBQXNCOytHQUVqQixrQkFBa0IsYUFsQmhCO1lBQ1Asd0JBQXdCO1lBQ3hCLG9CQUFvQjtZQUNwQjtnQkFDSSxPQUFPLEVBQUUsd0JBQXdCO2dCQUNqQyxRQUFRLEVBQUU7b0JBQ04saUJBQWlCLEVBQUUsc0JBQXNCO2lCQUM1QzthQUNKO1NBQ0osWUFFRyxZQUFZOzsyRkFPUCxrQkFBa0I7a0JBeEI5QixRQUFRO21CQUFDO29CQUNOLFlBQVksRUFBRSxDQUFDLHFCQUFxQjt3QkFDaEMsaUJBQWlCO3dCQUNqQixvQkFBb0I7d0JBQ3BCLHdCQUF3Qjt3QkFDeEIsc0JBQXNCLENBQUM7b0JBQzNCLFNBQVMsRUFBRTt3QkFDUCx3QkFBd0I7d0JBQ3hCLG9CQUFvQjt3QkFDcEI7NEJBQ0ksT0FBTyxFQUFFLHdCQUF3Qjs0QkFDakMsUUFBUSxFQUFFO2dDQUNOLGlCQUFpQixFQUFFLHNCQUFzQjs2QkFDNUM7eUJBQ0o7cUJBQ0o7b0JBQ0QsT0FBTyxFQUFFO3dCQUNMLFlBQVk7cUJBQ2Y7b0JBQ0QsT0FBTyxFQUFFLENBQUMscUJBQXFCO3dCQUMzQixpQkFBaUI7d0JBQ2pCLG9CQUFvQjt3QkFDcEIsc0JBQXNCLENBQUM7aUJBQzlCIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgTmdNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IE5neEZsb3djaGFydENvbXBvbmVudCB9IGZyb20gJy4vbmd4LWZsb3djaGFydC5jb21wb25lbnQnO1xuaW1wb3J0IHsgRmNNb2RlbFZhbGlkYXRpb25TZXJ2aWNlIH0gZnJvbSAnLi9tb2RlbHZhbGlkYXRpb24uc2VydmljZSc7XG5pbXBvcnQgeyBGY0VkZ2VEcmF3aW5nU2VydmljZSB9IGZyb20gJy4vZWRnZS1kcmF3aW5nLnNlcnZpY2UnO1xuaW1wb3J0IHsgQ29tbW9uTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uJztcbmltcG9ydCB7IEZjTWFnbmV0RGlyZWN0aXZlIH0gZnJvbSAnLi9tYWduZXQuZGlyZWN0aXZlJztcbmltcG9ydCB7IEZjQ29ubmVjdG9yRGlyZWN0aXZlIH0gZnJvbSAnLi9jb25uZWN0b3IuZGlyZWN0aXZlJztcbmltcG9ydCB7IEZjTm9kZUNvbnRhaW5lckNvbXBvbmVudCB9IGZyb20gJy4vbm9kZS5jb21wb25lbnQnO1xuaW1wb3J0IHsgRkNfTk9ERV9DT01QT05FTlRfQ09ORklHIH0gZnJvbSAnLi9uZ3gtZmxvd2NoYXJ0Lm1vZGVscyc7XG5pbXBvcnQgeyBEZWZhdWx0RmNOb2RlQ29tcG9uZW50IH0gZnJvbSAnLi9kZWZhdWx0LW5vZGUuY29tcG9uZW50JztcblxuQE5nTW9kdWxlKHtcbiAgICBkZWNsYXJhdGlvbnM6IFtOZ3hGbG93Y2hhcnRDb21wb25lbnQsXG4gICAgICAgIEZjTWFnbmV0RGlyZWN0aXZlLFxuICAgICAgICBGY0Nvbm5lY3RvckRpcmVjdGl2ZSxcbiAgICAgICAgRmNOb2RlQ29udGFpbmVyQ29tcG9uZW50LFxuICAgICAgICBEZWZhdWx0RmNOb2RlQ29tcG9uZW50XSxcbiAgICBwcm92aWRlcnM6IFtcbiAgICAgICAgRmNNb2RlbFZhbGlkYXRpb25TZXJ2aWNlLFxuICAgICAgICBGY0VkZ2VEcmF3aW5nU2VydmljZSxcbiAgICAgICAge1xuICAgICAgICAgICAgcHJvdmlkZTogRkNfTk9ERV9DT01QT05FTlRfQ09ORklHLFxuICAgICAgICAgICAgdXNlVmFsdWU6IHtcbiAgICAgICAgICAgICAgICBub2RlQ29tcG9uZW50VHlwZTogRGVmYXVsdEZjTm9kZUNvbXBvbmVudFxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgXSxcbiAgICBpbXBvcnRzOiBbXG4gICAgICAgIENvbW1vbk1vZHVsZVxuICAgIF0sXG4gICAgZXhwb3J0czogW05neEZsb3djaGFydENvbXBvbmVudCxcbiAgICAgICAgRmNNYWduZXREaXJlY3RpdmUsXG4gICAgICAgIEZjQ29ubmVjdG9yRGlyZWN0aXZlLFxuICAgICAgICBEZWZhdWx0RmNOb2RlQ29tcG9uZW50XVxufSlcbmV4cG9ydCBjbGFzcyBOZ3hGbG93Y2hhcnRNb2R1bGUgeyB9XG4iXX0= -------------------------------------------------------------------------------- /dist/ngx-flowchart/esm2022/lib/scrollparent.mjs: -------------------------------------------------------------------------------- 1 | const regex = /(auto|scroll)/; 2 | const style = (node, prop) => getComputedStyle(node, null).getPropertyValue(prop); 3 | const scroll = (node) => regex.test(style(node, 'overflow') + 4 | style(node, 'overflow-y') + 5 | style(node, 'overflow-x')); 6 | const scrollparent = (node) => !node || node === document.body 7 | ? document.body 8 | : scroll(node) 9 | ? node 10 | : scrollparent(node.parentNode); 11 | export default scrollparent; 12 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2Nyb2xscGFyZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vcHJvamVjdHMvbmd4LWZsb3djaGFydC9zcmMvbGliL3Njcm9sbHBhcmVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxNQUFNLEtBQUssR0FBRyxlQUFlLENBQUM7QUFFOUIsTUFBTSxLQUFLLEdBQUcsQ0FBQyxJQUFhLEVBQUUsSUFBWSxFQUFVLEVBQUUsQ0FDcEQsZ0JBQWdCLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDLGdCQUFnQixDQUFDLElBQUksQ0FBQyxDQUFDO0FBRXRELE1BQU0sTUFBTSxHQUFHLENBQUMsSUFBYSxFQUFFLEVBQUUsQ0FDL0IsS0FBSyxDQUFDLElBQUksQ0FDUixLQUFLLENBQUMsSUFBSSxFQUFFLFVBQVUsQ0FBQztJQUN2QixLQUFLLENBQUMsSUFBSSxFQUFFLFlBQVksQ0FBQztJQUN6QixLQUFLLENBQUMsSUFBSSxFQUFFLFlBQVksQ0FBQyxDQUFDLENBQUM7QUFFL0IsTUFBTSxZQUFZLEdBQUcsQ0FBQyxJQUFpQixFQUFlLEVBQUUsQ0FDdEQsQ0FBQyxJQUFJLElBQUksSUFBSSxLQUFLLFFBQVEsQ0FBQyxJQUFJO0lBQzdCLENBQUMsQ0FBQyxRQUFRLENBQUMsSUFBSTtJQUNmLENBQUMsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDO1FBQ2QsQ0FBQyxDQUFDLElBQUk7UUFDTixDQUFDLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxVQUF5QixDQUFDLENBQUM7QUFFbkQsZUFBZSxZQUFZLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJjb25zdCByZWdleCA9IC8oYXV0b3xzY3JvbGwpLztcblxuY29uc3Qgc3R5bGUgPSAobm9kZTogRWxlbWVudCwgcHJvcDogc3RyaW5nKTogc3RyaW5nID0+XG4gIGdldENvbXB1dGVkU3R5bGUobm9kZSwgbnVsbCkuZ2V0UHJvcGVydHlWYWx1ZShwcm9wKTtcblxuY29uc3Qgc2Nyb2xsID0gKG5vZGU6IEVsZW1lbnQpID0+XG4gIHJlZ2V4LnRlc3QoXG4gICAgc3R5bGUobm9kZSwgJ292ZXJmbG93JykgK1xuICAgIHN0eWxlKG5vZGUsICdvdmVyZmxvdy15JykgK1xuICAgIHN0eWxlKG5vZGUsICdvdmVyZmxvdy14JykpO1xuXG5jb25zdCBzY3JvbGxwYXJlbnQgPSAobm9kZTogSFRNTEVsZW1lbnQpOiBIVE1MRWxlbWVudCA9PlxuICAhbm9kZSB8fCBub2RlID09PSBkb2N1bWVudC5ib2R5XG4gICAgPyBkb2N1bWVudC5ib2R5XG4gICAgOiBzY3JvbGwobm9kZSlcbiAgICA/IG5vZGVcbiAgICA6IHNjcm9sbHBhcmVudChub2RlLnBhcmVudE5vZGUgYXMgSFRNTEVsZW1lbnQpO1xuXG5leHBvcnQgZGVmYXVsdCBzY3JvbGxwYXJlbnQ7XG4iXX0= -------------------------------------------------------------------------------- /dist/ngx-flowchart/esm2022/ngx-flowchart.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * Generated bundle index. Do not edit. 3 | */ 4 | export * from './public-api'; 5 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmd4LWZsb3djaGFydC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3Byb2plY3RzL25neC1mbG93Y2hhcnQvc3JjL25neC1mbG93Y2hhcnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFFSCxjQUFjLGNBQWMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogR2VuZXJhdGVkIGJ1bmRsZSBpbmRleC4gRG8gbm90IGVkaXQuXG4gKi9cblxuZXhwb3J0ICogZnJvbSAnLi9wdWJsaWMtYXBpJztcbiJdfQ== -------------------------------------------------------------------------------- /dist/ngx-flowchart/esm2022/public-api.mjs: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of ngx-flowchart 3 | */ 4 | export * from './lib/ngx-flowchart.component'; 5 | export * from './lib/ngx-flowchart.module'; 6 | export * from './lib/ngx-flowchart.models'; 7 | export { FcNodeComponent } from './lib/node.component'; 8 | export { FcMagnetDirective } from './lib/magnet.directive'; 9 | export { FcConnectorDirective } from './lib/connector.directive'; 10 | export { DefaultFcNodeComponent } from './lib/default-node.component'; 11 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVibGljLWFwaS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3Byb2plY3RzL25neC1mbG93Y2hhcnQvc3JjL3B1YmxpYy1hcGkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFFSCxjQUFjLCtCQUErQixDQUFDO0FBQzlDLGNBQWMsNEJBQTRCLENBQUM7QUFDM0MsY0FBYyw0QkFBNEIsQ0FBQztBQUMzQyxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDdkQsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDM0QsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFDakUsT0FBTyxFQUFFLHNCQUFzQixFQUFFLE1BQU0sOEJBQThCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuICogUHVibGljIEFQSSBTdXJmYWNlIG9mIG5neC1mbG93Y2hhcnRcbiAqL1xuXG5leHBvcnQgKiBmcm9tICcuL2xpYi9uZ3gtZmxvd2NoYXJ0LmNvbXBvbmVudCc7XG5leHBvcnQgKiBmcm9tICcuL2xpYi9uZ3gtZmxvd2NoYXJ0Lm1vZHVsZSc7XG5leHBvcnQgKiBmcm9tICcuL2xpYi9uZ3gtZmxvd2NoYXJ0Lm1vZGVscyc7XG5leHBvcnQgeyBGY05vZGVDb21wb25lbnQgfSBmcm9tICcuL2xpYi9ub2RlLmNvbXBvbmVudCc7XG5leHBvcnQgeyBGY01hZ25ldERpcmVjdGl2ZSB9IGZyb20gJy4vbGliL21hZ25ldC5kaXJlY3RpdmUnO1xuZXhwb3J0IHsgRmNDb25uZWN0b3JEaXJlY3RpdmUgfSBmcm9tICcuL2xpYi9jb25uZWN0b3IuZGlyZWN0aXZlJztcbmV4cG9ydCB7IERlZmF1bHRGY05vZGVDb21wb25lbnQgfSBmcm9tICcuL2xpYi9kZWZhdWx0LW5vZGUuY29tcG9uZW50JztcbiJdfQ== -------------------------------------------------------------------------------- /dist/ngx-flowchart/index.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Generated bundle index. Do not edit. 3 | */ 4 | /// 5 | export * from './public-api'; 6 | -------------------------------------------------------------------------------- /dist/ngx-flowchart/lib/connector.directive.d.ts: -------------------------------------------------------------------------------- 1 | import { ElementRef, OnChanges, OnInit, SimpleChanges } from '@angular/core'; 2 | import { FcCallbacks, FcConnector, FcNodeRectInfo } from './ngx-flowchart.models'; 3 | import { FcModelService } from './model.service'; 4 | import * as i0 from "@angular/core"; 5 | export declare class FcConnectorDirective implements OnInit, OnChanges { 6 | elementRef: ElementRef; 7 | callbacks: FcCallbacks; 8 | modelservice: FcModelService; 9 | connector: FcConnector; 10 | nodeRectInfo: FcNodeRectInfo; 11 | mouseOverConnector: FcConnector; 12 | constructor(elementRef: ElementRef); 13 | ngOnInit(): void; 14 | ngOnChanges(changes: SimpleChanges): void; 15 | private updateConnectorClass; 16 | dragover(event: Event | any): void; 17 | drop(event: Event | any): boolean; 18 | dragend(event: Event | any): void; 19 | dragstart(event: Event | any): void; 20 | mouseenter(event: MouseEvent): void; 21 | mouseleave(event: MouseEvent): void; 22 | static ɵfac: i0.ɵɵFactoryDeclaration; 23 | static ɵdir: i0.ɵɵDirectiveDeclaration; 24 | } 25 | -------------------------------------------------------------------------------- /dist/ngx-flowchart/lib/default-node.component.d.ts: -------------------------------------------------------------------------------- 1 | import { FcNodeComponent } from './node.component'; 2 | import * as i0 from "@angular/core"; 3 | export declare class DefaultFcNodeComponent extends FcNodeComponent { 4 | constructor(); 5 | static ɵfac: i0.ɵɵFactoryDeclaration; 6 | static ɵcmp: i0.ɵɵComponentDeclaration; 7 | } 8 | -------------------------------------------------------------------------------- /dist/ngx-flowchart/lib/edge-dragging.service.d.ts: -------------------------------------------------------------------------------- 1 | import { FcModelService } from './model.service'; 2 | import { FcConnector, FcCoords, FcEdge, FcModel } from './ngx-flowchart.models'; 3 | import { FcEdgeDrawingService } from './edge-drawing.service'; 4 | import { FcModelValidationService } from './modelvalidation.service'; 5 | export declare class FcEdgeDraggingService { 6 | edgeDragging: EdgeDragging; 7 | private draggedEdgeSource; 8 | private dragOffset; 9 | private destinationHtmlElement; 10 | private oldDisplayStyle; 11 | private readonly modelValidation; 12 | private readonly edgeDrawingService; 13 | private readonly modelService; 14 | private readonly model; 15 | private readonly isValidEdgeCallback; 16 | private readonly applyFunction; 17 | private readonly dragAnimation; 18 | private readonly edgeStyle; 19 | constructor(modelValidation: FcModelValidationService, edgeDrawingService: FcEdgeDrawingService, modelService: FcModelService, model: FcModel, isValidEdgeCallback: (source: FcConnector, destination: FcConnector) => boolean, applyFunction: (fn: (...args: any[]) => T) => T, dragAnimation: string, edgeStyle: string); 20 | dragstart(event: Event | any, connector: FcConnector): void; 21 | dragover(event: Event | any): void; 22 | dragoverConnector(event: Event | any, connector: FcConnector): boolean; 23 | dragleaveMagnet(event: Event | any): void; 24 | dragoverMagnet(event: Event | any, connector: FcConnector): boolean; 25 | dragend(event: Event | any): void; 26 | drop(event: Event | any, targetConnector: FcConnector): boolean; 27 | } 28 | export interface EdgeDragging { 29 | isDragging: boolean; 30 | shadowDragStarted: boolean; 31 | dragPoint1: FcCoords; 32 | dragPoint2: FcCoords; 33 | dragLabel?: string; 34 | prevEdge?: FcEdge; 35 | magnetActive?: boolean; 36 | gElement?: JQuery; 37 | pathElement?: JQuery; 38 | circleElement?: JQuery; 39 | } 40 | -------------------------------------------------------------------------------- /dist/ngx-flowchart/lib/edge-drawing.service.d.ts: -------------------------------------------------------------------------------- 1 | import { FcCoords } from './ngx-flowchart.models'; 2 | import * as i0 from "@angular/core"; 3 | export declare class FcEdgeDrawingService { 4 | constructor(); 5 | getEdgeDAttribute(pt1: FcCoords, pt2: FcCoords, style: string): string; 6 | getEdgeCenter(pt1: FcCoords, pt2: FcCoords): FcCoords; 7 | private computeEdgeTangentOffset; 8 | private computeEdgeSourceTangent; 9 | private computeEdgeDestinationTangent; 10 | static ɵfac: i0.ɵɵFactoryDeclaration; 11 | static ɵprov: i0.ɵɵInjectableDeclaration; 12 | } 13 | -------------------------------------------------------------------------------- /dist/ngx-flowchart/lib/magnet.directive.d.ts: -------------------------------------------------------------------------------- 1 | import { ElementRef, OnInit } from '@angular/core'; 2 | import { FcCallbacks, FcConnector } from './ngx-flowchart.models'; 3 | import * as i0 from "@angular/core"; 4 | export declare class FcMagnetDirective implements OnInit { 5 | elementRef: ElementRef; 6 | callbacks: FcCallbacks; 7 | connector: FcConnector; 8 | constructor(elementRef: ElementRef); 9 | ngOnInit(): void; 10 | dragover(event: Event | any): boolean; 11 | dragleave(event: Event | any): void; 12 | drop(event: Event | any): boolean; 13 | dragend(event: Event | any): void; 14 | static ɵfac: i0.ɵɵFactoryDeclaration; 15 | static ɵdir: i0.ɵɵDirectiveDeclaration; 16 | } 17 | -------------------------------------------------------------------------------- /dist/ngx-flowchart/lib/model.service.d.ts: -------------------------------------------------------------------------------- 1 | import { FcModelValidationService } from './modelvalidation.service'; 2 | import { FcConnector, FcConnectorRectInfo, FcCoords, FcEdge, FcItemInfo, FcModel, FcNode, FcRectBox } from './ngx-flowchart.models'; 3 | import { Observable, Subject } from 'rxjs'; 4 | import { EventEmitter } from '@angular/core'; 5 | interface HtmlElementMap { 6 | [id: string]: HTMLElement; 7 | } 8 | interface ConnectorRectInfoMap { 9 | [id: string]: FcConnectorRectInfo; 10 | } 11 | declare abstract class AbstractFcModel { 12 | modelService: FcModelService; 13 | protected constructor(modelService: FcModelService); 14 | select(object: T): void; 15 | deselect(object: T): void; 16 | toggleSelected(object: T): void; 17 | isSelected(object: T): boolean; 18 | isEdit(object: T): boolean; 19 | } 20 | declare class ConnectorsModel extends AbstractFcModel { 21 | constructor(modelService: FcModelService); 22 | getConnector(connectorId: string): FcConnector; 23 | getConnectorRectInfo(connectorId: string): FcConnectorRectInfo; 24 | setConnectorRectInfo(connectorId: string, connectorRectInfo: FcConnectorRectInfo): void; 25 | private _getCoords; 26 | getCoords(connectorId: string): FcCoords; 27 | getCenteredCoord(connectorId: string): FcCoords; 28 | } 29 | declare class NodesModel extends AbstractFcModel { 30 | constructor(modelService: FcModelService); 31 | getConnectorsByType(node: FcNode, type: string): Array; 32 | private _addConnector; 33 | delete(node: FcNode): void; 34 | getSelectedNodes(): Array; 35 | handleClicked(node: FcNode, ctrlKey?: boolean): void; 36 | private _addNode; 37 | getConnectorIds(node: FcNode): Array; 38 | getNodeByConnectorId(connectorId: string): FcNode; 39 | getHtmlElement(nodeId: string): HTMLElement; 40 | setHtmlElement(nodeId: string, element: HTMLElement): void; 41 | } 42 | declare class EdgesModel extends AbstractFcModel { 43 | constructor(modelService: FcModelService); 44 | sourceCoord(edge: FcEdge): FcCoords; 45 | destCoord(edge: FcEdge): FcCoords; 46 | delete(edge: FcEdge): void; 47 | getSelectedEdges(): Array; 48 | handleEdgeMouseClick(edge: FcEdge, ctrlKey?: boolean): void; 49 | putEdge(edge: FcEdge): void; 50 | _addEdge(event: Event, sourceConnector: FcConnector, destConnector: FcConnector, label: string): void; 51 | } 52 | export declare class FcModelService { 53 | modelValidation: FcModelValidationService; 54 | model: FcModel; 55 | private readonly detectChangesSubject; 56 | selectedObjects: any[]; 57 | connectorsRectInfos: ConnectorRectInfoMap; 58 | nodesHtmlElements: HtmlElementMap; 59 | canvasHtmlElement: HTMLElement; 60 | dragImage: HTMLImageElement; 61 | svgHtmlElement: SVGElement; 62 | dropNode: (event: Event, node: FcNode) => void; 63 | createEdge: (event: Event, edge: FcEdge) => Observable; 64 | edgeAddedCallback: (edge: FcEdge) => void; 65 | nodeRemovedCallback: (node: FcNode) => void; 66 | edgeRemovedCallback: (edge: FcEdge) => void; 67 | dropTargetId: string; 68 | private readonly modelChanged; 69 | private readonly debouncer; 70 | connectors: ConnectorsModel; 71 | nodes: NodesModel; 72 | edges: EdgesModel; 73 | constructor(modelValidation: FcModelValidationService, model: FcModel, modelChanged: EventEmitter, detectChangesSubject: Subject, selectedObjects: any[], dropNode: (event: Event, node: FcNode) => void, createEdge: (event: Event, edge: FcEdge) => Observable, edgeAddedCallback: (edge: FcEdge) => void, nodeRemovedCallback: (node: FcNode) => void, edgeRemovedCallback: (edge: FcEdge) => void, canvasHtmlElement: HTMLElement, svgHtmlElement: SVGElement); 74 | notifyModelChanged(): void; 75 | detectChanges(): void; 76 | selectObject(object: any): void; 77 | deselectObject(object: any): void; 78 | toggleSelectedObject(object: any): void; 79 | isSelectedObject(object: any): boolean; 80 | selectAll(): void; 81 | deselectAll(): void; 82 | isEditObject(object: any): boolean; 83 | private inRectBox; 84 | getItemInfoAtPoint(x: number, y: number): FcItemInfo; 85 | getNodeAtPoint(x: number, y: number): FcNode; 86 | getEdgeAtPoint(x: number, y: number): FcEdge; 87 | selectAllInRect(rectBox: FcRectBox): void; 88 | deleteSelected(): void; 89 | isEditable(): boolean; 90 | isDropSource(): boolean; 91 | getDragImage(): HTMLImageElement; 92 | } 93 | export {}; 94 | -------------------------------------------------------------------------------- /dist/ngx-flowchart/lib/modelvalidation.service.d.ts: -------------------------------------------------------------------------------- 1 | import { FcConnector, FcEdge, FcModel, FcNode } from './ngx-flowchart.models'; 2 | import * as i0 from "@angular/core"; 3 | export declare class FcModelValidationService { 4 | constructor(); 5 | validateModel(model: FcModel): FcModel; 6 | validateNodes(nodes: Array): Array; 7 | validateNode(node: FcNode): FcNode; 8 | private _validateEdges; 9 | validateEdges(edges: Array, nodes: Array): Array; 10 | private _validateEdge; 11 | validateEdge(edge: FcEdge, nodes: Array): FcEdge; 12 | validateConnector(connector: FcConnector): FcConnector; 13 | static ɵfac: i0.ɵɵFactoryDeclaration; 14 | static ɵprov: i0.ɵɵInjectableDeclaration; 15 | } 16 | -------------------------------------------------------------------------------- /dist/ngx-flowchart/lib/mouseover.service.d.ts: -------------------------------------------------------------------------------- 1 | import { FcConnector, FcEdge, FcNode } from './ngx-flowchart.models'; 2 | export declare class FcMouseOverService { 3 | mouseoverscope: MouseOverScope; 4 | private readonly applyFunction; 5 | constructor(applyFunction: (fn: (...args: any[]) => T) => T); 6 | nodeMouseOver(event: MouseEvent, node: FcNode): void; 7 | nodeMouseOut(event: MouseEvent, node: FcNode): void; 8 | connectorMouseEnter(event: MouseEvent, connector: FcConnector): void; 9 | connectorMouseLeave(event: MouseEvent, connector: FcConnector): void; 10 | edgeMouseEnter(event: MouseEvent, edge: FcEdge): void; 11 | edgeMouseLeave(event: MouseEvent, edge: FcEdge): void; 12 | } 13 | export interface MouseOverScope { 14 | connector: FcConnector; 15 | edge: FcEdge; 16 | node: FcNode; 17 | } 18 | -------------------------------------------------------------------------------- /dist/ngx-flowchart/lib/ngx-flowchart.component.d.ts: -------------------------------------------------------------------------------- 1 | import { ChangeDetectorRef, DoCheck, ElementRef, EventEmitter, IterableDiffers, NgZone, OnInit } from '@angular/core'; 2 | import { FcCallbacks, FcEdge, FcModel, UserCallbacks, UserNodeCallbacks } from './ngx-flowchart.models'; 3 | import { FcModelService } from './model.service'; 4 | import { FcModelValidationService } from './modelvalidation.service'; 5 | import { FcNodeDraggingService } from './node-dragging.service'; 6 | import { FcEdgeDrawingService } from './edge-drawing.service'; 7 | import { FcEdgeDraggingService } from './edge-dragging.service'; 8 | import { FcMouseOverService } from './mouseover.service'; 9 | import { FcRectangleSelectService } from './rectangleselect.service'; 10 | import * as i0 from "@angular/core"; 11 | export declare class NgxFlowchartComponent implements OnInit, DoCheck { 12 | private elementRef; 13 | private differs; 14 | private modelValidation; 15 | edgeDrawingService: FcEdgeDrawingService; 16 | private cd; 17 | private zone; 18 | get canvasClass(): string; 19 | model: FcModel; 20 | selectedObjects: any[]; 21 | edgeStyle: string; 22 | userCallbacks: UserCallbacks; 23 | automaticResize: boolean; 24 | dragAnimation: string; 25 | nodeWidth: number; 26 | nodeHeight: number; 27 | dropTargetId: string; 28 | modelChanged: EventEmitter; 29 | private fitModelSizeByDefaultValue; 30 | get fitModelSizeByDefault(): boolean; 31 | set fitModelSizeByDefault(value: boolean); 32 | callbacks: FcCallbacks; 33 | userNodeCallbacks: UserNodeCallbacks; 34 | modelService: FcModelService; 35 | nodeDraggingService: FcNodeDraggingService; 36 | edgeDraggingService: FcEdgeDraggingService; 37 | mouseoverService: FcMouseOverService; 38 | rectangleSelectService: FcRectangleSelectService; 39 | arrowDefId: string; 40 | arrowDefIdSelected: string; 41 | flowchartConstants: { 42 | htmlPrefix: string; 43 | leftConnectorType: string; 44 | rightConnectorType: string; 45 | curvedStyle: string; 46 | lineStyle: string; 47 | dragAnimationRepaint: string; 48 | dragAnimationShadow: string; 49 | canvasClass: string; 50 | selectedClass: string; 51 | editClass: string; 52 | activeClass: string; 53 | hoverClass: string; 54 | draggingClass: string; 55 | edgeClass: string; 56 | edgeLabelClass: string; 57 | connectorClass: string; 58 | magnetClass: string; 59 | nodeClass: string; 60 | nodeOverlayClass: string; 61 | leftConnectorClass: string; 62 | rightConnectorClass: string; 63 | canvasResizeThreshold: number; 64 | canvasResizeStep: number; 65 | }; 66 | private nodesDiffer; 67 | private edgesDiffer; 68 | private readonly detectChangesSubject; 69 | constructor(elementRef: ElementRef, differs: IterableDiffers, modelValidation: FcModelValidationService, edgeDrawingService: FcEdgeDrawingService, cd: ChangeDetectorRef, zone: NgZone); 70 | ngOnInit(): void; 71 | ngDoCheck(): void; 72 | getEdgeDAttribute(edge: FcEdge): string; 73 | adjustCanvasSize(fit?: boolean): void; 74 | canvasClick(event: MouseEvent): void; 75 | edgeMouseDown(event: MouseEvent, edge: FcEdge): void; 76 | edgeClick(event: MouseEvent, edge: FcEdge): void; 77 | edgeRemove(event: Event, edge: FcEdge): void; 78 | edgeEdit(event: Event, edge: FcEdge): void; 79 | edgeDoubleClick(event: MouseEvent, edge: FcEdge): void; 80 | edgeMouseOver(event: MouseEvent, edge: FcEdge): void; 81 | edgeMouseEnter(event: MouseEvent, edge: FcEdge): void; 82 | edgeMouseLeave(event: MouseEvent, edge: FcEdge): void; 83 | dragover(event: Event | any): void; 84 | drop(event: Event | any): void; 85 | mousedown(event: MouseEvent): void; 86 | mousemove(event: MouseEvent): void; 87 | mouseup(event: MouseEvent): void; 88 | static ɵfac: i0.ɵɵFactoryDeclaration; 89 | static ɵcmp: i0.ɵɵComponentDeclaration; 90 | } 91 | -------------------------------------------------------------------------------- /dist/ngx-flowchart/lib/ngx-flowchart.models.d.ts: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rxjs'; 2 | import { InjectionToken, Type } from '@angular/core'; 3 | import { FcNodeComponent } from './node.component'; 4 | export declare const FC_NODE_COMPONENT_CONFIG: InjectionToken; 5 | export interface FcNodeComponentConfig { 6 | nodeComponentType: Type; 7 | } 8 | export declare const FlowchartConstants: { 9 | htmlPrefix: string; 10 | leftConnectorType: string; 11 | rightConnectorType: string; 12 | curvedStyle: string; 13 | lineStyle: string; 14 | dragAnimationRepaint: string; 15 | dragAnimationShadow: string; 16 | canvasClass: string; 17 | selectedClass: string; 18 | editClass: string; 19 | activeClass: string; 20 | hoverClass: string; 21 | draggingClass: string; 22 | edgeClass: string; 23 | edgeLabelClass: string; 24 | connectorClass: string; 25 | magnetClass: string; 26 | nodeClass: string; 27 | nodeOverlayClass: string; 28 | leftConnectorClass: string; 29 | rightConnectorClass: string; 30 | canvasResizeThreshold: number; 31 | canvasResizeStep: number; 32 | }; 33 | export interface FcCoords { 34 | x?: number; 35 | y?: number; 36 | } 37 | export interface FcRectBox { 38 | top: number; 39 | left: number; 40 | right: number; 41 | bottom: number; 42 | } 43 | export interface FcConnector { 44 | id: string; 45 | type: string; 46 | } 47 | export interface FcNode extends FcCoords { 48 | id: string; 49 | name: string; 50 | connectors: Array; 51 | readonly?: boolean; 52 | [key: string]: any; 53 | } 54 | export interface FcNodeRectInfo { 55 | width(): number; 56 | height(): number; 57 | top(): number; 58 | left(): number; 59 | right(): number; 60 | bottom(): number; 61 | } 62 | export interface FcConnectorRectInfo { 63 | type: string; 64 | width: number; 65 | height: number; 66 | nodeRectInfo: FcNodeRectInfo; 67 | } 68 | export interface FcEdge { 69 | label?: string; 70 | source?: string; 71 | destination?: string; 72 | active?: boolean; 73 | } 74 | export interface FcItemInfo { 75 | node?: FcNode; 76 | edge?: FcEdge; 77 | } 78 | export interface FcModel { 79 | nodes: Array; 80 | edges: Array; 81 | } 82 | export interface UserCallbacks { 83 | dropNode?: (event: Event, node: FcNode) => void; 84 | createEdge?: (event: Event, edge: FcEdge) => Observable; 85 | edgeAdded?: (edge: FcEdge) => void; 86 | nodeRemoved?: (node: FcNode) => void; 87 | edgeRemoved?: (edge: FcEdge) => void; 88 | edgeDoubleClick?: (event: MouseEvent, edge: FcEdge) => void; 89 | edgeMouseOver?: (event: MouseEvent, edge: FcEdge) => void; 90 | isValidEdge?: (source: FcConnector, destination: FcConnector) => boolean; 91 | edgeEdit?: (event: Event, edge: FcEdge) => void; 92 | nodeCallbacks?: UserNodeCallbacks; 93 | } 94 | export interface UserNodeCallbacks { 95 | nodeEdit?: (event: MouseEvent, node: FcNode) => void; 96 | doubleClick?: (event: MouseEvent, node: FcNode) => void; 97 | mouseDown?: (event: MouseEvent, node: FcNode) => void; 98 | mouseEnter?: (event: MouseEvent, node: FcNode) => void; 99 | mouseLeave?: (event: MouseEvent, node: FcNode) => void; 100 | } 101 | export interface FcCallbacks { 102 | nodeDragstart: (event: Event | any, node: FcNode) => void; 103 | nodeDragend: (event: Event | any) => void; 104 | edgeDragstart: (event: Event | any, connector: FcConnector) => void; 105 | edgeDragend: (event: Event | any) => void; 106 | edgeDrop: (event: Event | any, targetConnector: FcConnector) => boolean; 107 | edgeDragoverConnector: (event: Event | any, connector: FcConnector) => boolean; 108 | edgeDragoverMagnet: (event: Event | any, connector: FcConnector) => boolean; 109 | edgeDragleaveMagnet: (event: Event | any) => void; 110 | nodeMouseOver: (event: MouseEvent, node: FcNode) => void; 111 | nodeMouseOut: (event: MouseEvent, node: FcNode) => void; 112 | connectorMouseEnter: (event: MouseEvent, connector: FcConnector) => void; 113 | connectorMouseLeave: (event: MouseEvent, connector: FcConnector) => void; 114 | nodeClicked: (event: MouseEvent, node: FcNode) => void; 115 | } 116 | export interface FcAdjacentList { 117 | [id: string]: { 118 | incoming: number; 119 | outgoing: Array; 120 | }; 121 | } 122 | declare class BaseError { 123 | constructor(); 124 | } 125 | export declare class ModelvalidationError extends BaseError { 126 | message: string; 127 | constructor(message: string); 128 | } 129 | export declare const fcTopSort: (graph: FcModel) => Array | null; 130 | export {}; 131 | -------------------------------------------------------------------------------- /dist/ngx-flowchart/lib/ngx-flowchart.module.d.ts: -------------------------------------------------------------------------------- 1 | import * as i0 from "@angular/core"; 2 | import * as i1 from "./ngx-flowchart.component"; 3 | import * as i2 from "./magnet.directive"; 4 | import * as i3 from "./connector.directive"; 5 | import * as i4 from "./node.component"; 6 | import * as i5 from "./default-node.component"; 7 | import * as i6 from "@angular/common"; 8 | export declare class NgxFlowchartModule { 9 | static ɵfac: i0.ɵɵFactoryDeclaration; 10 | static ɵmod: i0.ɵɵNgModuleDeclaration; 11 | static ɵinj: i0.ɵɵInjectorDeclaration; 12 | } 13 | -------------------------------------------------------------------------------- /dist/ngx-flowchart/lib/node-dragging.service.d.ts: -------------------------------------------------------------------------------- 1 | import { FcModelService } from './model.service'; 2 | import { FcNode } from './ngx-flowchart.models'; 3 | export declare class FcNodeDraggingService { 4 | nodeDraggingScope: NodeDraggingScope; 5 | private dragOffsets; 6 | private draggedElements; 7 | private destinationHtmlElements; 8 | private oldDisplayStyles; 9 | private readonly modelService; 10 | private readonly automaticResize; 11 | private readonly dragAnimation; 12 | private readonly applyFunction; 13 | constructor(modelService: FcModelService, applyFunction: (fn: (...args: any[]) => T) => T, automaticResize: boolean, dragAnimation: string); 14 | private getCoordinate; 15 | private getXCoordinate; 16 | private getYCoordinate; 17 | private resizeCanvas; 18 | isDraggingNode(node: FcNode): boolean; 19 | dragstart(event: Event | any, node: FcNode): void; 20 | drop(event: Event | any): boolean; 21 | dragover(event: Event | any): boolean; 22 | dragend(event: Event | any): void; 23 | } 24 | export interface NodeDraggingScope { 25 | draggedNodes: Array; 26 | shadowElements: Array>; 27 | shadowDragStarted: boolean; 28 | dropElement: HTMLElement; 29 | } 30 | export interface NodeDropElement extends HTMLElement { 31 | offsetInfo?: { 32 | offsetX: number; 33 | offsetY: number; 34 | }; 35 | } 36 | export interface NodeDropScope { 37 | dropElement: NodeDropElement; 38 | } 39 | export interface DropNodeInfo { 40 | node: FcNode; 41 | dropTargetId: string; 42 | offsetX: number; 43 | offsetY: number; 44 | } 45 | -------------------------------------------------------------------------------- /dist/ngx-flowchart/lib/node.component.d.ts: -------------------------------------------------------------------------------- 1 | import { AfterViewInit, ComponentFactoryResolver, ElementRef, OnChanges, OnInit, SimpleChanges, ViewContainerRef } from '@angular/core'; 2 | import { FcCallbacks, FcConnector, FcNode, FcNodeComponentConfig, FcNodeRectInfo, UserNodeCallbacks } from './ngx-flowchart.models'; 3 | import { FcModelService } from './model.service'; 4 | import * as i0 from "@angular/core"; 5 | export declare class FcNodeContainerComponent implements OnInit, AfterViewInit, OnChanges { 6 | private nodeComponentConfig; 7 | private elementRef; 8 | private componentFactoryResolver; 9 | callbacks: FcCallbacks; 10 | userNodeCallbacks: UserNodeCallbacks; 11 | node: FcNode; 12 | selected: boolean; 13 | edit: boolean; 14 | underMouse: boolean; 15 | mouseOverConnector: FcConnector; 16 | modelservice: FcModelService; 17 | dragging: boolean; 18 | get nodeId(): string; 19 | get top(): string; 20 | get left(): string; 21 | nodeComponent: FcNodeComponent; 22 | nodeContentContainer: ViewContainerRef; 23 | constructor(nodeComponentConfig: FcNodeComponentConfig, elementRef: ElementRef, componentFactoryResolver: ComponentFactoryResolver); 24 | ngOnInit(): void; 25 | ngAfterViewInit(): void; 26 | ngOnChanges(changes: SimpleChanges): void; 27 | private updateNodeClass; 28 | private updateNodeComponent; 29 | private toggleClass; 30 | mousedown(event: MouseEvent): void; 31 | dragstart(event: Event | any): void; 32 | dragend(event: Event | any): void; 33 | click(event: MouseEvent): void; 34 | mouseover(event: MouseEvent): void; 35 | mouseout(event: MouseEvent): void; 36 | static ɵfac: i0.ɵɵFactoryDeclaration; 37 | static ɵcmp: i0.ɵɵComponentDeclaration; 38 | } 39 | export declare abstract class FcNodeComponent implements OnInit { 40 | callbacks: FcCallbacks; 41 | userNodeCallbacks: UserNodeCallbacks; 42 | node: FcNode; 43 | selected: boolean; 44 | edit: boolean; 45 | underMouse: boolean; 46 | mouseOverConnector: FcConnector; 47 | modelservice: FcModelService; 48 | dragging: boolean; 49 | flowchartConstants: { 50 | htmlPrefix: string; 51 | leftConnectorType: string; 52 | rightConnectorType: string; 53 | curvedStyle: string; 54 | lineStyle: string; 55 | dragAnimationRepaint: string; 56 | dragAnimationShadow: string; 57 | canvasClass: string; 58 | selectedClass: string; 59 | editClass: string; 60 | activeClass: string; 61 | hoverClass: string; 62 | draggingClass: string; 63 | edgeClass: string; 64 | edgeLabelClass: string; 65 | connectorClass: string; 66 | magnetClass: string; 67 | nodeClass: string; 68 | nodeOverlayClass: string; 69 | leftConnectorClass: string; 70 | rightConnectorClass: string; 71 | canvasResizeThreshold: number; 72 | canvasResizeStep: number; 73 | }; 74 | width: number; 75 | height: number; 76 | nodeRectInfo: FcNodeRectInfo; 77 | ngOnInit(): void; 78 | static ɵfac: i0.ɵɵFactoryDeclaration; 79 | static ɵdir: i0.ɵɵDirectiveDeclaration; 80 | } 81 | -------------------------------------------------------------------------------- /dist/ngx-flowchart/lib/rectangleselect.service.d.ts: -------------------------------------------------------------------------------- 1 | import { FcModelService } from './model.service'; 2 | export declare class FcRectangleSelectService { 3 | private readonly selectRect; 4 | private readonly modelService; 5 | private readonly selectElement; 6 | private readonly $canvasElement; 7 | private readonly $scrollParent; 8 | private readonly applyFunction; 9 | constructor(modelService: FcModelService, selectElement: HTMLElement, applyFunction: (fn: (...args: any[]) => T) => T); 10 | mousedown(e: MouseEvent): void; 11 | mousemove(e: MouseEvent): void; 12 | private updateScroll; 13 | mouseup(e: MouseEvent): void; 14 | private updateSelectRect; 15 | private selectObjects; 16 | } 17 | -------------------------------------------------------------------------------- /dist/ngx-flowchart/lib/scrollparent.d.ts: -------------------------------------------------------------------------------- 1 | declare const scrollparent: (node: HTMLElement) => HTMLElement; 2 | export default scrollparent; 3 | -------------------------------------------------------------------------------- /dist/ngx-flowchart/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngx-flowchart", 3 | "version": "2.0.0", 4 | "peerDependencies": { 5 | "@angular/common": "^18.2.6", 6 | "@angular/core": "^18.2.6", 7 | "jquery": "^3.7.1" 8 | }, 9 | "module": "fesm2022/ngx-flowchart.mjs", 10 | "typings": "index.d.ts", 11 | "exports": { 12 | "./package.json": { 13 | "default": "./package.json" 14 | }, 15 | ".": { 16 | "types": "./index.d.ts", 17 | "esm2022": "./esm2022/ngx-flowchart.mjs", 18 | "esm": "./esm2022/ngx-flowchart.mjs", 19 | "default": "./fesm2022/ngx-flowchart.mjs" 20 | } 21 | }, 22 | "sideEffects": false, 23 | "dependencies": { 24 | "tslib": "^2.3.0" 25 | } 26 | } -------------------------------------------------------------------------------- /dist/ngx-flowchart/public-api.d.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/ngx-flowchart.component'; 2 | export * from './lib/ngx-flowchart.module'; 3 | export * from './lib/ngx-flowchart.models'; 4 | export { FcNodeComponent } from './lib/node.component'; 5 | export { FcMagnetDirective } from './lib/magnet.directive'; 6 | export { FcConnectorDirective } from './lib/connector.directive'; 7 | export { DefaultFcNodeComponent } from './lib/default-node.component'; 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngx-flowchart-demo", 3 | "version": "3.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve --host 0.0.0.0 --open --port 4300", 7 | "build": "ng build ngx-flowchart --configuration production", 8 | "lint": "ng lint" 9 | }, 10 | "private": true, 11 | "peerDependencies": { 12 | "jquery": "^3.7.1" 13 | }, 14 | "devDependencies": { 15 | "@angular-devkit/build-angular": "18.2.6", 16 | "@angular-devkit/core": "18.2.6", 17 | "@angular-devkit/schematics": "18.2.6", 18 | "@angular-eslint/builder": "18.3.1", 19 | "@angular-eslint/eslint-plugin": "18.3.1", 20 | "@angular-eslint/eslint-plugin-template": "18.3.1", 21 | "@angular-eslint/schematics": "18.3.1", 22 | "@angular-eslint/template-parser": "18.3.1", 23 | "@angular/animations": "^18.2.6", 24 | "@angular/cdk": "^18.2.6", 25 | "@angular/cli": "^18.2.6", 26 | "@angular/common": "^18.2.6", 27 | "@angular/compiler": "^18.2.6", 28 | "@angular/compiler-cli": "^18.2.6", 29 | "@angular/core": "^18.2.6", 30 | "@angular/forms": "^18.2.6", 31 | "@angular/platform-browser": "^18.2.6", 32 | "@angular/platform-browser-dynamic": "^18.2.6", 33 | "@angular/router": "^18.2.6", 34 | "@types/jquery": "^3.5.31", 35 | "@types/node": "~20.16.9", 36 | "@typescript-eslint/eslint-plugin": "^8.7.0", 37 | "@typescript-eslint/parser": "^8.7.0", 38 | "@typescript-eslint/utils": "8.7.0", 39 | "eslint": "^9.11.1", 40 | "eslint-plugin-import": "latest", 41 | "eslint-plugin-jsdoc": "latest", 42 | "eslint-plugin-prefer-arrow": "latest", 43 | "jquery": "^3.7.1", 44 | "ng-packagr": "~18.2.1", 45 | "rxjs": "~7.8.1", 46 | "ts-node": "^10.9.2", 47 | "typescript": "~5.5.4", 48 | "zone.js": "~0.14.10" 49 | }, 50 | "dependencies": { 51 | "tslib": "^2.7.0" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /projects/ngx-flowchart/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../.eslintrc.json", 3 | "ignorePatterns": [ 4 | "!**/*" 5 | ], 6 | "overrides": [ 7 | { 8 | "files": [ 9 | "*.ts" 10 | ], 11 | "parserOptions": { 12 | "project": [ 13 | "projects/ngx-flowchart/tsconfig.lib.json" 14 | ], 15 | "createDefaultProgram": true 16 | }, 17 | "rules": { 18 | "@angular-eslint/directive-selector": [ 19 | "error", 20 | { 21 | "type": "attribute", 22 | "prefix": "lib", 23 | "style": "camelCase" 24 | } 25 | ], 26 | "@angular-eslint/component-selector": [ 27 | "error", 28 | { 29 | "type": "element", 30 | "prefix": "lib", 31 | "style": "kebab-case" 32 | } 33 | ], 34 | "no-underscore-dangle": "off" 35 | } 36 | }, 37 | { 38 | "files": [ 39 | "*.html" 40 | ], 41 | "rules": {} 42 | } 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /projects/ngx-flowchart/README.md: -------------------------------------------------------------------------------- 1 | # NgxFlowchart 2 | 3 | This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 14.2.0. 4 | 5 | ## Code scaffolding 6 | 7 | Run `ng generate component component-name --project ngx-flowchart` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project ngx-flowchart`. 8 | > Note: Don't forget to add `--project ngx-flowchart` or else it will be added to the default project in your `angular.json` file. 9 | 10 | ## Build 11 | 12 | Run `ng build ngx-flowchart` to build the project. The build artifacts will be stored in the `dist/` directory. 13 | 14 | ## Publishing 15 | 16 | After building your library with `ng build ngx-flowchart`, go to the dist folder `cd dist/ngx-flowchart` and run `npm publish`. 17 | 18 | ## Further help 19 | 20 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 21 | -------------------------------------------------------------------------------- /projects/ngx-flowchart/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/ngx-flowchart", 4 | "deleteDestPath": false, 5 | "lib": { 6 | "entryFile": "src/public-api.ts" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /projects/ngx-flowchart/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngx-flowchart", 3 | "version": "3.0.0", 4 | "peerDependencies": { 5 | "@angular/common": "^18.2.6", 6 | "@angular/core": "^18.2.6", 7 | "jquery": "^3.7.1" 8 | }, 9 | "devDependencies": { 10 | "@types/jquery": "^3.5.31" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /projects/ngx-flowchart/src/lib/connector.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, ElementRef, HostListener, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; 2 | import { FcCallbacks, FcConnector, FcConnectorRectInfo, FcNodeRectInfo, FlowchartConstants } from './ngx-flowchart.models'; 3 | import { FcModelService } from './model.service'; 4 | 5 | @Directive({ 6 | // eslint-disable-next-line @angular-eslint/directive-selector 7 | selector: '[fc-connector]' 8 | }) 9 | export class FcConnectorDirective implements OnInit, OnChanges { 10 | 11 | @Input() 12 | callbacks: FcCallbacks; 13 | 14 | @Input() 15 | modelservice: FcModelService; 16 | 17 | @Input() 18 | connector: FcConnector; 19 | 20 | @Input() 21 | nodeRectInfo: FcNodeRectInfo; 22 | 23 | @Input() 24 | mouseOverConnector: FcConnector; 25 | 26 | constructor(public elementRef: ElementRef) { 27 | } 28 | 29 | ngOnInit(): void { 30 | const element = $(this.elementRef.nativeElement); 31 | element.addClass(FlowchartConstants.connectorClass); 32 | if (this.modelservice.isEditable()) { 33 | element.attr('draggable', 'true'); 34 | this.updateConnectorClass(); 35 | } 36 | const connectorRectInfo: FcConnectorRectInfo = { 37 | type: this.connector.type, 38 | width: this.elementRef.nativeElement.offsetWidth, 39 | height: this.elementRef.nativeElement.offsetHeight, 40 | nodeRectInfo: this.nodeRectInfo 41 | }; 42 | this.modelservice.connectors.setConnectorRectInfo(this.connector.id, connectorRectInfo); 43 | } 44 | 45 | ngOnChanges(changes: SimpleChanges): void { 46 | let updateConnector = false; 47 | for (const propName of Object.keys(changes)) { 48 | const change = changes[propName]; 49 | if (!change.firstChange && change.currentValue !== change.previousValue) { 50 | if (propName === 'mouseOverConnector') { 51 | updateConnector = true; 52 | } 53 | } 54 | } 55 | if (updateConnector && this.modelservice.isEditable()) { 56 | this.updateConnectorClass(); 57 | } 58 | } 59 | 60 | private updateConnectorClass() { 61 | const element = $(this.elementRef.nativeElement); 62 | if (this.connector === this.mouseOverConnector) { 63 | element.addClass(FlowchartConstants.hoverClass); 64 | } else { 65 | element.removeClass(FlowchartConstants.hoverClass); 66 | } 67 | } 68 | 69 | @HostListener('dragover', ['$event']) 70 | dragover(event: Event | any) { 71 | // Skip - conflict with magnet 72 | /* if (this.modelservice.isEditable()) { 73 | return this.callbacks.edgeDragoverConnector(event, this.connector); 74 | }*/ 75 | } 76 | 77 | @HostListener('drop', ['$event']) 78 | drop(event: Event | any) { 79 | if (this.modelservice.isEditable()) { 80 | return this.callbacks.edgeDrop(event, this.connector); 81 | } 82 | } 83 | 84 | @HostListener('dragend', ['$event']) 85 | dragend(event: Event | any) { 86 | if (this.modelservice.isEditable()) { 87 | this.callbacks.edgeDragend(event); 88 | } 89 | } 90 | 91 | @HostListener('dragstart', ['$event']) 92 | dragstart(event: Event | any) { 93 | if (this.modelservice.isEditable()) { 94 | this.callbacks.edgeDragstart(event, this.connector); 95 | } 96 | } 97 | 98 | @HostListener('mouseenter', ['$event']) 99 | mouseenter(event: MouseEvent) { 100 | if (this.modelservice.isEditable()) { 101 | this.callbacks.connectorMouseEnter(event, this.connector); 102 | } 103 | } 104 | 105 | @HostListener('mouseleave', ['$event']) 106 | mouseleave(event: MouseEvent) { 107 | if (this.modelservice.isEditable()) { 108 | this.callbacks.connectorMouseLeave(event, this.connector); 109 | } 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /projects/ngx-flowchart/src/lib/default-node.component.html: -------------------------------------------------------------------------------- 1 |
3 |
4 |
5 |

{{ node.name }}

6 | 7 |
8 |
10 |
15 |
16 |
17 |
18 |
20 |
25 |
26 |
27 |
28 |
29 | 30 |
31 |
32 | × 33 |
34 |
35 | -------------------------------------------------------------------------------- /projects/ngx-flowchart/src/lib/default-node.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | 3 | .fc-node-overlay { 4 | position: absolute; 5 | pointer-events: none; 6 | left: 0; 7 | top: 0; 8 | right: 0; 9 | bottom: 0; 10 | background-color: #000; 11 | opacity: 0; 12 | } 13 | 14 | :host-context(.fc-hover) .fc-node-overlay { 15 | opacity: 0.25; 16 | transition: opacity .2s; 17 | } 18 | 19 | :host-context(.fc-selected) .fc-node-overlay { 20 | opacity: 0.25; 21 | } 22 | 23 | .innerNode { 24 | display: flex; 25 | justify-content: center; 26 | align-items: center; 27 | min-width: 100px; 28 | border-radius: 5px; 29 | 30 | background-color: #F15B26; 31 | color: #fff; 32 | font-size: 16px; 33 | pointer-events: none; 34 | 35 | p { 36 | padding: 0 15px; 37 | text-align: center; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /projects/ngx-flowchart/src/lib/default-node.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { FcNodeComponent } from './node.component'; 3 | 4 | @Component({ 5 | // eslint-disable-next-line @angular-eslint/component-selector 6 | selector: 'fc-default-node', 7 | templateUrl: './default-node.component.html', 8 | styleUrls: ['./default-node.component.scss'] 9 | }) 10 | export class DefaultFcNodeComponent extends FcNodeComponent { 11 | 12 | constructor() { 13 | super(); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /projects/ngx-flowchart/src/lib/edge-dragging.service.ts: -------------------------------------------------------------------------------- 1 | import { FcModelService } from './model.service'; 2 | import { FcConnector, FcCoords, FcEdge, FcModel, FlowchartConstants, ModelvalidationError } from './ngx-flowchart.models'; 3 | import { FcEdgeDrawingService } from './edge-drawing.service'; 4 | import { FcModelValidationService } from './modelvalidation.service'; 5 | 6 | export class FcEdgeDraggingService { 7 | 8 | edgeDragging: EdgeDragging = { 9 | isDragging: false, 10 | dragPoint1: null, 11 | dragPoint2: null, 12 | shadowDragStarted: false 13 | }; 14 | 15 | private draggedEdgeSource: FcConnector = null; 16 | private dragOffset: FcCoords = {}; 17 | private destinationHtmlElement: HTMLElement = null; 18 | private oldDisplayStyle = ''; 19 | 20 | private readonly modelValidation: FcModelValidationService; 21 | private readonly edgeDrawingService: FcEdgeDrawingService; 22 | private readonly modelService: FcModelService; 23 | private readonly model: FcModel; 24 | private readonly isValidEdgeCallback: (source: FcConnector, destination: FcConnector) => boolean; 25 | private readonly applyFunction: (fn: (...args: any[]) => T) => T; 26 | private readonly dragAnimation: string; 27 | private readonly edgeStyle: string; 28 | 29 | constructor(modelValidation: FcModelValidationService, 30 | edgeDrawingService: FcEdgeDrawingService, 31 | modelService: FcModelService, 32 | model: FcModel, 33 | isValidEdgeCallback: (source: FcConnector, destination: FcConnector) => boolean, 34 | applyFunction: (fn: (...args: any[]) => T) => T, 35 | dragAnimation: string, 36 | edgeStyle: string) { 37 | this.modelValidation = modelValidation; 38 | this.edgeDrawingService = edgeDrawingService; 39 | this.modelService = modelService; 40 | this.model = model; 41 | this.isValidEdgeCallback = isValidEdgeCallback || (() => true); 42 | this.applyFunction = applyFunction; 43 | this.dragAnimation = dragAnimation; 44 | this.edgeStyle = edgeStyle; 45 | } 46 | 47 | public dragstart(event: Event | any, connector: FcConnector) { 48 | let swapConnector: FcConnector; 49 | let dragLabel: string; 50 | let prevEdge: FcEdge; 51 | if (connector.type === FlowchartConstants.leftConnectorType) { 52 | for (const edge of this.model.edges) { 53 | if (edge.destination === connector.id) { 54 | swapConnector = this.modelService.connectors.getConnector(edge.source); 55 | dragLabel = edge.label; 56 | prevEdge = edge; 57 | this.applyFunction(() => { 58 | this.modelService.edges.delete(edge); 59 | }); 60 | break; 61 | } 62 | } 63 | } 64 | this.edgeDragging.isDragging = true; 65 | if (swapConnector !== undefined) { 66 | this.draggedEdgeSource = swapConnector; 67 | this.edgeDragging.dragPoint1 = this.modelService.connectors.getCenteredCoord(swapConnector.id); 68 | this.edgeDragging.dragLabel = dragLabel; 69 | this.edgeDragging.prevEdge = prevEdge; 70 | } else { 71 | this.draggedEdgeSource = connector; 72 | this.edgeDragging.dragPoint1 = this.modelService.connectors.getCenteredCoord(connector.id); 73 | } 74 | const canvas = this.modelService.canvasHtmlElement; 75 | if (!canvas) { 76 | throw new Error('No canvas while edgedraggingService found.'); 77 | } 78 | this.dragOffset.x = -canvas.getBoundingClientRect().left; 79 | this.dragOffset.y = -canvas.getBoundingClientRect().top; 80 | 81 | this.edgeDragging.dragPoint2 = { 82 | x: event.clientX + this.dragOffset.x, 83 | y: event.clientY + this.dragOffset.y 84 | }; 85 | const originalEvent: Event | any = (event as any).originalEvent || event; 86 | 87 | originalEvent.dataTransfer.setData('Text', 'Just to support firefox'); 88 | if (originalEvent.dataTransfer.setDragImage) { 89 | originalEvent.dataTransfer.setDragImage(this.modelService.getDragImage(), 0, 0); 90 | } else { 91 | this.destinationHtmlElement = event.target as HTMLElement; 92 | this.oldDisplayStyle = this.destinationHtmlElement.style.display; 93 | this.destinationHtmlElement.style.display = 'none'; 94 | if (this.dragAnimation === FlowchartConstants.dragAnimationShadow) { 95 | this.edgeDragging.shadowDragStarted = true; 96 | } 97 | } 98 | if (this.dragAnimation === FlowchartConstants.dragAnimationShadow) { 99 | if (this.edgeDragging.gElement === undefined) { 100 | this.edgeDragging.gElement = $(document.querySelectorAll('.shadow-svg-class')); 101 | this.edgeDragging.pathElement = $(document.querySelectorAll('.shadow-svg-class')).find('path'); 102 | this.edgeDragging.circleElement = $(document.querySelectorAll('.shadow-svg-class')).find('circle'); 103 | } 104 | 105 | this.edgeDragging.gElement.css('display', 'block'); 106 | this.edgeDragging.pathElement.attr('d', 107 | this.edgeDrawingService.getEdgeDAttribute(this.edgeDragging.dragPoint1, this.edgeDragging.dragPoint2, this.edgeStyle)); 108 | this.edgeDragging.circleElement.attr('cx', this.edgeDragging.dragPoint2.x); 109 | this.edgeDragging.circleElement.attr('cy', this.edgeDragging.dragPoint2.y); 110 | } 111 | event.stopPropagation(); 112 | } 113 | 114 | public dragover(event: Event | any) { 115 | if (this.edgeDragging.isDragging) { 116 | if (!this.edgeDragging.magnetActive && this.dragAnimation === FlowchartConstants.dragAnimationShadow) { 117 | if (this.destinationHtmlElement !== null) { 118 | this.destinationHtmlElement.style.display = this.oldDisplayStyle; 119 | } 120 | 121 | if (this.edgeDragging.shadowDragStarted) { 122 | this.applyFunction(() => { 123 | this.edgeDragging.shadowDragStarted = false; 124 | }); 125 | } 126 | 127 | this.edgeDragging.dragPoint2 = { 128 | x: event.clientX + this.dragOffset.x, 129 | y: event.clientY + this.dragOffset.y 130 | }; 131 | 132 | this.edgeDragging.pathElement.attr('d', 133 | this.edgeDrawingService.getEdgeDAttribute(this.edgeDragging.dragPoint1, this.edgeDragging.dragPoint2, this.edgeStyle)); 134 | this.edgeDragging.circleElement.attr('cx', this.edgeDragging.dragPoint2.x); 135 | this.edgeDragging.circleElement.attr('cy', this.edgeDragging.dragPoint2.y); 136 | 137 | } else if (this.dragAnimation === FlowchartConstants.dragAnimationRepaint) { 138 | return this.applyFunction(() => { 139 | if (this.destinationHtmlElement !== null) { 140 | this.destinationHtmlElement.style.display = this.oldDisplayStyle; 141 | } 142 | 143 | this.edgeDragging.dragPoint2 = { 144 | x: event.clientX + this.dragOffset.x, 145 | y: event.clientY + this.dragOffset.y 146 | }; 147 | }); 148 | } 149 | } 150 | } 151 | 152 | public dragoverConnector(event: Event | any, connector: FcConnector): boolean { 153 | if (this.edgeDragging.isDragging) { 154 | this.dragover(event); 155 | try { 156 | this.modelValidation.validateEdges(this.model.edges.concat([{ 157 | source: this.draggedEdgeSource.id, 158 | destination: connector.id 159 | }]), this.model.nodes); 160 | } catch (error) { 161 | if (error instanceof ModelvalidationError) { 162 | return true; 163 | } else { 164 | throw error; 165 | } 166 | } 167 | if (this.isValidEdgeCallback(this.draggedEdgeSource, connector)) { 168 | event.preventDefault(); 169 | event.stopPropagation(); 170 | return false; 171 | } 172 | } 173 | } 174 | 175 | public dragleaveMagnet(event: Event | any) { 176 | this.edgeDragging.magnetActive = false; 177 | } 178 | 179 | public dragoverMagnet(event: Event | any, connector: FcConnector): boolean { 180 | if (this.edgeDragging.isDragging) { 181 | this.dragover(event); 182 | try { 183 | this.modelValidation.validateEdges(this.model.edges.concat([{ 184 | source: this.draggedEdgeSource.id, 185 | destination: connector.id 186 | }]), this.model.nodes); 187 | } catch (error) { 188 | if (error instanceof ModelvalidationError) { 189 | return true; 190 | } else { 191 | throw error; 192 | } 193 | } 194 | if (this.isValidEdgeCallback(this.draggedEdgeSource, connector)) { 195 | if (this.dragAnimation === FlowchartConstants.dragAnimationShadow) { 196 | 197 | this.edgeDragging.magnetActive = true; 198 | 199 | this.edgeDragging.dragPoint2 = this.modelService.connectors.getCenteredCoord(connector.id); 200 | this.edgeDragging.pathElement.attr('d', 201 | this.edgeDrawingService.getEdgeDAttribute(this.edgeDragging.dragPoint1, this.edgeDragging.dragPoint2, this.edgeStyle)); 202 | this.edgeDragging.circleElement.attr('cx', this.edgeDragging.dragPoint2.x); 203 | this.edgeDragging.circleElement.attr('cy', this.edgeDragging.dragPoint2.y); 204 | 205 | event.preventDefault(); 206 | event.stopPropagation(); 207 | return false; 208 | } else if (this.dragAnimation === FlowchartConstants.dragAnimationRepaint) { 209 | return this.applyFunction(() => { 210 | this.edgeDragging.dragPoint2 = this.modelService.connectors.getCenteredCoord(connector.id); 211 | event.preventDefault(); 212 | event.stopPropagation(); 213 | return false; 214 | }); 215 | } 216 | } 217 | } 218 | } 219 | 220 | public dragend(event: Event | any) { 221 | if (this.edgeDragging.isDragging) { 222 | this.edgeDragging.isDragging = false; 223 | this.edgeDragging.dragPoint1 = null; 224 | this.edgeDragging.dragPoint2 = null; 225 | this.edgeDragging.dragLabel = null; 226 | event.stopPropagation(); 227 | 228 | if (this.dragAnimation === FlowchartConstants.dragAnimationShadow) { 229 | this.edgeDragging.gElement.css('display', 'none'); 230 | } 231 | if (this.edgeDragging.prevEdge) { 232 | const edge = this.edgeDragging.prevEdge; 233 | this.edgeDragging.prevEdge = null; 234 | this.applyFunction(() => { 235 | this.modelService.edges.putEdge(edge); 236 | }); 237 | } 238 | } 239 | } 240 | 241 | public drop(event: Event | any, targetConnector: FcConnector): boolean { 242 | if (this.edgeDragging.isDragging) { 243 | try { 244 | this.modelValidation.validateEdges(this.model.edges.concat([{ 245 | source: this.draggedEdgeSource.id, 246 | destination: targetConnector.id 247 | }]), this.model.nodes); 248 | } catch (error) { 249 | if (error instanceof ModelvalidationError) { 250 | return true; 251 | } else { 252 | throw error; 253 | } 254 | } 255 | 256 | if (this.isValidEdgeCallback(this.draggedEdgeSource, targetConnector)) { 257 | this.edgeDragging.prevEdge = null; 258 | this.modelService.edges._addEdge(event, this.draggedEdgeSource, targetConnector, this.edgeDragging.dragLabel); 259 | event.stopPropagation(); 260 | event.preventDefault(); 261 | return false; 262 | } 263 | } 264 | } 265 | } 266 | 267 | export interface EdgeDragging { 268 | isDragging: boolean; 269 | shadowDragStarted: boolean; 270 | dragPoint1: FcCoords; 271 | dragPoint2: FcCoords; 272 | dragLabel?: string; 273 | prevEdge?: FcEdge; 274 | magnetActive?: boolean; 275 | gElement?: JQuery; 276 | pathElement?: JQuery; 277 | circleElement?: JQuery; 278 | } 279 | -------------------------------------------------------------------------------- /projects/ngx-flowchart/src/lib/edge-drawing.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { FcCoords, FlowchartConstants } from './ngx-flowchart.models'; 3 | 4 | @Injectable() 5 | export class FcEdgeDrawingService { 6 | 7 | constructor() { 8 | } 9 | 10 | public getEdgeDAttribute(pt1: FcCoords, pt2: FcCoords, style: string): string { 11 | let dAddribute = `M ${pt1.x}, ${pt1.y} `; 12 | if (style === FlowchartConstants.curvedStyle) { 13 | const sourceTangent = this.computeEdgeSourceTangent(pt1, pt2); 14 | const destinationTangent = this.computeEdgeDestinationTangent(pt1, pt2); 15 | dAddribute += `C ${sourceTangent.x}, ${sourceTangent.y} ${(destinationTangent.x - 50)}, ${destinationTangent.y} ${pt2.x}, ${pt2.y}`; 16 | } else { 17 | dAddribute += `L ${pt2.x}, ${pt2.y}`; 18 | } 19 | return dAddribute; 20 | } 21 | 22 | public getEdgeCenter(pt1: FcCoords, pt2: FcCoords): FcCoords { 23 | return { 24 | x: (pt1.x + pt2.x) / 2, 25 | y: (pt1.y + pt2.y) / 2 26 | }; 27 | } 28 | 29 | private computeEdgeTangentOffset(pt1: FcCoords, pt2: FcCoords): number { 30 | return (pt2.y - pt1.y) / 2; 31 | } 32 | 33 | private computeEdgeSourceTangent(pt1: FcCoords, pt2: FcCoords): FcCoords { 34 | return { 35 | x: pt1.x, 36 | y: pt1.y + this.computeEdgeTangentOffset(pt1, pt2) 37 | }; 38 | } 39 | 40 | private computeEdgeDestinationTangent(pt1: FcCoords, pt2: FcCoords): FcCoords { 41 | return { 42 | x: pt2.x, 43 | y: pt2.y - this.computeEdgeTangentOffset(pt1, pt2) 44 | }; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /projects/ngx-flowchart/src/lib/magnet.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, ElementRef, HostListener, Input, OnInit } from '@angular/core'; 2 | import { FcCallbacks, FcConnector, FlowchartConstants } from './ngx-flowchart.models'; 3 | 4 | @Directive({ 5 | // eslint-disable-next-line @angular-eslint/directive-selector 6 | selector: '[fc-magnet]' 7 | }) 8 | export class FcMagnetDirective implements OnInit { 9 | 10 | @Input() 11 | callbacks: FcCallbacks; 12 | 13 | @Input() 14 | connector: FcConnector; 15 | 16 | constructor(public elementRef: ElementRef) { 17 | } 18 | 19 | ngOnInit(): void { 20 | const element = $(this.elementRef.nativeElement); 21 | element.addClass(FlowchartConstants.magnetClass); 22 | } 23 | 24 | @HostListener('dragover', ['$event']) 25 | dragover(event: Event | any) { 26 | return this.callbacks.edgeDragoverMagnet(event, this.connector); 27 | } 28 | 29 | @HostListener('dragleave', ['$event']) 30 | dragleave(event: Event | any) { 31 | this.callbacks.edgeDragleaveMagnet(event); 32 | } 33 | 34 | @HostListener('drop', ['$event']) 35 | drop(event: Event | any) { 36 | return this.callbacks.edgeDrop(event, this.connector); 37 | } 38 | 39 | @HostListener('dragend', ['$event']) 40 | dragend(event: Event | any) { 41 | this.callbacks.edgeDragend(event); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /projects/ngx-flowchart/src/lib/modelvalidation.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { FcConnector, FcEdge, FcModel, FcNode, fcTopSort, ModelvalidationError } from './ngx-flowchart.models'; 3 | 4 | @Injectable() 5 | export class FcModelValidationService { 6 | 7 | constructor() { } 8 | 9 | public validateModel(model: FcModel): FcModel { 10 | this.validateNodes(model.nodes); 11 | this._validateEdges(model.edges, model.nodes); 12 | return model; 13 | } 14 | 15 | public validateNodes(nodes: Array): Array { 16 | const ids: string[] = []; 17 | nodes.forEach((node) => { 18 | this.validateNode(node); 19 | if (ids.indexOf(node.id) !== -1) { 20 | throw new ModelvalidationError('Id not unique.'); 21 | } 22 | ids.push(node.id); 23 | }); 24 | const connectorIds: string[] = []; 25 | nodes.forEach((node) => { 26 | node.connectors.forEach((connector) => { 27 | if (connectorIds.indexOf(connector.id) !== -1) { 28 | throw new ModelvalidationError('Id not unique.'); 29 | } 30 | connectorIds.push(connector.id); 31 | }); 32 | }); 33 | return nodes; 34 | } 35 | 36 | public validateNode(node: FcNode): FcNode { 37 | if (node.id === undefined) { 38 | throw new ModelvalidationError('Id not valid.'); 39 | } 40 | if (typeof node.name !== 'string') { 41 | throw new ModelvalidationError('Name not valid.'); 42 | } 43 | if (typeof node.x !== 'number' || node.x < 0 || Math.round(node.x) !== node.x) { 44 | throw new ModelvalidationError('Coordinates not valid.'); 45 | } 46 | if (typeof node.y !== 'number' || node.y < 0 || Math.round(node.y) !== node.y) { 47 | throw new ModelvalidationError('Coordinates not valid.'); 48 | } 49 | if (!Array.isArray(node.connectors)) { 50 | throw new ModelvalidationError('Connectors not valid.'); 51 | } 52 | node.connectors.forEach((connector) => { 53 | this.validateConnector(connector); 54 | }); 55 | return node; 56 | } 57 | 58 | private _validateEdges(edges: Array, nodes: Array): Array { 59 | edges.forEach((edge) => { 60 | this._validateEdge(edge, nodes); 61 | }); 62 | edges.forEach((edge1, index1) => { 63 | edges.forEach((edge2, index2) => { 64 | if (index1 !== index2) { 65 | if ((edge1.source === edge2.source && edge1.destination === edge2.destination) || 66 | (edge1.source === edge2.destination && edge1.destination === edge2.source)) { 67 | throw new ModelvalidationError('Duplicated edge.'); 68 | } 69 | } 70 | }); 71 | }); 72 | if (fcTopSort({nodes, edges}) === null) { 73 | throw new ModelvalidationError('Graph has a circle.'); 74 | } 75 | return edges; 76 | } 77 | 78 | public validateEdges(edges: Array, nodes: Array): Array { 79 | this.validateNodes(nodes); 80 | return this._validateEdges(edges, nodes); 81 | } 82 | 83 | private _validateEdge(edge: FcEdge, nodes: Array): FcEdge { 84 | if (edge.source === undefined) { 85 | throw new ModelvalidationError('Source not valid.'); 86 | } 87 | if (edge.destination === undefined) { 88 | throw new ModelvalidationError('Destination not valid.'); 89 | } 90 | if (edge.source === edge.destination) { 91 | throw new ModelvalidationError('Edge with same source and destination connectors.'); 92 | } 93 | const sourceNode = nodes.filter((node) => node.connectors.some((connector) => connector.id === edge.source))[0]; 94 | if (sourceNode === undefined) { 95 | throw new ModelvalidationError('Source not valid.'); 96 | } 97 | const destinationNode = nodes.filter((node) => node.connectors.some((connector) => connector.id === edge.destination))[0]; 98 | if (destinationNode === undefined) { 99 | throw new ModelvalidationError('Destination not valid.'); 100 | } 101 | if (sourceNode === destinationNode) { 102 | throw new ModelvalidationError('Edge with same source and destination nodes.'); 103 | } 104 | return edge; 105 | } 106 | 107 | public validateEdge(edge: FcEdge, nodes: Array): FcEdge { 108 | this.validateNodes(nodes); 109 | return this._validateEdge(edge, nodes); 110 | } 111 | 112 | public validateConnector(connector: FcConnector): FcConnector { 113 | if (connector.id === undefined) { 114 | throw new ModelvalidationError('Id not valid.'); 115 | } 116 | if (connector.type === undefined || connector.type === null || typeof connector.type !== 'string') { 117 | throw new ModelvalidationError('Type not valid.'); 118 | } 119 | return connector; 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /projects/ngx-flowchart/src/lib/mouseover.service.ts: -------------------------------------------------------------------------------- 1 | import { FcConnector, FcEdge, FcNode } from './ngx-flowchart.models'; 2 | 3 | export class FcMouseOverService { 4 | 5 | mouseoverscope: MouseOverScope = { 6 | connector: null, 7 | edge: null, 8 | node: null 9 | }; 10 | 11 | private readonly applyFunction: (fn: (...args: any[]) => T) => T; 12 | 13 | constructor(applyFunction: (fn: (...args: any[]) => T) => T) { 14 | this.applyFunction = applyFunction; 15 | } 16 | 17 | public nodeMouseOver(event: MouseEvent, node: FcNode) { 18 | return this.applyFunction(() => { 19 | this.mouseoverscope.node = node; 20 | }); 21 | } 22 | 23 | public nodeMouseOut(event: MouseEvent, node: FcNode) { 24 | return this.applyFunction(() => { 25 | this.mouseoverscope.node = null; 26 | }); 27 | } 28 | 29 | public connectorMouseEnter(event: MouseEvent, connector: FcConnector) { 30 | return this.applyFunction(() => { 31 | this.mouseoverscope.connector = connector; 32 | }); 33 | } 34 | 35 | public connectorMouseLeave(event: MouseEvent, connector: FcConnector) { 36 | return this.applyFunction(() => { 37 | this.mouseoverscope.connector = null; 38 | }); 39 | } 40 | 41 | public edgeMouseEnter(event: MouseEvent, edge: FcEdge) { 42 | this.mouseoverscope.edge = edge; 43 | } 44 | 45 | public edgeMouseLeave(event: MouseEvent, edge: FcEdge) { 46 | this.mouseoverscope.edge = null; 47 | } 48 | } 49 | 50 | export interface MouseOverScope { 51 | connector: FcConnector; 52 | edge: FcEdge; 53 | node: FcNode; 54 | } 55 | -------------------------------------------------------------------------------- /projects/ngx-flowchart/src/lib/ngx-flowchart.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 26 | 27 | 28 | 29 | 31 | 34 | 35 | 36 | 42 | 43 | 44 | 54 | 55 | 56 |
62 |
63 | {{edgeDraggingService.edgeDragging.dragLabel}} 64 |
65 |
66 |
83 |
84 |
85 | 86 |
87 |
88 | × 89 |
90 | {{edge.label}} 91 |
92 |
93 | 95 |
96 | -------------------------------------------------------------------------------- /projects/ngx-flowchart/src/lib/ngx-flowchart.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | display: block; 3 | position: relative; 4 | width: 100%; 5 | height: 100%; 6 | background-size: 25px 25px; 7 | background-image: linear-gradient(to right, rgba(0, 0, 0, .1) 1px, transparent 1px), linear-gradient(to bottom, rgba(0, 0, 0, .1) 1px, transparent 1px); 8 | background-color: transparent; 9 | min-width: 100%; 10 | min-height: 100%; 11 | -webkit-touch-callout: none; 12 | -webkit-user-select: none; 13 | -khtml-user-select: none; 14 | -moz-user-select: none; 15 | -ms-user-select: none; 16 | user-select: none; 17 | 18 | .fc-canvas-container { 19 | display: block; 20 | position: relative; 21 | width: 100%; 22 | height: 100%; 23 | svg.fc-canvas-svg { 24 | display: block; 25 | position: relative; 26 | width: 100%; 27 | height: 100%; 28 | } 29 | } 30 | 31 | .fc-edge { 32 | stroke: gray; 33 | stroke-width: 4; 34 | transition: stroke-width .2s; 35 | fill: transparent; 36 | &.fc-hover { 37 | stroke: gray; 38 | stroke-width: 6; 39 | fill: transparent; 40 | } 41 | &.fc-selected { 42 | stroke: red; 43 | stroke-width: 4; 44 | fill: transparent; 45 | } 46 | &.fc-active { 47 | animation: dash 3s linear infinite; 48 | stroke-dasharray: 20; 49 | } 50 | &.fc-dragging { 51 | pointer-events: none; 52 | } 53 | } 54 | 55 | .fc-arrow-marker polygon { 56 | stroke: gray; 57 | fill: gray; 58 | } 59 | 60 | .fc-arrow-marker-selected polygon { 61 | stroke: red; 62 | fill: red; 63 | } 64 | 65 | .edge-endpoint { 66 | fill: gray; 67 | } 68 | 69 | .fc-noselect { 70 | -webkit-touch-callout: none; /* iOS Safari */ 71 | -webkit-user-select: none; /* Safari */ 72 | -khtml-user-select: none; /* Konqueror HTML */ 73 | -moz-user-select: none; /* Firefox */ 74 | -ms-user-select: none; /* Internet Explorer/Edge */ 75 | user-select: none; /* Non-prefixed version, currently 76 | supported by Chrome and Opera */ 77 | } 78 | 79 | .fc-edge-label { 80 | position: absolute; 81 | opacity: 0.8; 82 | transition: transform .2s; 83 | transform-origin: bottom left; 84 | margin: 0 auto; 85 | .fc-edge-label-text { 86 | position: absolute; 87 | -webkit-transform: translate(-50%, -50%); 88 | transform: translate(-50%, -50%); 89 | white-space: nowrap; 90 | text-align: center; 91 | font-size: 16px; 92 | span { 93 | cursor: default; 94 | border: solid #ff3d00; 95 | border-radius: 10px; 96 | color: #ff3d00; 97 | background-color: #fff; 98 | padding: 3px 5px; 99 | } 100 | } 101 | .fc-nodeedit { 102 | top: -30px; 103 | right: 14px; 104 | } 105 | .fc-nodedelete { 106 | top: -30px; 107 | right: -13px; 108 | } 109 | &.fc-hover { 110 | transform: scale(1.25); 111 | } 112 | &.fc-selected, 113 | &.fc-edit { 114 | .fc-edge-label-text { 115 | span { 116 | border: solid red; 117 | color: #fff; 118 | font-weight: 600; 119 | background-color: red; 120 | } 121 | } 122 | } 123 | } 124 | 125 | .fc-select-rectangle { 126 | border: 2px dashed #5262ff; 127 | position: absolute; 128 | background: rgba(20,125,255,0.1); 129 | z-index: 2; 130 | } 131 | } 132 | 133 | @keyframes dash { 134 | from { 135 | stroke-dashoffset: 500; 136 | } 137 | } 138 | 139 | :host ::ng-deep { 140 | 141 | .fc-nodeedit { 142 | display: none; 143 | font-size: 15px; 144 | } 145 | 146 | .fc-nodedelete { 147 | display: none; 148 | font-size: 18px; 149 | } 150 | 151 | .fc-edit { 152 | .fc-nodeedit, 153 | .fc-nodedelete { 154 | display: block; 155 | position: absolute; 156 | border: solid 2px #eee; 157 | 158 | border-radius: 50%; 159 | font-weight: 600; 160 | line-height: 20px; 161 | 162 | height: 20px; 163 | padding-top: 2px; 164 | width: 22px; 165 | 166 | background: #494949; 167 | color: #fff; 168 | text-align: center; 169 | vertical-align: bottom; 170 | 171 | cursor: pointer; 172 | } 173 | .fc-nodeedit { 174 | top: -24px; 175 | right: 16px; 176 | } 177 | .fc-nodedelete { 178 | top: -24px; 179 | right: -13px; 180 | } 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /projects/ngx-flowchart/src/lib/ngx-flowchart.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ChangeDetectionStrategy, 3 | ChangeDetectorRef, 4 | Component, 5 | DoCheck, 6 | ElementRef, 7 | EventEmitter, 8 | HostBinding, 9 | HostListener, 10 | Input, 11 | IterableDiffer, 12 | IterableDiffers, 13 | NgZone, 14 | OnInit, 15 | Output 16 | } from '@angular/core'; 17 | import { FcCallbacks, FcEdge, FcModel, FcNode, FlowchartConstants, UserCallbacks, UserNodeCallbacks } from './ngx-flowchart.models'; 18 | import { FcModelService } from './model.service'; 19 | import { FcModelValidationService } from './modelvalidation.service'; 20 | import { FcNodeDraggingService } from './node-dragging.service'; 21 | import { FcEdgeDrawingService } from './edge-drawing.service'; 22 | import { FcEdgeDraggingService } from './edge-dragging.service'; 23 | import { FcMouseOverService } from './mouseover.service'; 24 | import { FcRectangleSelectService } from './rectangleselect.service'; 25 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; 26 | import { Subject } from 'rxjs'; 27 | import { debounceTime } from 'rxjs/operators'; 28 | 29 | @Component({ 30 | // eslint-disable-next-line @angular-eslint/component-selector 31 | selector: 'fc-canvas', 32 | templateUrl: './ngx-flowchart.component.html', 33 | styleUrls: ['./ngx-flowchart.component.scss'], 34 | changeDetection: ChangeDetectionStrategy.OnPush 35 | }) 36 | export class NgxFlowchartComponent implements OnInit, DoCheck { 37 | 38 | @HostBinding('attr.class') 39 | get canvasClass(): string { 40 | return FlowchartConstants.canvasClass; 41 | } 42 | 43 | @Input() 44 | model: FcModel; 45 | 46 | @Input() 47 | selectedObjects: any[]; 48 | 49 | @Input() 50 | edgeStyle: string; 51 | 52 | @Input() 53 | userCallbacks: UserCallbacks; 54 | 55 | @Input() 56 | automaticResize: boolean; 57 | 58 | @Input() 59 | dragAnimation: string; 60 | 61 | @Input() 62 | nodeWidth: number; 63 | 64 | @Input() 65 | nodeHeight: number; 66 | 67 | @Input() 68 | dropTargetId: string; 69 | 70 | @Output() 71 | modelChanged = new EventEmitter(); 72 | 73 | private fitModelSizeByDefaultValue = true; 74 | get fitModelSizeByDefault(): boolean { 75 | return this.fitModelSizeByDefaultValue; 76 | } 77 | @Input() 78 | set fitModelSizeByDefault(value: boolean) { 79 | this.fitModelSizeByDefaultValue = coerceBooleanProperty(value); 80 | } 81 | 82 | callbacks: FcCallbacks; 83 | 84 | userNodeCallbacks: UserNodeCallbacks; 85 | 86 | modelService: FcModelService; 87 | nodeDraggingService: FcNodeDraggingService; 88 | edgeDraggingService: FcEdgeDraggingService; 89 | mouseoverService: FcMouseOverService; 90 | rectangleSelectService: FcRectangleSelectService; 91 | 92 | arrowDefId: string; 93 | arrowDefIdSelected: string; 94 | 95 | flowchartConstants = FlowchartConstants; 96 | 97 | private nodesDiffer: IterableDiffer = this.differs.find([]).create((index, item) => item); 98 | 99 | private edgesDiffer: IterableDiffer = this.differs.find([]).create((index, item) => item); 100 | 101 | private readonly detectChangesSubject = new Subject(); 102 | 103 | constructor(private elementRef: ElementRef, 104 | private differs: IterableDiffers, 105 | private modelValidation: FcModelValidationService, 106 | public edgeDrawingService: FcEdgeDrawingService, 107 | private cd: ChangeDetectorRef, 108 | private zone: NgZone) { 109 | this.arrowDefId = 'arrow-' + Math.random(); 110 | this.arrowDefIdSelected = this.arrowDefId + '-selected'; 111 | this.detectChangesSubject 112 | .pipe(debounceTime(50)) 113 | .subscribe(() => this.cd.detectChanges()); 114 | } 115 | 116 | ngOnInit() { 117 | if (!this.dropTargetId && this.edgeStyle !== FlowchartConstants.curvedStyle && this.edgeStyle !== FlowchartConstants.lineStyle) { 118 | throw new Error('edgeStyle not supported.'); 119 | } 120 | this.nodeHeight = this.nodeHeight || 200; 121 | this.nodeWidth = this.nodeWidth || 200; 122 | this.dragAnimation = this.dragAnimation || FlowchartConstants.dragAnimationRepaint; 123 | this.userCallbacks = this.userCallbacks || {}; 124 | this.automaticResize = this.automaticResize || false; 125 | 126 | for (const key of Object.keys(this.userCallbacks)) { 127 | const callback = this.userCallbacks[key]; 128 | if (typeof callback !== 'function' && key !== 'nodeCallbacks') { 129 | throw new Error('All callbacks should be functions.'); 130 | } 131 | } 132 | 133 | this.userNodeCallbacks = this.userCallbacks.nodeCallbacks; 134 | 135 | const element = $(this.elementRef.nativeElement); 136 | 137 | this.modelService = new FcModelService(this.modelValidation, this.model, this.modelChanged, 138 | this.detectChangesSubject, this.selectedObjects, 139 | this.userCallbacks.dropNode, this.userCallbacks.createEdge, this.userCallbacks.edgeAdded, this.userCallbacks.nodeRemoved, 140 | this.userCallbacks.edgeRemoved, element[0], element[0].querySelector('svg')); 141 | 142 | if (this.dropTargetId) { 143 | this.modelService.dropTargetId = this.dropTargetId; 144 | } 145 | 146 | const applyFunction = this.zone.run.bind(this.zone); 147 | 148 | this.nodeDraggingService = new FcNodeDraggingService(this.modelService, applyFunction, 149 | this.automaticResize, this.dragAnimation); 150 | 151 | this.edgeDraggingService = new FcEdgeDraggingService(this.modelValidation, this.edgeDrawingService, this.modelService, 152 | this.model, this.userCallbacks.isValidEdge || null, applyFunction, 153 | this.dragAnimation, this.edgeStyle); 154 | 155 | this.mouseoverService = new FcMouseOverService(applyFunction); 156 | 157 | this.rectangleSelectService = new FcRectangleSelectService(this.modelService, 158 | element[0].querySelector('#select-rectangle'), applyFunction); 159 | 160 | this.callbacks = { 161 | nodeDragstart: this.nodeDraggingService.dragstart.bind(this.nodeDraggingService), 162 | nodeDragend: this.nodeDraggingService.dragend.bind(this.nodeDraggingService), 163 | edgeDragstart: this.edgeDraggingService.dragstart.bind(this.edgeDraggingService), 164 | edgeDragend: this.edgeDraggingService.dragend.bind(this.edgeDraggingService), 165 | edgeDrop: this.edgeDraggingService.drop.bind(this.edgeDraggingService), 166 | edgeDragoverConnector: this.edgeDraggingService.dragoverConnector.bind(this.edgeDraggingService), 167 | edgeDragoverMagnet: this.edgeDraggingService.dragoverMagnet.bind(this.edgeDraggingService), 168 | edgeDragleaveMagnet: this.edgeDraggingService.dragleaveMagnet.bind(this.edgeDraggingService), 169 | nodeMouseOver: this.mouseoverService.nodeMouseOver.bind(this.mouseoverService), 170 | nodeMouseOut: this.mouseoverService.nodeMouseOut.bind(this.mouseoverService), 171 | connectorMouseEnter: this.mouseoverService.connectorMouseEnter.bind(this.mouseoverService), 172 | connectorMouseLeave: this.mouseoverService.connectorMouseLeave.bind(this.mouseoverService), 173 | nodeClicked: (event, node) => { 174 | this.modelService.nodes.handleClicked(node, event.ctrlKey); 175 | event.stopPropagation(); 176 | event.preventDefault(); 177 | } 178 | }; 179 | this.adjustCanvasSize(this.fitModelSizeByDefault); 180 | } 181 | 182 | ngDoCheck(): void { 183 | if (this.model) { 184 | const nodesChange = this.nodesDiffer.diff(this.model.nodes); 185 | const edgesChange = this.edgesDiffer.diff(this.model.edges); 186 | let nodesChanged = false; 187 | let edgesChanged = false; 188 | if (nodesChange !== null) { 189 | nodesChange.forEachAddedItem(() => { 190 | nodesChanged = true; 191 | }); 192 | nodesChange.forEachRemovedItem(() => { 193 | nodesChanged = true; 194 | }); 195 | } 196 | if (edgesChange !== null) { 197 | edgesChange.forEachAddedItem(() => { 198 | edgesChanged = true; 199 | }); 200 | edgesChange.forEachRemovedItem(() => { 201 | edgesChanged = true; 202 | }); 203 | } 204 | if (nodesChanged) { 205 | this.adjustCanvasSize(this.fitModelSizeByDefault); 206 | } 207 | if (nodesChanged || edgesChanged) { 208 | this.detectChangesSubject.next(null); 209 | } 210 | } 211 | } 212 | 213 | getEdgeDAttribute(edge: FcEdge): string { 214 | return this.edgeDrawingService.getEdgeDAttribute(this.modelService.edges.sourceCoord(edge), 215 | this.modelService.edges.destCoord(edge), this.edgeStyle); 216 | } 217 | 218 | public adjustCanvasSize(fit?: boolean) { 219 | let maxX = 0; 220 | let maxY = 0; 221 | const element = $(this.elementRef.nativeElement); 222 | this.model.nodes.forEach((node) => { 223 | maxX = Math.max(node.x + this.nodeWidth, maxX); 224 | maxY = Math.max(node.y + this.nodeHeight, maxY); 225 | }); 226 | let width; 227 | let height; 228 | if (fit) { 229 | width = maxX; 230 | height = maxY; 231 | } else { 232 | width = Math.max(maxX, element.prop('offsetWidth')); 233 | height = Math.max(maxY, element.prop('offsetHeight')); 234 | } 235 | element.css('width', width + 'px'); 236 | element.css('height', height + 'px'); 237 | } 238 | 239 | canvasClick(event: MouseEvent) {} 240 | 241 | edgeMouseDown(event: MouseEvent, edge: FcEdge) { 242 | event.stopPropagation(); 243 | } 244 | 245 | edgeClick(event: MouseEvent, edge: FcEdge) { 246 | this.modelService.edges.handleEdgeMouseClick(edge, event.ctrlKey); 247 | event.stopPropagation(); 248 | event.preventDefault(); 249 | } 250 | 251 | edgeRemove(event: Event, edge: FcEdge) { 252 | this.modelService.edges.delete(edge); 253 | event.stopPropagation(); 254 | event.preventDefault(); 255 | } 256 | 257 | edgeEdit(event: Event, edge: FcEdge) { 258 | if (this.userCallbacks.edgeEdit) { 259 | this.userCallbacks.edgeEdit(event, edge); 260 | } 261 | } 262 | 263 | edgeDoubleClick(event: MouseEvent, edge: FcEdge) { 264 | if (this.userCallbacks.edgeDoubleClick) { 265 | this.userCallbacks.edgeDoubleClick(event, edge); 266 | } 267 | } 268 | 269 | edgeMouseOver(event: MouseEvent, edge: FcEdge) { 270 | if (this.userCallbacks.edgeMouseOver) { 271 | this.userCallbacks.edgeMouseOver(event, edge); 272 | } 273 | } 274 | 275 | edgeMouseEnter(event: MouseEvent, edge: FcEdge) { 276 | this.mouseoverService.edgeMouseEnter(event, edge); 277 | } 278 | 279 | edgeMouseLeave(event: MouseEvent, edge: FcEdge) { 280 | this.mouseoverService.edgeMouseLeave(event, edge); 281 | } 282 | 283 | @HostListener('dragover', ['$event']) 284 | dragover(event: Event | any) { 285 | this.nodeDraggingService.dragover(event); 286 | this.edgeDraggingService.dragover(event); 287 | } 288 | 289 | @HostListener('drop', ['$event']) 290 | drop(event: Event | any) { 291 | if (event.preventDefault) { 292 | event.preventDefault(); 293 | } 294 | if (event.stopPropagation) { 295 | event.stopPropagation(); 296 | } 297 | this.nodeDraggingService.drop(event); 298 | } 299 | 300 | @HostListener('mousedown', ['$event']) 301 | mousedown(event: MouseEvent) { 302 | this.rectangleSelectService.mousedown(event); 303 | } 304 | 305 | @HostListener('mousemove', ['$event']) 306 | mousemove(event: MouseEvent) { 307 | this.rectangleSelectService.mousemove(event); 308 | } 309 | 310 | @HostListener('mouseup', ['$event']) 311 | mouseup(event: MouseEvent) { 312 | this.rectangleSelectService.mouseup(event); 313 | } 314 | 315 | } 316 | -------------------------------------------------------------------------------- /projects/ngx-flowchart/src/lib/ngx-flowchart.models.ts: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rxjs'; 2 | import { InjectionToken, Type } from '@angular/core'; 3 | import { FcNodeComponent } from './node.component'; 4 | 5 | export const FC_NODE_COMPONENT_CONFIG = new InjectionToken('fc-node.component.config'); 6 | 7 | export interface FcNodeComponentConfig { 8 | nodeComponentType: Type; 9 | } 10 | 11 | const htmlPrefix = 'fc'; 12 | const leftConnectorType = 'leftConnector'; 13 | const rightConnectorType = 'rightConnector'; 14 | 15 | // eslint-disable-next-line @typescript-eslint/naming-convention 16 | export const FlowchartConstants = { 17 | htmlPrefix, 18 | leftConnectorType, 19 | rightConnectorType, 20 | curvedStyle: 'curved', 21 | lineStyle: 'line', 22 | dragAnimationRepaint: 'repaint', 23 | dragAnimationShadow: 'shadow', 24 | canvasClass: htmlPrefix + '-canvas', 25 | selectedClass: htmlPrefix + '-selected', 26 | editClass: htmlPrefix + '-edit', 27 | activeClass: htmlPrefix + '-active', 28 | hoverClass: htmlPrefix + '-hover', 29 | draggingClass: htmlPrefix + '-dragging', 30 | edgeClass: htmlPrefix + '-edge', 31 | edgeLabelClass: htmlPrefix + '-edge-label', 32 | connectorClass: htmlPrefix + '-connector', 33 | magnetClass: htmlPrefix + '-magnet', 34 | nodeClass: htmlPrefix + '-node', 35 | nodeOverlayClass: htmlPrefix + '-node-overlay', 36 | leftConnectorClass: htmlPrefix + '-' + leftConnectorType + 's', 37 | rightConnectorClass: htmlPrefix + '-' + rightConnectorType + 's', 38 | canvasResizeThreshold: 200, 39 | canvasResizeStep: 200 40 | }; 41 | 42 | 43 | export interface FcCoords { 44 | x?: number; 45 | y?: number; 46 | } 47 | 48 | export interface FcRectBox { 49 | top: number; 50 | left: number; 51 | right: number; 52 | bottom: number; 53 | } 54 | 55 | export interface FcConnector { 56 | id: string; 57 | type: string; 58 | } 59 | 60 | export interface FcNode extends FcCoords { 61 | id: string; 62 | name: string; 63 | connectors: Array; 64 | readonly?: boolean; 65 | [key: string]: any; 66 | } 67 | 68 | export interface FcNodeRectInfo { 69 | width(): number; 70 | height(): number; 71 | top(): number; 72 | left(): number; 73 | right(): number; 74 | bottom(): number; 75 | } 76 | 77 | export interface FcConnectorRectInfo { 78 | type: string; 79 | width: number; 80 | height: number; 81 | nodeRectInfo: FcNodeRectInfo; 82 | } 83 | 84 | export interface FcEdge { 85 | label?: string; 86 | source?: string; 87 | destination?: string; 88 | active?: boolean; 89 | } 90 | 91 | export interface FcItemInfo { 92 | node?: FcNode; 93 | edge?: FcEdge; 94 | } 95 | 96 | export interface FcModel { 97 | nodes: Array; 98 | edges: Array; 99 | } 100 | 101 | export interface UserCallbacks { 102 | dropNode?: (event: Event, node: FcNode) => void; 103 | createEdge?: (event: Event, edge: FcEdge) => Observable; 104 | edgeAdded?: (edge: FcEdge) => void; 105 | nodeRemoved?: (node: FcNode) => void; 106 | edgeRemoved?: (edge: FcEdge) => void; 107 | edgeDoubleClick?: (event: MouseEvent, edge: FcEdge) => void; 108 | edgeMouseOver?: (event: MouseEvent, edge: FcEdge) => void; 109 | isValidEdge?: (source: FcConnector, destination: FcConnector) => boolean; 110 | edgeEdit?: (event: Event, edge: FcEdge) => void; 111 | nodeCallbacks?: UserNodeCallbacks; 112 | } 113 | 114 | export interface UserNodeCallbacks { 115 | nodeEdit?: (event: MouseEvent, node: FcNode) => void; 116 | doubleClick?: (event: MouseEvent, node: FcNode) => void; 117 | mouseDown?: (event: MouseEvent, node: FcNode) => void; 118 | mouseEnter?: (event: MouseEvent, node: FcNode) => void; 119 | mouseLeave?: (event: MouseEvent, node: FcNode) => void; 120 | } 121 | 122 | export interface FcCallbacks { 123 | nodeDragstart: (event: Event | any, node: FcNode) => void; 124 | nodeDragend: (event: Event | any) => void; 125 | edgeDragstart: (event: Event | any, connector: FcConnector) => void; 126 | edgeDragend: (event: Event | any) => void; 127 | edgeDrop: (event: Event | any, targetConnector: FcConnector) => boolean; 128 | edgeDragoverConnector: (event: Event | any, connector: FcConnector) => boolean; 129 | edgeDragoverMagnet: (event: Event | any, connector: FcConnector) => boolean; 130 | edgeDragleaveMagnet: (event: Event | any) => void; 131 | nodeMouseOver: (event: MouseEvent, node: FcNode) => void; 132 | nodeMouseOut: (event: MouseEvent, node: FcNode) => void; 133 | connectorMouseEnter: (event: MouseEvent, connector: FcConnector) => void; 134 | connectorMouseLeave: (event: MouseEvent, connector: FcConnector) => void; 135 | nodeClicked: (event: MouseEvent, node: FcNode) => void; 136 | } 137 | 138 | export interface FcAdjacentList { 139 | [id: string]: { 140 | incoming: number; 141 | outgoing: Array; 142 | }; 143 | } 144 | 145 | class BaseError { 146 | constructor() { 147 | Error.apply(this, arguments); 148 | } 149 | } 150 | 151 | Object.defineProperty(BaseError, 'prototype', new Error()); 152 | 153 | export class ModelvalidationError extends BaseError { 154 | constructor(public message: string) { 155 | super(); 156 | } 157 | } 158 | 159 | export const fcTopSort = (graph: FcModel): Array | null => { 160 | const adjacentList: FcAdjacentList = {}; 161 | graph.nodes.forEach((node) => { 162 | adjacentList[node.id] = {incoming: 0, outgoing: []}; 163 | }); 164 | graph.edges.forEach((edge) => { 165 | const sourceNode = graph.nodes.filter((node) => node.connectors.some((connector) => connector.id === edge.source))[0]; 166 | const destinationNode = graph.nodes.filter((node) => node.connectors.some((connector) => connector.id === edge.destination))[0]; 167 | adjacentList[sourceNode.id].outgoing.push(destinationNode.id); 168 | adjacentList[destinationNode.id].incoming++; 169 | }); 170 | const orderedNodes: string[] = []; 171 | const sourceNodes: string[] = []; 172 | for (const node of Object.keys(adjacentList)) { 173 | const edges = adjacentList[node]; 174 | if (edges.incoming === 0) { 175 | sourceNodes.push(node); 176 | } 177 | } 178 | while (sourceNodes.length !== 0) { 179 | const sourceNode = sourceNodes.pop(); 180 | for (let i = 0; i < adjacentList[sourceNode].outgoing.length; i++) { 181 | const destinationNode = adjacentList[sourceNode].outgoing[i]; 182 | adjacentList[destinationNode].incoming--; 183 | if (adjacentList[destinationNode].incoming === 0) { 184 | sourceNodes.push(destinationNode); 185 | } 186 | adjacentList[sourceNode].outgoing.splice(i, 1); 187 | i--; 188 | } 189 | orderedNodes.push(sourceNode); 190 | } 191 | let hasEdges = false; 192 | for (const node of Object.keys(adjacentList)) { 193 | const edges = adjacentList[node]; 194 | if (edges.incoming !== 0) { 195 | hasEdges = true; 196 | } 197 | } 198 | if (hasEdges) { 199 | return null; 200 | } else { 201 | return orderedNodes; 202 | } 203 | }; 204 | -------------------------------------------------------------------------------- /projects/ngx-flowchart/src/lib/ngx-flowchart.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { NgxFlowchartComponent } from './ngx-flowchart.component'; 3 | import { FcModelValidationService } from './modelvalidation.service'; 4 | import { FcEdgeDrawingService } from './edge-drawing.service'; 5 | import { CommonModule } from '@angular/common'; 6 | import { FcMagnetDirective } from './magnet.directive'; 7 | import { FcConnectorDirective } from './connector.directive'; 8 | import { FcNodeContainerComponent } from './node.component'; 9 | import { FC_NODE_COMPONENT_CONFIG } from './ngx-flowchart.models'; 10 | import { DefaultFcNodeComponent } from './default-node.component'; 11 | 12 | @NgModule({ 13 | declarations: [NgxFlowchartComponent, 14 | FcMagnetDirective, 15 | FcConnectorDirective, 16 | FcNodeContainerComponent, 17 | DefaultFcNodeComponent], 18 | providers: [ 19 | FcModelValidationService, 20 | FcEdgeDrawingService, 21 | { 22 | provide: FC_NODE_COMPONENT_CONFIG, 23 | useValue: { 24 | nodeComponentType: DefaultFcNodeComponent 25 | } 26 | } 27 | ], 28 | imports: [ 29 | CommonModule 30 | ], 31 | exports: [NgxFlowchartComponent, 32 | FcMagnetDirective, 33 | FcConnectorDirective, 34 | DefaultFcNodeComponent] 35 | }) 36 | export class NgxFlowchartModule { } 37 | -------------------------------------------------------------------------------- /projects/ngx-flowchart/src/lib/node-dragging.service.ts: -------------------------------------------------------------------------------- 1 | import { FcModelService } from './model.service'; 2 | import { FcCoords, FcNode, FlowchartConstants } from './ngx-flowchart.models'; 3 | 4 | const nodeDropScope: NodeDropScope = { 5 | dropElement: null 6 | }; 7 | 8 | export class FcNodeDraggingService { 9 | 10 | nodeDraggingScope: NodeDraggingScope = { 11 | shadowDragStarted: false, 12 | dropElement: null, 13 | draggedNodes: [], 14 | shadowElements: [] 15 | }; 16 | 17 | private dragOffsets: FcCoords[] = []; 18 | private draggedElements: HTMLElement[] = []; 19 | 20 | private destinationHtmlElements: HTMLElement[] = []; 21 | private oldDisplayStyles: string[] = []; 22 | 23 | private readonly modelService: FcModelService; 24 | private readonly automaticResize: boolean; 25 | private readonly dragAnimation: string; 26 | private readonly applyFunction: (fn: (...args: any[]) => T) => T; 27 | 28 | constructor(modelService: FcModelService, 29 | applyFunction: (fn: (...args: any[]) => T) => T, 30 | automaticResize: boolean, dragAnimation: string) { 31 | this.modelService = modelService; 32 | this.automaticResize = automaticResize; 33 | this.dragAnimation = dragAnimation; 34 | this.applyFunction = applyFunction; 35 | } 36 | 37 | private getCoordinate(coordinate: number, max: number): number { 38 | coordinate = Math.max(coordinate, 0); 39 | coordinate = Math.min(coordinate, max); 40 | return coordinate; 41 | } 42 | 43 | private getXCoordinate(x: number): number { 44 | return this.getCoordinate(x, this.modelService.canvasHtmlElement.offsetWidth); 45 | } 46 | 47 | private getYCoordinate(y: number): number { 48 | return this.getCoordinate(y, this.modelService.canvasHtmlElement.offsetHeight); 49 | } 50 | 51 | private resizeCanvas(draggedNode: FcNode, nodeElement: HTMLElement) { 52 | if (this.automaticResize && !this.modelService.isDropSource()) { 53 | const canvasElement = this.modelService.canvasHtmlElement; 54 | if (canvasElement.offsetWidth < draggedNode.x + nodeElement.offsetWidth + FlowchartConstants.canvasResizeThreshold) { 55 | canvasElement.style.width = canvasElement.offsetWidth + FlowchartConstants.canvasResizeStep + 'px'; 56 | } 57 | if (canvasElement.offsetHeight < draggedNode.y + nodeElement.offsetHeight + FlowchartConstants.canvasResizeThreshold) { 58 | canvasElement.style.height = canvasElement.offsetHeight + FlowchartConstants.canvasResizeStep + 'px'; 59 | } 60 | } 61 | } 62 | 63 | public isDraggingNode(node: FcNode): boolean { 64 | return this.nodeDraggingScope.draggedNodes.includes(node); 65 | } 66 | 67 | public dragstart(event: Event | any, node: FcNode) { 68 | if (node.readonly) { 69 | return; 70 | } 71 | this.dragOffsets.length = 0; 72 | this.draggedElements.length = 0; 73 | this.nodeDraggingScope.draggedNodes.length = 0; 74 | this.nodeDraggingScope.shadowElements.length = 0; 75 | this.destinationHtmlElements.length = 0; 76 | this.oldDisplayStyles.length = 0; 77 | const elements: Array> = []; 78 | const nodes: Array = []; 79 | if (this.modelService.nodes.isSelected(node)) { 80 | const selectedNodes = this.modelService.nodes.getSelectedNodes(); 81 | for (const selectedNode of selectedNodes) { 82 | const element = $(this.modelService.nodes.getHtmlElement(selectedNode.id)); 83 | elements.push(element); 84 | nodes.push(selectedNode); 85 | } 86 | } else { 87 | elements.push($(event.target as HTMLElement)); 88 | nodes.push(node); 89 | } 90 | const offsetsX: number[] = []; 91 | const offsetsY: number[] = []; 92 | for (const element of elements) { 93 | offsetsX.push(parseInt(element.css('left'), 10) - event.clientX); 94 | offsetsY.push(parseInt(element.css('top'), 10) - event.clientY); 95 | } 96 | const originalEvent: Event | any = (event as any).originalEvent || event; 97 | if (this.modelService.isDropSource()) { 98 | if (nodeDropScope.dropElement) { 99 | nodeDropScope.dropElement.parentNode.removeChild(nodeDropScope.dropElement); 100 | nodeDropScope.dropElement = null; 101 | } 102 | nodeDropScope.dropElement = elements[0][0].cloneNode(true) as NodeDropElement; 103 | const offset = $(this.modelService.canvasHtmlElement).offset(); 104 | nodeDropScope.dropElement.offsetInfo = { 105 | offsetX: Math.round(offsetsX[0] + offset.left), 106 | offsetY: Math.round(offsetsY[0] + offset.top) 107 | }; 108 | nodeDropScope.dropElement.style.position = 'absolute'; 109 | nodeDropScope.dropElement.style.pointerEvents = 'none'; 110 | nodeDropScope.dropElement.style.zIndex = '9999'; 111 | 112 | document.body.appendChild(nodeDropScope.dropElement); 113 | const dropNodeInfo: DropNodeInfo = { 114 | node, 115 | dropTargetId: this.modelService.dropTargetId, 116 | offsetX: Math.round(offsetsX[0] + offset.left), 117 | offsetY: Math.round(offsetsY[0] + offset.top) 118 | }; 119 | originalEvent.dataTransfer.setData('text', JSON.stringify(dropNodeInfo)); 120 | 121 | if (originalEvent.dataTransfer.setDragImage) { 122 | originalEvent.dataTransfer.setDragImage(this.modelService.getDragImage(), 0, 0); 123 | } else { 124 | const target: HTMLElement = event.target as HTMLElement; 125 | const cloneNode = target.cloneNode(true); 126 | target.parentNode.insertBefore(cloneNode, target); 127 | target.style.visibility = 'collapse'; 128 | setTimeout(() => { 129 | target.parentNode.removeChild(cloneNode); 130 | target.style.visibility = 'visible'; 131 | }, 0); 132 | } 133 | return; 134 | } 135 | this.nodeDraggingScope.draggedNodes = nodes; 136 | for (let i = 0; i < elements.length; i++) { 137 | this.draggedElements.push(elements[i][0]); 138 | this.dragOffsets.push( 139 | { 140 | x: offsetsX[i], 141 | y: offsetsY[i] 142 | } 143 | ); 144 | } 145 | 146 | if (this.dragAnimation === FlowchartConstants.dragAnimationShadow) { 147 | for (let i = 0; i < this.draggedElements.length; i++) { 148 | const dragOffset = this.dragOffsets[i]; 149 | const draggedNode = this.nodeDraggingScope.draggedNodes[i]; 150 | const shadowElement = $(`
` + 153 | `

${draggedNode.name}

`); 154 | const targetInnerNode = $(this.draggedElements[i]).children()[0]; 155 | shadowElement.children()[0].style.backgroundColor = targetInnerNode.style.backgroundColor; 156 | this.nodeDraggingScope.shadowElements.push(shadowElement); 157 | this.modelService.canvasHtmlElement.appendChild(this.nodeDraggingScope.shadowElements[i][0]); 158 | } 159 | } 160 | originalEvent.dataTransfer.setData('text', 'Just to support firefox'); 161 | if (originalEvent.dataTransfer.setDragImage) { 162 | originalEvent.dataTransfer.setDragImage(this.modelService.getDragImage(), 0, 0); 163 | } else { 164 | this.draggedElements.forEach((draggedElement) => { 165 | const cloneNode = draggedElement.cloneNode(true); 166 | draggedElement.parentNode.insertBefore(cloneNode, draggedElement); 167 | draggedElement.style.visibility = 'collapse'; 168 | setTimeout(() => { 169 | draggedElement.parentNode.removeChild(cloneNode); 170 | draggedElement.style.visibility = 'visible'; 171 | }, 0); 172 | }); 173 | if (this.dragAnimation === FlowchartConstants.dragAnimationShadow) { 174 | for (let i = 0; i < this.draggedElements.length; i++) { 175 | this.destinationHtmlElements.push(this.draggedElements[i]); 176 | this.oldDisplayStyles.push(this.destinationHtmlElements[i].style.display); 177 | this.destinationHtmlElements[i].style.display = 'none'; 178 | } 179 | this.nodeDraggingScope.shadowDragStarted = true; 180 | } 181 | } 182 | } 183 | 184 | public drop(event: Event | any): boolean { 185 | if (this.modelService.isDropSource()) { 186 | event.preventDefault(); 187 | return false; 188 | } 189 | let dropNode: FcNode = null; 190 | const originalEvent: Event | any = (event as any).originalEvent || event; 191 | const infoText = originalEvent.dataTransfer.getData('text'); 192 | if (infoText) { 193 | let dropNodeInfo: DropNodeInfo = null; 194 | try { 195 | dropNodeInfo = JSON.parse(infoText); 196 | } catch (e) {} 197 | if (dropNodeInfo && dropNodeInfo.dropTargetId) { 198 | if (this.modelService.canvasHtmlElement.id && 199 | this.modelService.canvasHtmlElement.id === dropNodeInfo.dropTargetId) { 200 | dropNode = dropNodeInfo.node; 201 | const offset = $(this.modelService.canvasHtmlElement).offset(); 202 | const x = event.clientX - offset.left; 203 | const y = event.clientY - offset.top; 204 | dropNode.x = Math.round(this.getXCoordinate(dropNodeInfo.offsetX + x)); 205 | dropNode.y = Math.round(this.getYCoordinate(dropNodeInfo.offsetY + y)); 206 | } 207 | } 208 | } 209 | if (dropNode) { 210 | this.modelService.dropNode(event, dropNode); 211 | event.preventDefault(); 212 | return false; 213 | } else if (this.nodeDraggingScope.draggedNodes.length) { 214 | return this.applyFunction(() => { 215 | for (let i = 0; i < this.nodeDraggingScope.draggedNodes.length; i++) { 216 | const draggedNode = this.nodeDraggingScope.draggedNodes[i]; 217 | const dragOffset = this.dragOffsets[i]; 218 | draggedNode.x = Math.round(this.getXCoordinate(dragOffset.x + event.clientX)); 219 | draggedNode.y = Math.round(this.getYCoordinate(dragOffset.y + event.clientY)); 220 | } 221 | event.preventDefault(); 222 | this.modelService.notifyModelChanged(); 223 | return false; 224 | }); 225 | } 226 | } 227 | 228 | public dragover(event: Event | any) { 229 | if (nodeDropScope.dropElement) { 230 | const offsetInfo = nodeDropScope.dropElement.offsetInfo; 231 | nodeDropScope.dropElement.style.left = (offsetInfo.offsetX + event.clientX) + 'px'; 232 | nodeDropScope.dropElement.style.top = (offsetInfo.offsetY + event.clientY) + 'px'; 233 | if (this.nodeDraggingScope.shadowDragStarted) { 234 | this.applyFunction(() => { 235 | this.destinationHtmlElements[0].style.display = this.oldDisplayStyles[0]; 236 | this.nodeDraggingScope.shadowDragStarted = false; 237 | }); 238 | } 239 | event.preventDefault(); 240 | return; 241 | } 242 | if (this.modelService.isDropSource()) { 243 | event.preventDefault(); 244 | return; 245 | } 246 | if (!this.nodeDraggingScope.draggedNodes.length) { 247 | event.preventDefault(); 248 | return; 249 | } 250 | if (this.dragAnimation === FlowchartConstants.dragAnimationRepaint) { 251 | if (this.nodeDraggingScope.draggedNodes.length) { 252 | return this.applyFunction(() => { 253 | for (let i = 0; i < this.nodeDraggingScope.draggedNodes.length; i++) { 254 | const draggedNode = this.nodeDraggingScope.draggedNodes[i]; 255 | const dragOffset = this.dragOffsets[i]; 256 | draggedNode.x = this.getXCoordinate(dragOffset.x + event.clientX); 257 | draggedNode.y = this.getYCoordinate(dragOffset.y + event.clientY); 258 | this.resizeCanvas(draggedNode, this.draggedElements[i]); 259 | } 260 | event.preventDefault(); 261 | this.modelService.notifyModelChanged(); 262 | return false; 263 | }); 264 | } 265 | } else if (this.dragAnimation === FlowchartConstants.dragAnimationShadow) { 266 | if (this.nodeDraggingScope.draggedNodes.length) { 267 | if (this.nodeDraggingScope.shadowDragStarted) { 268 | this.applyFunction(() => { 269 | for (let i = 0; i < this.nodeDraggingScope.draggedNodes.length; i++) { 270 | this.destinationHtmlElements[i].style.display = this.oldDisplayStyles[i]; 271 | } 272 | this.nodeDraggingScope.shadowDragStarted = false; 273 | }); 274 | } 275 | for (let i = 0; i < this.nodeDraggingScope.draggedNodes.length; i++) { 276 | const draggedNode = this.nodeDraggingScope.draggedNodes[i]; 277 | const dragOffset = this.dragOffsets[i]; 278 | this.nodeDraggingScope.shadowElements[i].css('left', this.getXCoordinate(dragOffset.x + event.clientX) + 'px'); 279 | this.nodeDraggingScope.shadowElements[i].css('top', this.getYCoordinate(dragOffset.y + event.clientY) + 'px'); 280 | this.resizeCanvas(draggedNode, this.draggedElements[i]); 281 | } 282 | event.preventDefault(); 283 | } 284 | } 285 | } 286 | 287 | public dragend(event: Event | any) { 288 | this.applyFunction(() => { 289 | if (nodeDropScope.dropElement) { 290 | nodeDropScope.dropElement.parentNode.removeChild(nodeDropScope.dropElement); 291 | nodeDropScope.dropElement = null; 292 | } 293 | if (this.modelService.isDropSource()) { 294 | return; 295 | } 296 | if (this.nodeDraggingScope.shadowElements.length) { 297 | for (let i = 0; i < this.nodeDraggingScope.draggedNodes.length; i++) { 298 | const draggedNode = this.nodeDraggingScope.draggedNodes[i]; 299 | const shadowElement = this.nodeDraggingScope.shadowElements[i]; 300 | draggedNode.x = parseInt(shadowElement.css('left').replace('px', ''), 10); 301 | draggedNode.y = parseInt(shadowElement.css('top').replace('px', ''), 10); 302 | this.modelService.canvasHtmlElement.removeChild(shadowElement[0]); 303 | } 304 | this.nodeDraggingScope.shadowElements.length = 0; 305 | this.modelService.notifyModelChanged(); 306 | } 307 | 308 | if (this.nodeDraggingScope.draggedNodes.length) { 309 | this.nodeDraggingScope.draggedNodes.length = 0; 310 | this.draggedElements.length = 0; 311 | this.dragOffsets.length = 0; 312 | } 313 | }); 314 | } 315 | 316 | } 317 | 318 | export interface NodeDraggingScope { 319 | draggedNodes: Array; 320 | shadowElements: Array>; 321 | shadowDragStarted: boolean; 322 | dropElement: HTMLElement; 323 | } 324 | 325 | export interface NodeDropElement extends HTMLElement { 326 | offsetInfo?: { 327 | offsetX: number; 328 | offsetY: number; 329 | }; 330 | } 331 | 332 | export interface NodeDropScope { 333 | dropElement: NodeDropElement; 334 | } 335 | 336 | export interface DropNodeInfo { 337 | node: FcNode; 338 | dropTargetId: string; 339 | offsetX: number; 340 | offsetY: number; 341 | } 342 | -------------------------------------------------------------------------------- /projects/ngx-flowchart/src/lib/node.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | position: absolute; 3 | z-index: 1; 4 | &.fc-dragging { 5 | z-index: 10; 6 | } 7 | } 8 | 9 | :host ::ng-deep { 10 | .fc-leftConnectors, .fc-rightConnectors { 11 | position: absolute; 12 | top: 0; 13 | height: 100%; 14 | 15 | display: flex; 16 | flex-direction: column; 17 | 18 | z-index: -10; 19 | .fc-magnet { 20 | align-items: center; 21 | } 22 | } 23 | 24 | .fc-leftConnectors { 25 | left: -20px; 26 | } 27 | 28 | .fc-rightConnectors { 29 | right: -20px; 30 | } 31 | 32 | .fc-magnet { 33 | display: flex; 34 | flex-grow: 1; 35 | height: 60px; 36 | 37 | justify-content: center; 38 | } 39 | 40 | .fc-connector { 41 | width: 18px; 42 | height: 18px; 43 | 44 | border: 10px solid transparent; 45 | -moz-background-clip: padding; /* Firefox 3.6 */ 46 | -webkit-background-clip: padding; /* Safari 4? Chrome 6? */ 47 | background-clip: padding-box; 48 | border-radius: 50% 50%; 49 | background-color: #F7A789; 50 | color: #fff; 51 | pointer-events: all; 52 | 53 | &.fc-hover { 54 | background-color: #000; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /projects/ngx-flowchart/src/lib/node.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AfterViewInit, 3 | Component, 4 | ComponentFactoryResolver, 5 | Directive, 6 | ElementRef, 7 | HostBinding, 8 | HostListener, 9 | Inject, 10 | Input, 11 | OnChanges, 12 | OnInit, 13 | SimpleChanges, 14 | ViewChild, 15 | ViewContainerRef 16 | } from '@angular/core'; 17 | import { 18 | FC_NODE_COMPONENT_CONFIG, 19 | FcCallbacks, 20 | FcConnector, 21 | FcNode, 22 | FcNodeComponentConfig, 23 | FcNodeRectInfo, 24 | FlowchartConstants, 25 | UserNodeCallbacks 26 | } from './ngx-flowchart.models'; 27 | import { FcModelService } from './model.service'; 28 | 29 | @Component({ 30 | // eslint-disable-next-line @angular-eslint/component-selector 31 | selector: 'fc-node', 32 | template: '', 33 | styleUrls: ['./node.component.scss'] 34 | }) 35 | export class FcNodeContainerComponent implements OnInit, AfterViewInit, OnChanges { 36 | 37 | @Input() 38 | callbacks: FcCallbacks; 39 | 40 | @Input() 41 | userNodeCallbacks: UserNodeCallbacks; 42 | 43 | @Input() 44 | node: FcNode; 45 | 46 | @Input() 47 | selected: boolean; 48 | 49 | @Input() 50 | edit: boolean; 51 | 52 | @Input() 53 | underMouse: boolean; 54 | 55 | @Input() 56 | mouseOverConnector: FcConnector; 57 | 58 | @Input() 59 | modelservice: FcModelService; 60 | 61 | @Input() 62 | dragging: boolean; 63 | 64 | @HostBinding('attr.id') 65 | get nodeId(): string { 66 | return this.node.id; 67 | } 68 | 69 | @HostBinding('style.top') 70 | get top(): string { 71 | return this.node.y + 'px'; 72 | } 73 | 74 | @HostBinding('style.left') 75 | get left(): string { 76 | return this.node.x + 'px'; 77 | } 78 | 79 | nodeComponent: FcNodeComponent; 80 | 81 | @ViewChild('nodeContent', {read: ViewContainerRef, static: true}) nodeContentContainer: ViewContainerRef; 82 | 83 | constructor(@Inject(FC_NODE_COMPONENT_CONFIG) private nodeComponentConfig: FcNodeComponentConfig, 84 | private elementRef: ElementRef, 85 | private componentFactoryResolver: ComponentFactoryResolver) { 86 | } 87 | 88 | ngOnInit(): void { 89 | if (!this.userNodeCallbacks) { 90 | this.userNodeCallbacks = {}; 91 | } 92 | this.userNodeCallbacks.nodeEdit = this.userNodeCallbacks.nodeEdit || (() => {}); 93 | this.userNodeCallbacks.doubleClick = this.userNodeCallbacks.doubleClick || (() => {}); 94 | this.userNodeCallbacks.mouseDown = this.userNodeCallbacks.mouseDown || (() => {}); 95 | this.userNodeCallbacks.mouseEnter = this.userNodeCallbacks.mouseEnter || (() => {}); 96 | this.userNodeCallbacks.mouseLeave = this.userNodeCallbacks.mouseLeave || (() => {}); 97 | 98 | const element = $(this.elementRef.nativeElement); 99 | element.addClass(FlowchartConstants.nodeClass); 100 | if (!this.node.readonly) { 101 | element.attr('draggable', 'true'); 102 | } 103 | this.updateNodeClass(); 104 | this.modelservice.nodes.setHtmlElement(this.node.id, element[0]); 105 | this.nodeContentContainer.clear(); 106 | const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.nodeComponentConfig.nodeComponentType); 107 | const componentRef = this.nodeContentContainer.createComponent(componentFactory); 108 | this.nodeComponent = componentRef.instance; 109 | this.nodeComponent.callbacks = this.callbacks; 110 | this.nodeComponent.userNodeCallbacks = this.userNodeCallbacks; 111 | this.nodeComponent.node = this.node; 112 | this.nodeComponent.modelservice = this.modelservice; 113 | this.updateNodeComponent(); 114 | this.nodeComponent.width = this.elementRef.nativeElement.offsetWidth; 115 | this.nodeComponent.height = this.elementRef.nativeElement.offsetHeight; 116 | } 117 | 118 | ngAfterViewInit(): void { 119 | this.nodeComponent.width = this.elementRef.nativeElement.offsetWidth; 120 | this.nodeComponent.height = this.elementRef.nativeElement.offsetHeight; 121 | } 122 | 123 | ngOnChanges(changes: SimpleChanges): void { 124 | let updateNode = false; 125 | for (const propName of Object.keys(changes)) { 126 | const change = changes[propName]; 127 | if (!change.firstChange && change.currentValue !== change.previousValue) { 128 | if (['selected', 'edit', 'underMouse', 'mouseOverConnector', 'dragging'].includes(propName)) { 129 | updateNode = true; 130 | } 131 | } 132 | } 133 | if (updateNode) { 134 | this.updateNodeClass(); 135 | this.updateNodeComponent(); 136 | } 137 | } 138 | 139 | private updateNodeClass() { 140 | const element = $(this.elementRef.nativeElement); 141 | this.toggleClass(element, FlowchartConstants.selectedClass, this.selected); 142 | this.toggleClass(element, FlowchartConstants.editClass, this.edit); 143 | this.toggleClass(element, FlowchartConstants.hoverClass, this.underMouse); 144 | this.toggleClass(element, FlowchartConstants.draggingClass, this.dragging); 145 | } 146 | 147 | private updateNodeComponent() { 148 | this.nodeComponent.selected = this.selected; 149 | this.nodeComponent.edit = this.edit; 150 | this.nodeComponent.underMouse = this.underMouse; 151 | this.nodeComponent.mouseOverConnector = this.mouseOverConnector; 152 | this.nodeComponent.dragging = this.dragging; 153 | } 154 | 155 | private toggleClass(element: JQuery, clazz: string, set: boolean) { 156 | if (set) { 157 | element.addClass(clazz); 158 | } else { 159 | element.removeClass(clazz); 160 | } 161 | } 162 | 163 | @HostListener('mousedown', ['$event']) 164 | mousedown(event: MouseEvent) { 165 | event.stopPropagation(); 166 | } 167 | 168 | @HostListener('dragstart', ['$event']) 169 | dragstart(event: Event | any) { 170 | if (!this.node.readonly) { 171 | this.callbacks.nodeDragstart(event, this.node); 172 | } 173 | } 174 | 175 | @HostListener('dragend', ['$event']) 176 | dragend(event: Event | any) { 177 | if (!this.node.readonly) { 178 | this.callbacks.nodeDragend(event); 179 | } 180 | } 181 | 182 | @HostListener('click', ['$event']) 183 | click(event: MouseEvent) { 184 | if (!this.node.readonly) { 185 | this.callbacks.nodeClicked(event, this.node); 186 | } 187 | } 188 | 189 | @HostListener('mouseover', ['$event']) 190 | mouseover(event: MouseEvent) { 191 | if (!this.node.readonly) { 192 | this.callbacks.nodeMouseOver(event, this.node); 193 | } 194 | } 195 | 196 | @HostListener('mouseout', ['$event']) 197 | mouseout(event: MouseEvent) { 198 | if (!this.node.readonly) { 199 | this.callbacks.nodeMouseOut(event, this.node); 200 | } 201 | } 202 | 203 | } 204 | 205 | @Directive() 206 | // tslint:disable-next-line:directive-class-suffix 207 | export abstract class FcNodeComponent implements OnInit { 208 | 209 | @Input() 210 | callbacks: FcCallbacks; 211 | 212 | @Input() 213 | userNodeCallbacks: UserNodeCallbacks; 214 | 215 | @Input() 216 | node: FcNode; 217 | 218 | @Input() 219 | selected: boolean; 220 | 221 | @Input() 222 | edit: boolean; 223 | 224 | @Input() 225 | underMouse: boolean; 226 | 227 | @Input() 228 | mouseOverConnector: FcConnector; 229 | 230 | @Input() 231 | modelservice: FcModelService; 232 | 233 | @Input() 234 | dragging: boolean; 235 | 236 | flowchartConstants = FlowchartConstants; 237 | 238 | width: number; 239 | 240 | height: number; 241 | 242 | nodeRectInfo: FcNodeRectInfo = { 243 | top: () => this.node.y, 244 | 245 | left: () => this.node.x, 246 | 247 | bottom: () => this.node.y + this.height, 248 | 249 | right: () => this.node.x + this.width, 250 | 251 | width: () => this.width, 252 | 253 | height: () => this.height 254 | }; 255 | 256 | ngOnInit(): void { 257 | } 258 | 259 | } 260 | -------------------------------------------------------------------------------- /projects/ngx-flowchart/src/lib/rectangleselect.service.ts: -------------------------------------------------------------------------------- 1 | import { FcModelService } from './model.service'; 2 | import { FcRectBox } from './ngx-flowchart.models'; 3 | import scrollparent from './scrollparent'; 4 | 5 | interface Rectangle { 6 | x1: number; 7 | x2: number; 8 | y1: number; 9 | y2: number; 10 | } 11 | 12 | export class FcRectangleSelectService { 13 | 14 | private readonly selectRect: Rectangle = { 15 | x1: 0, 16 | x2: 0, 17 | y1: 0, 18 | y2: 0 19 | }; 20 | 21 | private readonly modelService: FcModelService; 22 | private readonly selectElement: HTMLElement; 23 | private readonly $canvasElement: JQuery; 24 | private readonly $scrollParent: JQuery; 25 | private readonly applyFunction: (fn: (...args: any[]) => T) => T; 26 | 27 | constructor(modelService: FcModelService, 28 | selectElement: HTMLElement, 29 | applyFunction: (fn: (...args: any[]) => T) => T) { 30 | this.modelService = modelService; 31 | this.selectElement = selectElement; 32 | this.$canvasElement = $(this.modelService.canvasHtmlElement); 33 | this.$scrollParent = $(scrollparent(this.modelService.canvasHtmlElement)); 34 | this.applyFunction = applyFunction; 35 | } 36 | 37 | public mousedown(e: MouseEvent) { 38 | if (this.modelService.isEditable() && !e.ctrlKey && !e.metaKey && e.button === 0 39 | && this.selectElement.hidden) { 40 | this.selectElement.hidden = false; 41 | const offset = this.$canvasElement.offset(); 42 | this.selectRect.x1 = Math.round(e.pageX - offset.left); 43 | this.selectRect.y1 = Math.round(e.pageY - offset.top); 44 | this.selectRect.x2 = this.selectRect.x1; 45 | this.selectRect.y2 = this.selectRect.y1; 46 | this.updateSelectRect(); 47 | } 48 | } 49 | 50 | public mousemove(e: MouseEvent) { 51 | if (this.modelService.isEditable() && !e.ctrlKey && !e.metaKey && e.button === 0 52 | && !this.selectElement.hidden) { 53 | const offset = this.$canvasElement.offset(); 54 | this.selectRect.x2 = Math.round(e.pageX - offset.left); 55 | this.selectRect.y2 = Math.round(e.pageY - offset.top); 56 | this.updateScroll(offset); 57 | this.updateSelectRect(); 58 | } 59 | } 60 | 61 | private updateScroll(offset: JQuery.Coordinates) { 62 | const rect = this.$scrollParent[0].getBoundingClientRect(); 63 | const bottom = rect.bottom - offset.top; 64 | const right = rect.right - offset.left; 65 | const top = rect.top - offset.top; 66 | const left = rect.left - offset.left; 67 | if (this.selectRect.y2 - top < 25) { 68 | const topScroll = 25 - (this.selectRect.y2 - top); 69 | const scroll = this.$scrollParent.scrollTop(); 70 | this.$scrollParent.scrollTop(scroll - topScroll); 71 | } else if (bottom - this.selectRect.y2 < 40) { 72 | const bottomScroll = 40 - (bottom - this.selectRect.y2); 73 | const scroll = this.$scrollParent.scrollTop(); 74 | this.$scrollParent.scrollTop(scroll + bottomScroll); 75 | } 76 | if (this.selectRect.x2 - left < 25) { 77 | const leftScroll = 25 - (this.selectRect.x2 - left); 78 | const scroll = this.$scrollParent.scrollLeft(); 79 | this.$scrollParent.scrollLeft(scroll - leftScroll); 80 | } else if (right - this.selectRect.x2 < 40) { 81 | const rightScroll = 40 - (right - this.selectRect.x2); 82 | const scroll = this.$scrollParent.scrollLeft(); 83 | this.$scrollParent.scrollLeft(scroll + rightScroll); 84 | } 85 | } 86 | 87 | public mouseup(e: MouseEvent) { 88 | if (this.modelService.isEditable() && !e.ctrlKey && !e.metaKey && e.button === 0 89 | && !this.selectElement.hidden) { 90 | const rectBox = this.selectElement.getBoundingClientRect() as FcRectBox; 91 | this.selectElement.hidden = true; 92 | this.selectObjects(rectBox); 93 | } 94 | } 95 | 96 | private updateSelectRect() { 97 | const x3 = Math.min(this.selectRect.x1, this.selectRect.x2); 98 | const x4 = Math.max(this.selectRect.x1, this.selectRect.x2); 99 | const y3 = Math.min(this.selectRect.y1, this.selectRect.y2); 100 | const y4 = Math.max(this.selectRect.y1, this.selectRect.y2); 101 | this.selectElement.style.left = x3 + 'px'; 102 | this.selectElement.style.top = y3 + 'px'; 103 | this.selectElement.style.width = x4 - x3 + 'px'; 104 | this.selectElement.style.height = y4 - y3 + 'px'; 105 | } 106 | 107 | private selectObjects(rectBox: FcRectBox) { 108 | this.applyFunction(() => { 109 | this.modelService.selectAllInRect(rectBox); 110 | }); 111 | } 112 | 113 | } 114 | 115 | -------------------------------------------------------------------------------- /projects/ngx-flowchart/src/lib/scrollparent.ts: -------------------------------------------------------------------------------- 1 | const regex = /(auto|scroll)/; 2 | 3 | const style = (node: Element, prop: string): string => 4 | getComputedStyle(node, null).getPropertyValue(prop); 5 | 6 | const scroll = (node: Element) => 7 | regex.test( 8 | style(node, 'overflow') + 9 | style(node, 'overflow-y') + 10 | style(node, 'overflow-x')); 11 | 12 | const scrollparent = (node: HTMLElement): HTMLElement => 13 | !node || node === document.body 14 | ? document.body 15 | : scroll(node) 16 | ? node 17 | : scrollparent(node.parentNode as HTMLElement); 18 | 19 | export default scrollparent; 20 | -------------------------------------------------------------------------------- /projects/ngx-flowchart/src/public-api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of ngx-flowchart 3 | */ 4 | 5 | export * from './lib/ngx-flowchart.component'; 6 | export * from './lib/ngx-flowchart.module'; 7 | export * from './lib/ngx-flowchart.models'; 8 | export { FcNodeComponent } from './lib/node.component'; 9 | export { FcMagnetDirective } from './lib/magnet.directive'; 10 | export { FcConnectorDirective } from './lib/connector.directive'; 11 | export { DefaultFcNodeComponent } from './lib/default-node.component'; 12 | -------------------------------------------------------------------------------- /projects/ngx-flowchart/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/lib", 5 | "declaration": true, 6 | "declarationMap": true, 7 | "inlineSources": true, 8 | "types": ["jquery"] 9 | }, 10 | "exclude": [ 11 | "**/*.spec.ts" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /projects/ngx-flowchart/tsconfig.lib.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.lib.json", 3 | "compilerOptions": { 4 | "declarationMap": false 5 | }, 6 | "angularCompilerOptions": { 7 | "compilationMode": "partial" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 6 | 7 |
8 |
9 |
10 |
11 |
12 | 13 | 17 | 20 |
21 | 29 | 30 |
31 |
32 | -------------------------------------------------------------------------------- /src/app/app.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | display: flex; 3 | flex: 1; 4 | overflow: hidden; 5 | outline: none; 6 | 7 | .fc-container { 8 | display: flex; 9 | flex: 1; 10 | flex-direction: row; 11 | overflow: hidden; 12 | } 13 | 14 | .fc-left-pane { 15 | flex: 0.15; 16 | overflow: auto; 17 | } 18 | 19 | .fc-divider { 20 | flex: 0.01; 21 | } 22 | 23 | .fc-right-pane { 24 | flex: 0.84; 25 | overflow: auto; 26 | } 27 | 28 | .button-overlay { 29 | position: absolute; 30 | top: 40px; 31 | z-index: 10; 32 | } 33 | 34 | .button-overlay button { 35 | display: block; 36 | padding: 10px; 37 | margin-bottom: 15px; 38 | border-radius: 10px; 39 | border: none; 40 | box-shadow: none; 41 | color: #fff; 42 | font-size: 20px; 43 | background-color: #F15B26; 44 | user-select: none; 45 | } 46 | 47 | .button-overlay button:hover:not(:disabled) { 48 | border: 4px solid #b03911; 49 | border-radius: 5px; 50 | margin: -4px; 51 | margin-bottom: 11px; 52 | } 53 | 54 | .button-overlay button:disabled { 55 | -webkit-filter: brightness(70%); 56 | filter: brightness(70%); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { AfterViewInit, Component, HostBinding, HostListener, ViewChild } from '@angular/core'; 2 | import { FcModel, FcNode, FlowchartConstants, NgxFlowchartComponent, UserCallbacks } from 'ngx-flowchart-dev'; 3 | import { of } from 'rxjs'; 4 | import { DELETE } from '@angular/cdk/keycodes'; 5 | 6 | @Component({ 7 | selector: 'app-root', 8 | templateUrl: './app.component.html', 9 | styleUrls: ['./app.component.scss'] 10 | }) 11 | export class AppComponent implements AfterViewInit { 12 | 13 | @HostBinding('attr.tabindex') 14 | get tabindex(): string { 15 | return '0'; 16 | } 17 | 18 | flowchartConstants = FlowchartConstants; 19 | 20 | nodeTypesFlowchartselected = []; 21 | nodeTypesModel: FcModel = { 22 | nodes: [], 23 | edges: [] 24 | }; 25 | 26 | flowchartselected = []; 27 | model: FcModel = { 28 | nodes: [], 29 | edges: [] 30 | }; 31 | nextNodeID = 10; 32 | nextConnectorID = 20; 33 | 34 | callbacks: UserCallbacks = { 35 | edgeDoubleClick: (event, edge) => { 36 | console.log('Edge double clicked.'); 37 | }, 38 | edgeEdit: (event, edge) => { 39 | const label = prompt('Enter a link label:', edge.label); 40 | if (label) { 41 | edge.label = label; 42 | } 43 | }, 44 | edgeMouseOver: event => { 45 | console.log('mouserover'); 46 | }, 47 | isValidEdge: (source, destination) => 48 | source.type === FlowchartConstants.rightConnectorType && destination.type === FlowchartConstants.leftConnectorType 49 | , 50 | createEdge: (event, edge) => { 51 | if (!edge.label) { 52 | const label = prompt('Enter a link label:', 'New label'); 53 | edge.label = label; 54 | } 55 | return of(edge); 56 | }, 57 | dropNode: (event, node) => { 58 | const name = prompt('Enter a node name:', node.name); 59 | if (name) { 60 | node.name = name; 61 | node.id = (this.nextNodeID++) + ''; 62 | node.connectors = [ 63 | { 64 | id: (this.nextConnectorID++) + '', 65 | type: FlowchartConstants.leftConnectorType 66 | }, 67 | { 68 | id: (this.nextConnectorID++) + '', 69 | type: FlowchartConstants.rightConnectorType 70 | } 71 | ]; 72 | this.model.nodes.push(node); 73 | } 74 | }, 75 | edgeAdded: edge => { 76 | console.log('edge added'); 77 | console.log(edge); 78 | }, 79 | nodeRemoved: node => { 80 | console.log('node removed'); 81 | console.log(node); 82 | }, 83 | edgeRemoved: edge => { 84 | console.log('edge removed'); 85 | console.log(edge); 86 | }, 87 | nodeCallbacks: { 88 | doubleClick: event => { 89 | console.log('Node was doubleclicked.'); 90 | }, 91 | nodeEdit: (event, node) => { 92 | const name = prompt('Enter a node name:', node.name); 93 | if (name) { 94 | node.name = name; 95 | } 96 | } 97 | } 98 | }; 99 | 100 | @ViewChild('fcCanvas', {static: true}) fcCanvas: NgxFlowchartComponent; 101 | 102 | constructor() { 103 | this.initData(); 104 | } 105 | 106 | ngAfterViewInit(): void { 107 | console.log(this.fcCanvas.modelService); 108 | } 109 | 110 | private initData() { 111 | for (let i = 0; i < 10; i++) { 112 | const node: FcNode = { 113 | name: 'type' + i, 114 | id: (i + 1) + '', 115 | x: 50, 116 | y: 100 * (i + 1), 117 | connectors: [ 118 | { 119 | type: FlowchartConstants.leftConnectorType, 120 | id: (i * 2 + 1) + '' 121 | }, 122 | { 123 | type: FlowchartConstants.rightConnectorType, 124 | id: (i * 2 + 2) + '' 125 | } 126 | ] 127 | }; 128 | this.nodeTypesModel.nodes.push(node); 129 | } 130 | this.model.nodes.push(... 131 | [ 132 | { 133 | name: 'ngxFlowchart', 134 | readonly: true, 135 | id: '2', 136 | x: 300, 137 | y: 100, 138 | color: '#000', 139 | borderColor: '#000', 140 | connectors: [ 141 | { 142 | type: FlowchartConstants.leftConnectorType, 143 | id: '1' 144 | }, 145 | { 146 | type: FlowchartConstants.rightConnectorType, 147 | id: '2' 148 | } 149 | ] 150 | }, 151 | { 152 | name: 'Implemented with Angular', 153 | id: '3', 154 | x: 600, 155 | y: 100, 156 | color: '#F15B26', 157 | connectors: [ 158 | { 159 | type: FlowchartConstants.leftConnectorType, 160 | id: '3' 161 | }, 162 | { 163 | type: FlowchartConstants.rightConnectorType, 164 | id: '4' 165 | } 166 | ] 167 | }, 168 | { 169 | name: 'Easy Integration', 170 | id: '4', 171 | x: 1000, 172 | y: 100, 173 | color: '#000', 174 | borderColor: '#000', 175 | connectors: [ 176 | { 177 | type: FlowchartConstants.leftConnectorType, 178 | id: '5' 179 | }, 180 | { 181 | type: FlowchartConstants.rightConnectorType, 182 | id: '6' 183 | } 184 | ] 185 | }, 186 | { 187 | name: 'Customizable templates', 188 | id: '5', 189 | x: 1300, 190 | y: 100, 191 | color: '#000', 192 | borderColor: '#000', 193 | connectors: [ 194 | { 195 | type: FlowchartConstants.leftConnectorType, 196 | id: '7' 197 | }, 198 | { 199 | type: FlowchartConstants.rightConnectorType, 200 | id: '8' 201 | } 202 | ] 203 | } 204 | ] 205 | ); 206 | this.model.edges.push(... 207 | [ 208 | { 209 | source: '2', 210 | destination: '3', 211 | label: 'label1' 212 | }, 213 | { 214 | source: '4', 215 | destination: '5', 216 | label: 'label2' 217 | }, 218 | { 219 | source: '6', 220 | destination: '7', 221 | label: 'label3' 222 | } 223 | ] 224 | ); 225 | } 226 | 227 | @HostListener('keydown.control.a', ['$event']) 228 | public onCtrlA(event: KeyboardEvent) { 229 | this.fcCanvas.modelService.selectAll(); 230 | } 231 | 232 | @HostListener('keydown.esc', ['$event']) 233 | public onEsc(event: KeyboardEvent) { 234 | this.fcCanvas.modelService.deselectAll(); 235 | } 236 | 237 | @HostListener('keydown', ['$event']) 238 | public onKeydown(event: KeyboardEvent) { 239 | if (event.keyCode === DELETE) { 240 | this.fcCanvas.modelService.deleteSelected(); 241 | } 242 | } 243 | 244 | public addNewNode() { 245 | const nodeName = prompt('Enter a node name:', 'New node'); 246 | if (!nodeName) { 247 | return; 248 | } 249 | 250 | const newNode: FcNode = { 251 | name: nodeName, 252 | id: (this.nextNodeID++) + '', 253 | x: 200, 254 | y: 100, 255 | color: '#F15B26', 256 | connectors: [ 257 | { 258 | id: (this.nextConnectorID++) + '', 259 | type: FlowchartConstants.leftConnectorType 260 | }, 261 | { 262 | id: (this.nextConnectorID++) + '', 263 | type: FlowchartConstants.rightConnectorType 264 | } 265 | ] 266 | }; 267 | this.model.nodes.push(newNode); 268 | } 269 | 270 | public activateWorkflow() { 271 | this.model.edges.forEach((edge) => { 272 | edge.active = !edge.active; 273 | }); 274 | this.fcCanvas.modelService.detectChanges(); 275 | } 276 | 277 | public deleteSelected() { 278 | this.fcCanvas.modelService.deleteSelected(); 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | 4 | import { AppComponent } from './app.component'; 5 | import { FC_NODE_COMPONENT_CONFIG, NgxFlowchartModule } from 'ngx-flowchart-dev'; 6 | import { TestFcNodeComponent } from './test-node.component'; 7 | 8 | @NgModule({ 9 | /*providers: [ 10 | { 11 | provide: FC_NODE_COMPONENT_CONFIG, 12 | useValue: { 13 | nodeComponentType: TestFcNodeComponent 14 | } 15 | } 16 | ],*/ 17 | declarations: [ 18 | AppComponent, 19 | TestFcNodeComponent 20 | ], 21 | imports: [ 22 | BrowserModule, 23 | NgxFlowchartModule 24 | ], 25 | bootstrap: [AppComponent] 26 | }) 27 | export class AppModule { } 28 | -------------------------------------------------------------------------------- /src/app/test-node.component.html: -------------------------------------------------------------------------------- 1 |
3 | {{ node | json }} 4 |
5 | -------------------------------------------------------------------------------- /src/app/test-node.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { FcNodeComponent } from 'ngx-flowchart-dev'; 3 | 4 | @Component({ 5 | selector: 'app-default-node', 6 | templateUrl: './test-node.component.html', 7 | styleUrls: [] 8 | }) 9 | export class TestFcNodeComponent extends FcNodeComponent { 10 | 11 | constructor() { 12 | super(); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thingsboard/ngx-flowchart/77c6bf52a3b43baee96fe69a9fe8b0d9a307c241/src/assets/.gitkeep -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thingsboard/ngx-flowchart/77c6bf52a3b43baee96fe69a9fe8b0d9a307c241/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NgxFlowchart 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** 22 | * By default, zone.js will patch all possible macroTask and DomEvents 23 | * user can disable parts of macroTask/DomEvents patch by setting following flags 24 | * because those flags need to be set before `zone.js` being loaded, and webpack 25 | * will put import in the top of bundle, so user need to create a separate file 26 | * in this directory (for example: zone-flags.ts), and put the following flags 27 | * into that file, and then add the following code before importing zone.js. 28 | * import './zone-flags.ts'; 29 | * 30 | * The flags allowed in zone-flags.ts are listed here. 31 | * 32 | * The following flags will work for all browsers. 33 | * 34 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 35 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 36 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 37 | * 38 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 39 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 40 | * 41 | * (window as any).__Zone_enable_cross_context_check = true; 42 | * 43 | */ 44 | 45 | /*************************************************************************************************** 46 | * Zone JS is required by default for Angular itself. 47 | */ 48 | import 'zone.js'; // Included with Angular CLI. 49 | 50 | /*************************************************************************************************** 51 | * APPLICATION IMPORTS 52 | */ 53 | -------------------------------------------------------------------------------- /src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | 3 | body { 4 | font-family: sans-serif; 5 | margin: 0; 6 | display: flex; 7 | flex-direction: column; 8 | flex: 1; 9 | height: 100vh; 10 | } 11 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/app", 5 | "types": ["jquery"] 6 | }, 7 | "angularCompilerOptions": { 8 | "fullTemplateTypeCheck": true 9 | }, 10 | "files": [ 11 | "src/main.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.d.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "forceConsistentCasingInFileNames": true, 7 | "esModuleInterop": true, 8 | "noImplicitOverride": true, 9 | "noPropertyAccessFromIndexSignature": true, 10 | "noFallthroughCasesInSwitch": true, 11 | "sourceMap": true, 12 | "declaration": false, 13 | "module": "es2020", 14 | "moduleResolution": "node", 15 | "emitDecoratorMetadata": true, 16 | "experimentalDecorators": true, 17 | "importHelpers": true, 18 | "target": "ES2022", 19 | "typeRoots": [ 20 | "node_modules/@types" 21 | ], 22 | "lib": [ 23 | "es2018", 24 | "dom" 25 | ], 26 | "paths": { 27 | "ngx-flowchart": [ 28 | "dist/ngx-flowchart" 29 | ], 30 | "ngx-flowchart/*": [ 31 | "dist/ngx-flowchart/*" 32 | ], 33 | "ngx-flowchart-dev": [ 34 | "projects/ngx-flowchart/src/public-api" 35 | ], 36 | "ngx-flowchart1": [ 37 | "dist/ngx-flowchart1" 38 | ] 39 | }, 40 | "useDefineForClassFields": false 41 | }, 42 | "angularCompilerOptions": { 43 | "enableI18nLegacyMessageIdFormat": false, 44 | "strictInjectionParameters": true, 45 | "strictInputAccessModifiers": true, 46 | "strictTemplates": false 47 | } 48 | } 49 | --------------------------------------------------------------------------------