├── .angulardoc.json ├── .editorconfig ├── .gitignore ├── .gitlab-ci.yml ├── README.md ├── angular.json ├── e2e ├── protractor.conf.js ├── src │ ├── app.e2e-spec.ts │ └── app.po.ts └── tsconfig.e2e.json ├── package-lock.json ├── package.json ├── screenshot.png ├── server.js ├── server ├── db.js ├── db │ └── workflow.json └── workflow.js ├── src ├── app │ ├── action-bar │ │ ├── action-bar.component.css │ │ ├── action-bar.component.html │ │ ├── action-bar.component.spec.ts │ │ └── action-bar.component.ts │ ├── activity │ │ ├── activity.component.css │ │ ├── activity.component.html │ │ ├── activity.component.spec.ts │ │ └── activity.component.ts │ ├── apiclient │ │ ├── api-client.service.spec.ts │ │ └── api-client.service.ts │ ├── app.component.css │ ├── app.component.html │ ├── app.component.spec.ts │ ├── app.component.ts │ ├── app.module.ts │ ├── codepannel │ │ ├── codepannel.component.css │ │ ├── codepannel.component.html │ │ ├── codepannel.component.spec.ts │ │ └── codepannel.component.ts │ ├── component-selector │ │ ├── component-selector.component.css │ │ ├── component-selector.component.html │ │ ├── component-selector.component.spec.ts │ │ └── component-selector.component.ts │ ├── condition │ │ ├── condition.component.css │ │ ├── condition.component.html │ │ ├── condition.component.spec.ts │ │ └── condition.component.ts │ ├── connector │ │ ├── connector.component.css │ │ ├── connector.component.html │ │ ├── connector.component.spec.ts │ │ └── connector.component.ts │ ├── control-bar │ │ ├── control-bar.component.css │ │ ├── control-bar.component.html │ │ ├── control-bar.component.spec.ts │ │ └── control-bar.component.ts │ ├── control-type │ │ └── controlType.ts │ ├── interface │ │ └── IDraggableControl.ts │ ├── link-bar │ │ ├── link-bar.component.css │ │ ├── link-bar.component.html │ │ ├── link-bar.component.spec.ts │ │ └── link-bar.component.ts │ ├── model │ │ └── dataModel.ts │ ├── navbar │ │ ├── navbar.component.css │ │ ├── navbar.component.html │ │ ├── navbar.component.spec.ts │ │ └── navbar.component.ts │ ├── pipes │ │ └── Keys.ts │ ├── property-bar │ │ ├── property-bar.component.css │ │ ├── property-bar.component.html │ │ ├── property-bar.component.spec.ts │ │ └── property-bar.component.ts │ ├── receiver │ │ ├── receiver.component.css │ │ ├── receiver.component.html │ │ ├── receiver.component.spec.ts │ │ └── receiver.component.ts │ ├── services │ │ ├── connection.service.spec.ts │ │ ├── connection.service.ts │ │ ├── event-service.service.spec.ts │ │ ├── event-service.service.ts │ │ ├── state.service.spec.ts │ │ ├── state.service.ts │ │ ├── svg-arrowUtility.service.spec.ts │ │ ├── svg-arrowUtility.service.ts │ │ ├── utililty.service.spec.ts │ │ └── utililty.service.ts │ ├── startend │ │ ├── startend.component.css │ │ ├── startend.component.html │ │ ├── startend.component.spec.ts │ │ └── startend.component.ts │ ├── subaction │ │ ├── subaction.component.css │ │ ├── subaction.component.html │ │ ├── subaction.component.spec.ts │ │ └── subaction.component.ts │ ├── svg │ │ └── start.svg │ ├── template │ │ ├── template.component.css │ │ ├── template.component.html │ │ ├── template.component.spec.ts │ │ └── template.component.ts │ ├── twodimension-arrow │ │ ├── twodimension-arrow.component.css │ │ ├── twodimension-arrow.component.html │ │ ├── twodimension-arrow.component.spec.ts │ │ └── twodimension-arrow.component.ts │ ├── under-construction │ │ ├── under-construction.component.css │ │ ├── under-construction.component.html │ │ ├── under-construction.component.spec.ts │ │ └── under-construction.component.ts │ └── virtual-arrow │ │ ├── virtual-arrow.component.css │ │ ├── virtual-arrow.component.html │ │ ├── virtual-arrow.component.spec.ts │ │ └── virtual-arrow.component.ts ├── assets │ ├── .gitkeep │ ├── activity.svg │ ├── arrow.svg │ ├── condition.svg │ ├── end.svg │ ├── grid.gif │ ├── loading.gif │ ├── start.svg │ ├── success.gif │ ├── success_transparentbg.gif │ ├── under_construction_icon.png │ └── workflow.png ├── browserslist ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── karma.conf.js ├── main.ts ├── polyfills.ts ├── styles.css ├── test.ts ├── tsconfig.app.json ├── tsconfig.spec.json └── tslint.json ├── tsconfig.json ├── tslint.json ├── vsstart.bat └── workflow-ui.gif /.angulardoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "repoId": "aebb9524-aa4b-44c1-a063-51b62b06496c", 3 | "lastSync": 0 4 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | 8 | # dependencies 9 | /node_modules 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | .c9/ 16 | *.launch 17 | .settings/ 18 | *.sublime-workspace 19 | 20 | # IDE - VSCode 21 | .vscode/* 22 | !.vscode/settings.json 23 | !.vscode/tasks.json 24 | !.vscode/launch.json 25 | !.vscode/extensions.json 26 | 27 | # misc 28 | /.sass-cache 29 | /connect.lock 30 | /coverage 31 | /libpeerconnection.log 32 | npm-debug.log 33 | yarn-error.log 34 | testem.log 35 | /typings 36 | 37 | # System Files 38 | .DS_Store 39 | Thumbs.db 40 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | stages: 2 | - build 3 | 4 | job 1: 5 | stage: build 6 | script: ng build 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Workflow-UI v2.0.0 2 | 3 | ##### Workflow-UI is a diagramatic web ui tool to create workflows in a visualize way using different controls and convert into a generic `json` format. It allows controls to be drag and drop with editable facility and also customizable output json. 4 | 5 | ![](workflow-ui.gif) 6 | 7 | ## Table of contents 8 | 9 | * [Why Workflow-UI v2.0.0](#why-workflow-ui-v2.0.0) 10 | * [Usage](#usage) 11 | * [Technologies used](#technologies-used) 12 | * [Backend APIs](#backend-apis) 13 | * [How to Build, Run & Deploy](#build-run-deploy) 14 | * [Future release](#future-release) 15 | 16 | ## Why Workflow-UI v2.0.0 17 | 18 | * Drag/Drop facility for drawing workflows 19 | * Debugger window (Displaying instant `json` data and `co-ordinate` details for control/connectors) 20 | * Simple for use and great visualization 21 | * Customizabe json data object format 22 | 23 | ##### New features added in version 2.0.0 24 | * Drag connection from each direction of control and connect to any direction 25 | * Movement of arrows 26 | * key binding for movement 27 | * On screen mouse click movement 28 | * Toggle Yes/No direction in condition control 29 | * Toggle expand/collapse for propery/control/link bars 30 | 31 | ## Technologies Used 32 | 33 | * [Angular CLI](https://github.com/angular/angular-cli) version 6.1.1. 34 | * SVG (for drawing different shapes control) 35 | * bootstrap version 4.1.3 36 | * ngx-bootstrap version 3.0.1 37 | * font-awesome version 4.7.0 38 | 39 | ## Backend APIs 40 | 41 | Following API endpoints are configued in ui to fetch and post data from backend. 42 | 43 | Find all the api endpoints in api-client file from below path 44 | ``` 45 | workflow-ui/src/app/apiclient/api-client.service.ts 46 | ``` 47 | 48 | Response format of each end points 49 | 50 | ``` 51 | GET: http://apidomain/workflows/getsteptypes 52 | 200 OK, 53 | // response data 54 | [{ 55 | stepType: 'WorkflowCore.Service.Demo.HelloWorldService', 56 | stepName: 'HelloWorldService', 57 | assembly: 'WorkflowCore'}, 58 | { stepType: 'WorkflowCore.Service.Demo.AddNumbers', 59 | stepName: 'AddNumbers', 60 | assembly: 'WorkflowCore'}, 61 | { stepType: 'WorkflowCore.Service.Demo.PrintMessageService', 62 | stepName: 'PrintMessageService', 63 | assembly: 'WorkflowCore' 64 | }] 65 | 66 | ``` 67 | 68 | ``` 69 | GET: http://apidomain/workflows/DataContextAttributes 70 | 200 OK, 71 | // response data 72 | { 73 | Input: ['Value1', 'Value2', 'Value3', 'message'], 74 | Output: ['Value1', 'Value2', 'Value3', 'Result'] 75 | } 76 | ``` 77 | 78 | ``` 79 | GET: http://apidomain/workflows/GetAllWorkflows 80 | 200 OK, 81 | // response data 82 | [ 83 | { 84 | "id": 1, 85 | "name": "untitled_template_1", 86 | "jsonObject": "{\"controls\":[{\"id\":1,\"type\":\"circle\",\"cordinate\":{\"x\":\"386\",\"y\":\"61\"},\"nextId\":[2],\"data\":{\"Id\":\"Start\",\"StepType\":\"Start\",\"NextStepId\":\"\"},\"isSelected\":false,\"isMultiConnect\":false,\"conditionYesId\":[],\"isIntial\":false},{\"id\":2,\"type\":\"activity\",\"cordinate\":{\"x\":\"269\",\"y\":\"191\"},\"nextId\":[3],\"data\":{\"Id\":\"Activity1\",\"StepType\":\"WorkflowCore.Service.Demo.PrintMessageService, WorkflowCore\",\"NextStepId\":\"\"},\"isSelected\":false,\"isMultiConnect\":false,\"conditionYesId\":[]},{\"id\":3,\"type\":\"circle\",\"cordinate\":{\"x\":398,\"y\":\"401\"},\"nextId\":[],\"data\":{\"Id\":\"End\",\"StepType\":\"Start\",\"NextStepId\":\"\"},\"isSelected\":false,\"isMultiConnect\":false,\"conditionYesId\":[],\"isIntial\":false}],\"links\":[{\"id\":1,\"fromId\":\"1\",\"toId\":2,\"cordinate\":{\"x1\":\"386\",\"y1\":81,\"x2\":344,\"y2\":\"191\"},\"fromDirection\":\"down\",\"direction\":\"down\",\"isSelected\":false},{\"id\":2,\"fromId\":\"2\",\"toId\":3,\"cordinate\":{\"x1\":344,\"y1\":241,\"x2\":398,\"y2\":381},\"fromDirection\":\"down\",\"direction\":\"down\",\"isSelected\":false}],\"parsedData\":{\"Id\":\"untitled_template_1\",\"DataType\":\"WorkflowCore.Models.DataContext, WorkflowCore\",\"Version\":\"1\",\"Steps\":[{\"Id\":\"Activity1\",\"StepType\":\"WorkflowCore.Service.Demo.PrintMessageService, WorkflowCore\",\"NextStepId\":\"\"}]}}", 87 | "status": "active", 88 | "addedOn": "Mon Sep 24 2018 12:08:05 GMT+0530 (India Standard Time)", 89 | "_id": "b49714d9aa46481db7d86d47e3b9831e" 90 | } 91 | ] 92 | ``` 93 | ## Build Run Deploy 94 | 95 | * Run `npm install` to install all the dependencies used in this app (availabe in `package.json` file) 96 | * Run `ng build` to build the project. 97 | * The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. 98 | * Run `ng serve` to run in local server. (it will run in `http://localhost:4200` the default port of angular-cli server, to run in different port use `ng serve --port=`) 99 | * For deployment run `ng build` and deploy the `dist/` directory contents to any http server. 100 | 101 | ## Future Release 102 | 103 | 104 | * Resizing the controls 105 | * New controls in toolbar 106 | * Real-time status notification of each workflow steps while executing from backend (using message queue) 107 | * Undo/Redo feature 108 | * Export/Import workflow json from local drive 109 | 110 | 111 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "ch-ui": { 7 | "root": "", 8 | "sourceRoot": "src", 9 | "projectType": "application", 10 | "prefix": "app", 11 | "schematics": {}, 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:browser", 15 | "options": { 16 | "outputPath": "dist/ch-ui", 17 | "index": "src/index.html", 18 | "main": "src/main.ts", 19 | "polyfills": "src/polyfills.ts", 20 | "tsConfig": "src/tsconfig.app.json", 21 | "assets": [ 22 | "src/favicon.ico", 23 | "src/assets" 24 | ], 25 | "styles": [ 26 | "./node_modules/bootstrap/dist/css/bootstrap.min.css", 27 | "./node_modules/font-awesome/css/font-awesome.min.css", 28 | "src/styles.css" 29 | ], 30 | "scripts": [ 31 | "./node_modules/jquery/dist/jquery.min.js", 32 | "./node_modules/bootstrap/dist/js/bootstrap.min.js" 33 | ] 34 | }, 35 | "configurations": { 36 | "production": { 37 | "fileReplacements": [ 38 | { 39 | "replace": "src/environments/environment.ts", 40 | "with": "src/environments/environment.prod.ts" 41 | } 42 | ], 43 | "optimization": true, 44 | "outputHashing": "all", 45 | "sourceMap": false, 46 | "extractCss": true, 47 | "namedChunks": false, 48 | "aot": true, 49 | "extractLicenses": true, 50 | "vendorChunk": false, 51 | "buildOptimizer": true 52 | } 53 | } 54 | }, 55 | "serve": { 56 | "builder": "@angular-devkit/build-angular:dev-server", 57 | "options": { 58 | "browserTarget": "ch-ui:build" 59 | }, 60 | "configurations": { 61 | "production": { 62 | "browserTarget": "ch-ui:build:production" 63 | } 64 | } 65 | }, 66 | "extract-i18n": { 67 | "builder": "@angular-devkit/build-angular:extract-i18n", 68 | "options": { 69 | "browserTarget": "ch-ui:build" 70 | } 71 | }, 72 | "test": { 73 | "builder": "@angular-devkit/build-angular:karma", 74 | "options": { 75 | "main": "src/test.ts", 76 | "polyfills": "src/polyfills.ts", 77 | "tsConfig": "src/tsconfig.spec.json", 78 | "karmaConfig": "src/karma.conf.js", 79 | "styles": [ 80 | "src/styles.css" 81 | ], 82 | "scripts": [], 83 | "assets": [ 84 | "src/favicon.ico", 85 | "src/assets" 86 | ] 87 | } 88 | }, 89 | "lint": { 90 | "builder": "@angular-devkit/build-angular:tslint", 91 | "options": { 92 | "tsConfig": [ 93 | "src/tsconfig.app.json", 94 | "src/tsconfig.spec.json" 95 | ], 96 | "exclude": [ 97 | "**/node_modules/**" 98 | ] 99 | } 100 | } 101 | } 102 | }, 103 | "ch-ui-e2e": { 104 | "root": "e2e/", 105 | "projectType": "application", 106 | "architect": { 107 | "e2e": { 108 | "builder": "@angular-devkit/build-angular:protractor", 109 | "options": { 110 | "protractorConfig": "e2e/protractor.conf.js", 111 | "devServerTarget": "ch-ui:serve" 112 | }, 113 | "configurations": { 114 | "production": { 115 | "devServerTarget": "ch-ui:serve:production" 116 | } 117 | } 118 | }, 119 | "lint": { 120 | "builder": "@angular-devkit/build-angular:tslint", 121 | "options": { 122 | "tsConfig": "e2e/tsconfig.e2e.json", 123 | "exclude": [ 124 | "**/node_modules/**" 125 | ] 126 | } 127 | } 128 | } 129 | } 130 | }, 131 | "defaultProject": "ch-ui" 132 | } 133 | -------------------------------------------------------------------------------- /e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './src/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: require('path').join(__dirname, './tsconfig.e2e.json') 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; -------------------------------------------------------------------------------- /e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('workspace-project App', () => { 4 | let page: AppPage; 5 | 6 | beforeEach(() => { 7 | page = new AppPage(); 8 | }); 9 | 10 | it('should display welcome message', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('Welcome to ch-ui!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ch-ui", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "node server.js", 7 | "build": "ng build", 8 | "lint": "ng lint", 9 | "postinstall": "ng build --prod" 10 | }, 11 | "private": true, 12 | "engines": { 13 | "node": "8.11.2", 14 | "npm": "5.6.0" 15 | }, 16 | "dependencies": { 17 | "@angular-devkit/build-angular": "~0.7.0", 18 | "@angular/animations": "^6.1.0", 19 | "@angular/cdk": "^7.0.4", 20 | "@angular/cli": "~6.1.1", 21 | "@angular/common": "^6.1.0", 22 | "@angular/compiler": "^6.1.0", 23 | "@angular/compiler-cli": "^6.1.0", 24 | "@angular/core": "^6.1.0", 25 | "@angular/forms": "^6.1.0", 26 | "@angular/http": "^6.1.0", 27 | "@angular/platform-browser": "^6.1.0", 28 | "@angular/platform-browser-dynamic": "^6.1.0", 29 | "@angular/router": "^6.1.0", 30 | "body-parser": "^1.18.3", 31 | "bootstrap": "^4.1.3", 32 | "context-menu-angular6": "0.0.6", 33 | "core-js": "^2.5.4", 34 | "cors": "^2.8.4", 35 | "diskdb": "^0.1.17", 36 | "express": "^4.16.3", 37 | "font-awesome": "^4.7.0", 38 | "jquery": "^3.3.1", 39 | "method-override": "^3.0.0", 40 | "ngx-bootstrap": "^3.0.1", 41 | "path": "^0.12.7", 42 | "popper.js": "^1.14.4", 43 | "rxjs": "^6.0.0", 44 | "typescript": "~2.7.2", 45 | "zone.js": "~0.8.26" 46 | }, 47 | "devDependencies": { 48 | "@angular-devkit/build-angular": "~0.7.0", 49 | "@angular/cli": "~6.1.1", 50 | "@angular/compiler-cli": "^6.1.0", 51 | "@angular/language-service": "^6.1.0", 52 | "@types/jasmine": "~2.8.6", 53 | "@types/jasminewd2": "~2.0.3", 54 | "@types/node": "~8.9.4", 55 | "codelyzer": "~4.2.1", 56 | "enhanced-resolve": "^4.1.0", 57 | "jasmine-core": "~2.99.1", 58 | "jasmine-spec-reporter": "~4.2.1", 59 | "karma": "~1.7.1", 60 | "karma-chrome-launcher": "~2.2.0", 61 | "karma-coverage-istanbul-reporter": "~2.0.0", 62 | "karma-jasmine": "~1.1.1", 63 | "karma-jasmine-html-reporter": "^0.2.2", 64 | "protractor": "~5.3.0", 65 | "ts-node": "~5.0.1", 66 | "tslint": "~5.9.1", 67 | "typescript": "~2.7.2" 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saswat-pramati/workflow-ui/a95f539c1b7ea82a7e7ab5b68d6cf96773b875c0/screenshot.png -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | //Install express server 2 | const express = require('express'); 3 | const path = require('path'); 4 | var workflow = require('./server/workflow'); 5 | var bodyParser = require('body-parser'); 6 | var methodOverride = require('method-override'); 7 | var cors = require('cors'); 8 | 9 | const app = express(); 10 | 11 | const PORT = 8080; 12 | 13 | // Serve only the static files form the dist directory 14 | app.use(express.static(__dirname + '/dist/ch-ui')); 15 | 16 | app.disable('x-powered-by'); 17 | app.use(bodyParser.urlencoded({ 18 | 'extended': 'true' 19 | })); // parse application/x-www-form-urlencoded 20 | app.use(bodyParser.json()); // parse application/jso 21 | app.use(bodyParser.json({ 22 | type: 'application/vnd.api+json' 23 | })); // parse application/vnd.api+json as json 24 | 25 | app.use(methodOverride()); 26 | 27 | app.use(cors({origin: '*'})); 28 | 29 | app.use('/workflows', workflow); 30 | 31 | // app.get('/*', function(req,res) { 32 | 33 | // res.sendFile(path.join(__dirname+'/dist/ch-ui/index.html')); 34 | // }); 35 | 36 | // Start the app by listening on the default Heroku port 37 | app.listen(process.env.PORT || PORT, () => { 38 | console.log('server started on port: ' + PORT); 39 | }); 40 | -------------------------------------------------------------------------------- /server/db.js: -------------------------------------------------------------------------------- 1 | var db = require('diskdb'); 2 | 3 | db = db.connect('server/db', ['workflow']); 4 | 5 | exports.saveWorkflow = (data) => { 6 | return db.workflow.save(data); 7 | } 8 | 9 | exports.getworkflow = () => { 10 | return db.workflow.find(); 11 | } 12 | 13 | exports.getworkflowCount = () => { 14 | return db.workflow.count(); 15 | } 16 | -------------------------------------------------------------------------------- /server/db/workflow.json: -------------------------------------------------------------------------------- 1 | [{"id":1,"name":"untitled_template_1","jsonObject":"{\"controls\":[{\"id\":1,\"type\":\"circle\",\"cordinate\":{\"x\":\"386\",\"y\":\"61\"},\"nextId\":[2],\"data\":{\"Id\":\"Start\",\"StepType\":\"Start\",\"NextStepId\":\"\"},\"isSelected\":false,\"isMultiConnect\":false,\"conditionYesId\":[],\"isIntial\":false},{\"id\":2,\"type\":\"activity\",\"cordinate\":{\"x\":\"269\",\"y\":\"191\"},\"nextId\":[3],\"data\":{\"Id\":\"Activity1\",\"StepType\":\"WorkflowCore.Service.Demo.PrintMessageService, WorkflowCore\",\"NextStepId\":\"\"},\"isSelected\":false,\"isMultiConnect\":false,\"conditionYesId\":[]},{\"id\":3,\"type\":\"circle\",\"cordinate\":{\"x\":398,\"y\":\"401\"},\"nextId\":[],\"data\":{\"Id\":\"End\",\"StepType\":\"Start\",\"NextStepId\":\"\"},\"isSelected\":false,\"isMultiConnect\":false,\"conditionYesId\":[],\"isIntial\":false}],\"links\":[{\"id\":1,\"fromId\":\"1\",\"toId\":2,\"cordinate\":{\"x1\":\"386\",\"y1\":81,\"x2\":344,\"y2\":\"191\"},\"fromDirection\":\"down\",\"direction\":\"down\",\"isSelected\":false},{\"id\":2,\"fromId\":\"2\",\"toId\":3,\"cordinate\":{\"x1\":344,\"y1\":241,\"x2\":398,\"y2\":381},\"fromDirection\":\"down\",\"direction\":\"down\",\"isSelected\":false}],\"parsedData\":{\"Id\":\"untitled_template_1\",\"DataType\":\"WorkflowCore.Models.DataContext, WorkflowCore\",\"Version\":\"1\",\"Steps\":[{\"Id\":\"Activity1\",\"StepType\":\"WorkflowCore.Service.Demo.PrintMessageService, WorkflowCore\",\"NextStepId\":\"\"}]}}","status":"active","addedOn":"Mon Sep 24 2018 12:08:05 GMT+0530 (India Standard Time)","_id":"b49714d9aa46481db7d86d47e3b9831e"},{"id":2,"name":"untitled_template_2","jsonObject":"{\"controls\":[{\"id\":1,\"type\":\"circle\",\"cordinate\":{\"x\":\"360\",\"y\":\"102\"},\"nextId\":[2],\"data\":{\"Id\":\"Start\",\"StepType\":\"Start\",\"NextStepId\":\"\"},\"isSelected\":false,\"isMultiConnect\":false,\"conditionYesId\":[],\"isIntial\":false},{\"id\":2,\"type\":\"activity\",\"cordinate\":{\"x\":299,\"y\":\"234\"},\"nextId\":[3],\"data\":{\"Id\":\"Activity1\",\"StepType\":\"WorkflowCore.Service.Demo.AddNumbers, WorkflowCore\",\"NextStepId\":\"\",\"Inputs\":{\"Value1\":\"data.Value1\",\"Value2\":\"data.Value2\"}},\"isSelected\":false,\"isMultiConnect\":false,\"conditionYesId\":[]},{\"id\":3,\"type\":\"circle\",\"cordinate\":{\"x\":\"268\",\"y\":\"422\"},\"nextId\":[],\"data\":{\"Id\":\"End\",\"StepType\":\"Start\",\"NextStepId\":\"\"},\"isSelected\":false,\"isMultiConnect\":false,\"conditionYesId\":[],\"isIntial\":false}],\"links\":[{\"id\":1,\"fromId\":\"2\",\"toId\":3,\"cordinate\":{\"x1\":374,\"y1\":284,\"x2\":\"268\",\"y2\":402},\"fromDirection\":\"down\",\"direction\":\"down\",\"isSelected\":false},{\"id\":2,\"fromId\":\"1\",\"toId\":2,\"cordinate\":{\"x1\":\"360\",\"y1\":122,\"x2\":374,\"y2\":\"234\"},\"fromDirection\":\"down\",\"direction\":\"down\",\"isSelected\":false}],\"parsedData\":{\"Id\":\"untitled_template_2\",\"DataType\":\"WorkflowCore.Models.DataContext, WorkflowCore\",\"Version\":\"1\",\"Steps\":[{\"Id\":\"Activity1\",\"StepType\":\"WorkflowCore.Service.Demo.AddNumbers, WorkflowCore\",\"NextStepId\":\"\",\"Inputs\":{\"Value1\":\"data.Value1\",\"Value2\":\"data.Value2\"}}]}}","status":"active","addedOn":"Mon Sep 24 2018 12:19:48 GMT+0530 (India Standard Time)","_id":"b834bc03477a42378ce3593bfc8d5c30"},{"id":3,"name":"untitled_template_3","jsonObject":"{\"controls\":[{\"id\":1,\"type\":\"circle\",\"cordinate\":{\"x\":\"376\",\"y\":\"37\"},\"nextId\":[2],\"data\":{\"Id\":\"Start\",\"StepType\":\"Start\",\"NextStepId\":\"\"},\"isSelected\":false,\"isMultiConnect\":false,\"conditionYesId\":[],\"isIntial\":false},{\"id\":2,\"type\":\"activity\",\"cordinate\":{\"x\":\"318\",\"y\":\"136\"},\"nextId\":[3],\"data\":{\"Id\":\"Activity1\",\"StepType\":\"WorkflowCore.Service.Demo.PrintMessageService, WorkflowCore\",\"NextStepId\":\"\",\"Inputs\":{\"Value3\":\"data.Value3\",\"Value2\":\"data.Value2\"}},\"isSelected\":false,\"isMultiConnect\":false,\"conditionYesId\":[]},{\"id\":3,\"type\":\"circle\",\"cordinate\":{\"x\":\"264\",\"y\":\"323\"},\"nextId\":[],\"data\":{\"Id\":\"End\",\"StepType\":\"Start\",\"NextStepId\":\"\"},\"isSelected\":false,\"isMultiConnect\":false,\"conditionYesId\":[],\"isIntial\":false}],\"links\":[{\"id\":1,\"fromId\":\"1\",\"toId\":2,\"cordinate\":{\"x1\":\"376\",\"y1\":57,\"x2\":393,\"y2\":\"136\"},\"fromDirection\":\"down\",\"direction\":\"down\",\"isSelected\":false},{\"id\":2,\"fromId\":\"2\",\"toId\":3,\"cordinate\":{\"x1\":393,\"y1\":186,\"x2\":\"264\",\"y2\":303},\"fromDirection\":\"down\",\"direction\":\"down\",\"isSelected\":false}],\"parsedData\":{\"Id\":\"untitled_template_3\",\"DataType\":\"WorkflowCore.Models.DataContext, WorkflowCore\",\"Version\":\"1\",\"Steps\":[{\"Id\":\"Activity1\",\"StepType\":\"WorkflowCore.Service.Demo.PrintMessageService, WorkflowCore\",\"NextStepId\":\"\",\"Inputs\":{\"Value3\":\"data.Value3\",\"Value2\":\"data.Value2\"}}]}}","status":"active","addedOn":"Mon Sep 24 2018 14:38:17 GMT+0530 (India Standard Time)","_id":"c3c36c8befee40d0a43f2a22486526ae"},{"id":4,"name":"untitled_template","jsonObject":"{\"controls\":[{\"id\":1,\"type\":\"circle\",\"cordinate\":{\"x\":\"360\",\"y\":\"102\"},\"nextId\":[2],\"data\":{\"Id\":\"Start\",\"StepType\":\"Start\",\"NextStepId\":\"\"},\"isSelected\":false,\"isMultiConnect\":false,\"conditionYesId\":[],\"isIntial\":false},{\"id\":2,\"type\":\"activity\",\"cordinate\":{\"x\":\"285\",\"y\":236},\"nextId\":[3],\"data\":{\"Id\":\"Activity1\",\"StepType\":\"WorkflowCore.Service.Demo.AddNumbers, WorkflowCore\",\"NextStepId\":\"\",\"Inputs\":{\"Value1\":\"data.Value1\",\"Value2\":\"data.Value2\"}},\"isSelected\":false,\"isMultiConnect\":false,\"conditionYesId\":[]},{\"id\":3,\"type\":\"circle\",\"cordinate\":{\"x\":\"360\",\"y\":418},\"nextId\":[],\"data\":{\"Id\":\"End\",\"StepType\":\"Start\",\"NextStepId\":\"\"},\"isSelected\":true,\"isMultiConnect\":false,\"conditionYesId\":[],\"isIntial\":false}],\"links\":[{\"id\":1,\"fromId\":\"2\",\"toId\":3,\"cordinate\":{\"x1\":360,\"y1\":286,\"x2\":\"360\",\"y2\":398},\"fromDirection\":\"down\",\"direction\":\"down\",\"isSelected\":false},{\"id\":2,\"fromId\":\"1\",\"toId\":2,\"cordinate\":{\"x1\":\"360\",\"y1\":122,\"x2\":360,\"y2\":236},\"fromDirection\":\"down\",\"direction\":\"down\",\"isSelected\":false}],\"parsedData\":{\"Id\":\"untitled_template\",\"DataType\":\"WorkflowCore.Models.DataContext, WorkflowCore\",\"Version\":\"1\",\"Steps\":[{\"Id\":\"Activity1\",\"StepType\":\"WorkflowCore.Service.Demo.AddNumbers, WorkflowCore\",\"NextStepId\":\"\",\"Inputs\":{\"Value1\":\"data.Value1\",\"Value2\":\"data.Value2\"}}]}}","status":"active","addedOn":"Tue Sep 25 2018 15:40:45 GMT+0530 (India Standard Time)","_id":"4c0ef8668f22465b97e9f3b60ce27bde"},{"id":5,"name":"untitled_template_5","jsonObject":"{\"controls\":[{\"id\":1,\"type\":\"circle\",\"cordinate\":{\"x\":\"219\",\"y\":\"107\"},\"dimension\":{\"height\":40,\"width\":80},\"nextId\":[\"2\"],\"data\":{\"Id\":\"Start\",\"StepType\":\"Start\",\"NextStepId\":\"\"},\"isSelected\":false,\"isMultiConnect\":false,\"conditionYesId\":[]},{\"id\":2,\"type\":\"activity\",\"cordinate\":{\"x\":\"366\",\"y\":\"125\"},\"dimension\":{\"height\":50,\"width\":150},\"nextId\":[\"3\"],\"data\":{\"Id\":\"Activity1\",\"StepType\":\"WorkflowCore.Service.Demo.HelloWorldService, WorkflowCore\",\"NextStepId\":\"\"},\"isSelected\":false,\"isMultiConnect\":false,\"conditionYesId\":[]},{\"id\":3,\"type\":\"circle\",\"cordinate\":{\"x\":638,\"y\":\"201\"},\"dimension\":{\"height\":40,\"width\":80},\"nextId\":[],\"data\":{\"Id\":\"End\",\"StepType\":\"Start\",\"NextStepId\":\"\"},\"isSelected\":false,\"isMultiConnect\":false,\"conditionYesId\":[]}],\"links\":[{\"id\":1,\"fromId\":\"1\",\"toId\":\"2\",\"cordinate\":{\"x1\":259,\"y1\":\"107\",\"x2\":366,\"y2\":150},\"fromDirection\":\"right\",\"toDirection\":\"right\",\"isSelected\":false},{\"id\":2,\"fromId\":\"2\",\"toId\":\"3\",\"cordinate\":{\"x1\":516,\"y1\":150,\"x2\":638,\"y2\":181},\"fromDirection\":\"right\",\"toDirection\":\"down\",\"isSelected\":false}],\"parsedData\":{\"Id\":\"untitled_template_5\",\"DataType\":\"WorkflowCore.Models.DataContext, WorkflowCore\",\"Version\":\"1\",\"Steps\":[{\"Id\":\"Activity1\",\"StepType\":\"WorkflowCore.Service.Demo.HelloWorldService, WorkflowCore\",\"NextStepId\":\"\"}]}}","status":"active","addedOn":"Wed Nov 21 2018 20:06:57 GMT+0530 (India Standard Time)","_id":"f3f6f9486eae46f3b0e22e7be89d76c6"}] -------------------------------------------------------------------------------- /server/workflow.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | var db = require('./db'); 4 | 5 | router.get('/check', (req, res) => { 6 | res.send('

Buddy ! I am running.....

'); 7 | }); 8 | 9 | router.post('/save', (req, res) => { 10 | console.log(req.body); 11 | let count = db.getworkflowCount(); 12 | let body = req.body; 13 | body.Id = count + 1; 14 | const newData = {id: count + 1, name: body.Name, 15 | jsonObject: body.JsonObject, status: 'active', addedOn: new Date().toString()} 16 | const saved = db.saveWorkflow(newData); 17 | console.log(saved); 18 | // res.status(200).send({saved: true, data: saved.id}); 19 | res.status(200).send(true); 20 | }); 21 | 22 | // function sendResponse(res) { 23 | // res.status(200).send(true); 24 | // } 25 | 26 | router.post('/run', (req, res) => { 27 | console.log(req.body); 28 | res.status(200).send(true); 29 | }); 30 | 31 | router.get('/getall', (req, res) => { 32 | let data = db.getworkflow(); 33 | res.status(200).send(data); 34 | }); 35 | 36 | router.get('/getsteptypes', (req, res) => { 37 | let data = [ { stepType: 'WorkflowCore.Service.Demo.HelloWorldService', 38 | stepName: 'HelloWorldService', 39 | assembly: 'WorkflowCore'}, 40 | { stepType: 'WorkflowCore.Service.Demo.AddNumbers', 41 | stepName: 'AddNumbers', 42 | assembly: 'WorkflowCore'}, 43 | { stepType: 'WorkflowCore.Service.Demo.PrintMessageService', 44 | stepName: 'PrintMessageService', 45 | assembly: 'WorkflowCore'}]; 46 | res.status(200).send(data); 47 | }); 48 | 49 | router.get('/DataContextAttributes', (req, res) => { 50 | let data = { 51 | Input: ['Value1', 'Value2', 'Value3', 'message'], 52 | Output: ['Value1', 'Value2', 'Value3', 'Result'] 53 | }; 54 | res.status(200).send(data); 55 | }); 56 | 57 | module.exports = router; 58 | -------------------------------------------------------------------------------- /src/app/action-bar/action-bar.component.css: -------------------------------------------------------------------------------- 1 | .list-group-item:hover { 2 | background-color: #7c7878; 3 | cursor: pointer; 4 | color: white 5 | } 6 | -------------------------------------------------------------------------------- /src/app/action-bar/action-bar.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
Actions
5 |
6 |
7 |
    8 |
  • New
  • 9 | 10 |
  • Load
  • 11 |
  • Clear
  • 12 |
13 |
14 |
15 |
16 | -------------------------------------------------------------------------------- /src/app/action-bar/action-bar.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ActionBarComponent } from './action-bar.component'; 4 | 5 | describe('ActionBarComponent', () => { 6 | let component: ActionBarComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ActionBarComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ActionBarComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/action-bar/action-bar.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Output, EventEmitter, Input } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-action-bar', 5 | templateUrl: './action-bar.component.html', 6 | styleUrls: ['./action-bar.component.css'] 7 | }) 8 | export class ActionBarComponent implements OnInit { 9 | // @Input() isEditMode: boolean; 10 | 11 | @Output() new: EventEmitter = new EventEmitter(); 12 | @Output() clear: EventEmitter = new EventEmitter(); 13 | @Output() load: EventEmitter = new EventEmitter(); 14 | 15 | constructor() { } 16 | 17 | ngOnInit() { 18 | } 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/app/activity/activity.component.css: -------------------------------------------------------------------------------- 1 | .rect:hover { 2 | fill: white; 3 | cursor: move; 4 | stroke: green; 5 | stroke-width: 2; 6 | } 7 | 8 | .rect { 9 | fill: white; 10 | stroke: #333; 11 | stroke-width: 0.75; 12 | } 13 | 14 | 15 | .rect-select { 16 | stroke: #000000; 17 | stroke-width: 1; 18 | stroke-dasharray: 8 2; 19 | animation: dash 20s linear alternate infinite; 20 | } 21 | 22 | @keyframes dash { 23 | from { 24 | stroke-dashoffset: 1000; 25 | } 26 | to { 27 | stroke-dashoffset: 0; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/app/activity/activity.component.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 18 | {{ name }} 19 | 20 | 21 | 22 | 28 | 29 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/app/activity/activity.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ActivityComponent } from './activity.component'; 4 | 5 | describe('ActivityComponent', () => { 6 | let component: ActivityComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ActivityComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ActivityComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/activity/activity.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input, Output, EventEmitter, OnChanges } from '@angular/core'; 2 | import { IDraggableControl } from '../interface/IDraggableControl'; 3 | 4 | @Component({ 5 | // tslint:disable-next-line:component-selector 6 | selector: '[app-activity]', 7 | templateUrl: './activity.component.html', 8 | styleUrls: ['./activity.component.css'] 9 | }) 10 | export class ActivityComponent implements OnInit, OnChanges, IDraggableControl { 11 | 12 | @Input() x: number; 13 | @Input() y: number; 14 | @Input() height; 15 | @Input() width; 16 | @Input() name; 17 | @Input() stepname; 18 | 19 | @Input() id: number; 20 | @Input() isSelected: boolean; 21 | @Input() nextId: number[]; 22 | 23 | 24 | @Output() select: EventEmitter = new EventEmitter(); 25 | @Output() position: EventEmitter = new EventEmitter(); 26 | 27 | isDragging = false; 28 | x_diff = 0; 29 | y_diff = 0; 30 | 31 | 32 | 33 | connectorCordinate: any; 34 | 35 | isHovered = false; 36 | 37 | constructor() { 38 | 39 | } 40 | 41 | ngOnInit() { 42 | this.connectorCordinate = {x: this.x, y: this.y}; 43 | 44 | } 45 | 46 | ngOnChanges(changes: any) { 47 | if (changes.isSelected && (changes.isSelected.currentValue !== changes.isSelected.previousvalue)) { 48 | this.isSelected = (changes.isSelected.currentValue === 'true'); 49 | } 50 | 51 | this.connectorCordinate = {x: this.x, y: this.y}; 52 | } 53 | 54 | 55 | onSelect(event: any) { 56 | event.stopPropagation(); 57 | this.isSelected = true; 58 | this.select.emit(this.id); 59 | } 60 | 61 | 62 | dragStart(event: any) { 63 | this.isDragging = true; 64 | this.x_diff = event.layerX - this.x; 65 | this.y_diff = event.layerY - this.y; 66 | } 67 | 68 | drag(event: any) { 69 | if (this.isDragging) { 70 | this.x = event.layerX - this.x_diff; 71 | this.y = event.layerY - this.y_diff; 72 | this.position.emit({x: this.x, y: this.y}); 73 | } 74 | } 75 | 76 | dragEnd() { 77 | this.isDragging = false; 78 | this.position.emit({x: this.x, y: this.y}); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/app/apiclient/api-client.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { ApiClientService } from './api-client.service'; 4 | 5 | describe('ApiClientService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [ApiClientService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject([ApiClientService], (service: ApiClientService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /src/app/apiclient/api-client.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient, HttpHeaders } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | 5 | @Injectable({ 6 | providedIn: 'root' 7 | }) 8 | export class ApiClientService { 9 | 10 | REAL_API_URL = 'http://localhost:8080/workflows'; 11 | 12 | constructor(private http: HttpClient) { 13 | } 14 | 15 | 16 | getStepTypes(): Observable { 17 | return this.http.get(this.REAL_API_URL + '/getsteptypes'); 18 | } 19 | 20 | getParamaters(): Observable { 21 | return this.http.get(this.REAL_API_URL + '/datacontextattributes'); 22 | } 23 | 24 | 25 | save(data: any): Observable { 26 | return this.http.post(this.REAL_API_URL + '/save', data); 27 | } 28 | 29 | getAll(): Observable { 30 | return this.http.get(this.REAL_API_URL + '/GetAllWorkflows'); 31 | } 32 | 33 | run(data: any): Observable { 34 | return this.http.post(this.REAL_API_URL + '/run', data, 35 | { headers: new HttpHeaders({ 'Content-Type': 'application/json'}), 36 | responseType: 'text'}); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/app/app.component.css: -------------------------------------------------------------------------------- 1 | .polygon { 2 | transition: fill .3s ease; 3 | /* cursor: pointer; */ 4 | } 5 | 6 | .static { 7 | cursor: not-allowed; 8 | } 9 | .draggable { 10 | cursor: move; 11 | } 12 | 13 | -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 | 6 | 7 | -------------------------------------------------------------------------------- /src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { AppComponent } from './app.component'; 3 | describe('AppComponent', () => { 4 | beforeEach(async(() => { 5 | TestBed.configureTestingModule({ 6 | declarations: [ 7 | AppComponent 8 | ], 9 | }).compileComponents(); 10 | })); 11 | it('should create the app', async(() => { 12 | const fixture = TestBed.createComponent(AppComponent); 13 | const app = fixture.debugElement.componentInstance; 14 | expect(app).toBeTruthy(); 15 | })); 16 | it(`should have as title 'ch-ui'`, async(() => { 17 | const fixture = TestBed.createComponent(AppComponent); 18 | const app = fixture.debugElement.componentInstance; 19 | expect(app.title).toEqual('ch-ui'); 20 | })); 21 | it('should render title in a h1 tag', async(() => { 22 | const fixture = TestBed.createComponent(AppComponent); 23 | fixture.detectChanges(); 24 | const compiled = fixture.debugElement.nativeElement; 25 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to ch-ui!'); 26 | })); 27 | }); 28 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.css'] 7 | }) 8 | export class AppComponent implements OnInit { 9 | 10 | title = 'ch-ui'; 11 | 12 | constructor() {} 13 | 14 | ngOnInit(): void { 15 | } 16 | 17 | 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | import { FormsModule } from '@angular/forms'; 4 | import { HttpClientModule } from '@angular/common/http'; 5 | import { RouterModule } from '@angular/router'; 6 | import { AppComponent } from './app.component'; 7 | import { ActivityComponent } from './activity/activity.component'; 8 | import { ConditionComponent } from './condition/condition.component'; 9 | import { StartendComponent } from './startend/startend.component'; 10 | import { NavbarComponent } from './navbar/navbar.component'; 11 | import { CodepannelComponent } from './codepannel/codepannel.component'; 12 | import { TwodimensionArrowComponent } from './twodimension-arrow/twodimension-arrow.component'; 13 | import { TemplateComponent } from './template/template.component'; 14 | import { ComponentSelectorComponent } from './component-selector/component-selector.component'; 15 | import { ControlBarComponent } from './control-bar/control-bar.component'; 16 | import { PropertyBarComponent } from './property-bar/property-bar.component'; 17 | import { LinkBarComponent } from './link-bar/link-bar.component'; 18 | import { ActionBarComponent } from './action-bar/action-bar.component'; 19 | import { UnderConstructionComponent } from './under-construction/under-construction.component'; 20 | import { ModalModule } from 'ngx-bootstrap/modal'; 21 | import { Keys } from './pipes/Keys'; 22 | import { VirtualArrowComponent } from './virtual-arrow/virtual-arrow.component'; 23 | import { SubactionComponent } from './subaction/subaction.component'; 24 | import { ConnectorComponent } from './connector/connector.component'; 25 | import { ReceiverComponent } from './receiver/receiver.component'; 26 | import { ShContextMenuModule } from 'context-menu-angular6'; 27 | 28 | const routes = [{ 29 | path: '', 30 | component: TemplateComponent 31 | }, 32 | { 33 | path: 'template', 34 | component: TemplateComponent 35 | }]; 36 | 37 | @NgModule({ 38 | declarations: [ 39 | AppComponent, 40 | ActivityComponent, 41 | ConditionComponent, 42 | StartendComponent, 43 | NavbarComponent, 44 | CodepannelComponent, 45 | TwodimensionArrowComponent, 46 | TemplateComponent, 47 | ComponentSelectorComponent, 48 | ControlBarComponent, 49 | PropertyBarComponent, 50 | LinkBarComponent, 51 | ActionBarComponent, 52 | UnderConstructionComponent, 53 | Keys, 54 | VirtualArrowComponent, 55 | SubactionComponent, 56 | ConnectorComponent, 57 | ReceiverComponent 58 | ], 59 | imports: [ 60 | BrowserModule, 61 | FormsModule, 62 | HttpClientModule, 63 | ModalModule.forRoot(), 64 | RouterModule.forRoot(routes), 65 | ShContextMenuModule 66 | ], 67 | providers: [], 68 | bootstrap: [AppComponent] 69 | }) 70 | export class AppModule { } 71 | -------------------------------------------------------------------------------- /src/app/codepannel/codepannel.component.css: -------------------------------------------------------------------------------- 1 | [data-toggle="collapse"].collapsed .if-not-collapsed { 2 | display: none; 3 | } 4 | [data-toggle="collapse"]:not(.collapsed) .if-collapsed { 5 | display: none; 6 | } 7 | -------------------------------------------------------------------------------- /src/app/codepannel/codepannel.component.html: -------------------------------------------------------------------------------- 1 |
2 |
Controls 3 | 4 | 5 | 6 | 7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | -------------------------------------------------------------------------------- /src/app/codepannel/codepannel.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { CodepannelComponent } from './codepannel.component'; 4 | 5 | describe('CodepannelComponent', () => { 6 | let component: CodepannelComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ CodepannelComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(CodepannelComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/codepannel/codepannel.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-codepannel', 5 | templateUrl: './codepannel.component.html', 6 | styleUrls: ['./codepannel.component.css'] 7 | }) 8 | export class CodepannelComponent implements OnInit { 9 | 10 | @Input() json: any; 11 | 12 | sampleJson = JSON.stringify({ 13 | 'Id': 'AddWorkflow', 14 | 'Version': 1, 15 | 'DataType': 'MyApp.MyDataClass, MyApp', 16 | 'Steps': [ 17 | { 18 | 'Id': 'Hello', 19 | 'StepType': 'MyApp.HelloWorld, MyApp', 20 | 'NextStepId': 'Add' 21 | }, 22 | { 23 | 'Id': 'Add', 24 | 'StepType': 'MyApp.AddNumbers, MyApp', 25 | 'NextStepId': 'Bye', 26 | 'Inputs': { 27 | 'Value1': 'data.Value1', 28 | 'Value2': 'data.Value2' 29 | }, 30 | 'Outputs': { 31 | 'Answer': 'step.Result' 32 | } 33 | }, 34 | { 35 | 'Id': 'Bye', 36 | 'StepType': 'MyApp.GoodbyeWorld, MyApp' 37 | } 38 | ] 39 | }, null, 2); 40 | 41 | 42 | 43 | constructor() { 44 | 45 | } 46 | 47 | ngOnInit() { 48 | this.json = JSON.stringify(this.json, null, 2); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/app/component-selector/component-selector.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saswat-pramati/workflow-ui/a95f539c1b7ea82a7e7ab5b68d6cf96773b875c0/src/app/component-selector/component-selector.component.css -------------------------------------------------------------------------------- /src/app/component-selector/component-selector.component.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | 10 | 11 | -------------------------------------------------------------------------------- /src/app/component-selector/component-selector.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ComponentSelectorComponent } from './component-selector.component'; 4 | 5 | describe('ComponentSelectorComponent', () => { 6 | let component: ComponentSelectorComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ComponentSelectorComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ComponentSelectorComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/component-selector/component-selector.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: '[app-component-selector]', 5 | templateUrl: './component-selector.component.html', 6 | styleUrls: ['./component-selector.component.css'] 7 | }) 8 | export class ComponentSelectorComponent implements OnInit { 9 | 10 | @Input() id: number; 11 | @Input() type: string; 12 | @Input() cordinate: {x: any, y: any}; 13 | @Input() isSelected: boolean; 14 | @Input() nextId: number[]; 15 | @Input() name: string; 16 | 17 | @Output() select: EventEmitter = new EventEmitter(); 18 | @Output() move: EventEmitter = new EventEmitter(); 19 | 20 | constructor() { } 21 | 22 | ngOnInit() { 23 | } 24 | 25 | 26 | onSelect(id: any) { 27 | this.select.emit(id); 28 | } 29 | 30 | onMove(cordinates: any) { 31 | this.move.emit(cordinates); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/app/condition/condition.component.css: -------------------------------------------------------------------------------- 1 | 2 | .polygon:hover { 3 | fill: white; 4 | cursor: move; 5 | stroke: green; 6 | stroke-width: 2; 7 | } 8 | 9 | .polygon { 10 | fill: white; 11 | stroke: #333; 12 | stroke-width: 0.75; 13 | } 14 | 15 | .polygon-select { 16 | stroke: #000000; 17 | stroke-width: 1; 18 | stroke-dasharray: 8 2; 19 | animation: dash 20s linear alternate infinite; 20 | } 21 | 22 | @keyframes dash { 23 | from { 24 | stroke-dashoffset: 1000; 25 | } 26 | to { 27 | stroke-dashoffset: 0; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/app/condition/condition.component.html: -------------------------------------------------------------------------------- 1 | 2 | 10 | {{name}} 19 | 20 | 27 | 34 | 35 | {{ conditionMenu.down }} 36 | {{ conditionMenu.right }} 37 | 38 | 45 | 46 | 47 | 48 | 49 |
Toggle direction?
50 |
51 |
52 | -------------------------------------------------------------------------------- /src/app/condition/condition.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ConditionComponent } from './condition.component'; 4 | 5 | describe('ConditionComponent', () => { 6 | let component: ConditionComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ConditionComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ConditionComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/condition/condition.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input, Output, EventEmitter, OnChanges, ViewChild } from '@angular/core'; 2 | import { IDraggableControl } from '../interface/IDraggableControl'; 3 | import { EventServiceService } from '../services/event-service.service'; 4 | import { ConnectionService } from '../services/connection.service'; 5 | import { UtililtyService } from '../services/utililty.service'; 6 | import { ControlType } from '../control-type/controlType'; 7 | 8 | @Component({ 9 | selector: '[app-condition]', 10 | templateUrl: './condition.component.html', 11 | styleUrls: ['./condition.component.css'] 12 | }) 13 | export class ConditionComponent implements OnInit, OnChanges, IDraggableControl { 14 | 15 | 16 | points: string; 17 | @Input() id: number; 18 | @Input() x: number; 19 | @Input() y: number; 20 | @Input() conditionName: string; 21 | @Input() isSelected: boolean; 22 | @Input() nextId: number[]; 23 | @Input() name: string; 24 | @Input() height: number; 25 | @Input() width: number; 26 | 27 | @Output() select: EventEmitter = new EventEmitter(); 28 | @Output() position: EventEmitter = new EventEmitter(); 29 | 30 | isDragging = false; 31 | x_diff = 0; 32 | y_diff = 0; 33 | 34 | connectorCordinate: any; 35 | 36 | conditionMenu = { down: 'Yes', right: 'No'}; 37 | 38 | 39 | yes_connector_cordinates: string; 40 | no_connector_cordinates: string; 41 | text_cordinates: {yes: {x: number, y: number }, no: {x: number, y: number }} = { yes: undefined, no: undefined }; 42 | 43 | isHovered = false; 44 | 45 | 46 | constructor(private eventService: EventServiceService, 47 | private connectService: ConnectionService, 48 | private utility: UtililtyService) { 49 | 50 | } 51 | 52 | ngOnChanges(changes: any): void { 53 | if (changes.isSelected && (changes.isSelected.currentValue !== changes.isSelected.previousvalue)) { 54 | this.isSelected = (changes.isSelected.currentValue === 'true'); 55 | } 56 | 57 | this.makeConnectorCordinate(this.x, this.y); 58 | this.makeTextCordinate(this.x, this.y); 59 | this.connectorCordinate = {x: this.x, y: this.y}; 60 | } 61 | 62 | 63 | ngOnInit() { 64 | this.connectorCordinate = {x: this.x, y: this.y}; 65 | this.makeConnectorCordinate(this.x, this.y); 66 | this.makeTextCordinate(this.x, this.y); 67 | } 68 | 69 | makePoints() { 70 | const _width = this.height / 2; 71 | const _height = this.height / 2; 72 | let coordinates = this.x + ' ' + (this.y - _height) + ','; 73 | coordinates += (this.x - _width) + ' ' + this.y + ','; 74 | coordinates += this.x + ' ' + (+this.y + +_height) + ','; 75 | coordinates += (+this.x + +_width) + ' ' + this.y; 76 | return coordinates; 77 | } 78 | 79 | makeConnectorCordinate(x: number, y: number) { 80 | const firstY: number = +y + +50 + +15; 81 | this.yes_connector_cordinates = x + ' ' + firstY + ','; 82 | this.yes_connector_cordinates += (x - 10) + ' ' + (firstY - 10) + ','; 83 | this.yes_connector_cordinates += (+x + +10) + ' ' + (firstY - 10); 84 | 85 | const noX: number = +x + +50 + +15; 86 | this.no_connector_cordinates = noX + ' ' + y + ','; 87 | this.no_connector_cordinates += (noX - 10) + ' ' + (y - 10) + ','; 88 | this.no_connector_cordinates += (noX - 10) + ' ' + (+y + +10); 89 | } 90 | 91 | makeTextCordinate(x: number, y: number) { 92 | const textY = +y + +60; 93 | const textX = +x + +53; 94 | this.text_cordinates.yes = {x: x - 30, y: textY}; 95 | this.text_cordinates.no = {x: textX, y: y - 15 }; 96 | } 97 | 98 | 99 | 100 | onSelect(event: any) { 101 | event.stopPropagation(); 102 | this.isSelected = true; 103 | this.select.emit(this.id); 104 | } 105 | 106 | dragStart(event: any) { 107 | this.isDragging = true; 108 | this.x_diff = event.layerX - this.x; 109 | this.y_diff = event.layerY - this.y; 110 | } 111 | 112 | drag(event: any) { 113 | if (this.isDragging) { 114 | this.x = event.layerX - this.x_diff; 115 | this.y = event.layerY - this.y_diff; 116 | this.points = this.makePoints(); 117 | this.makeConnectorCordinate(this.x, this.y); 118 | this.makeTextCordinate(this.x, this.y); 119 | this.position.emit({x: this.x, y: this.y}); 120 | } 121 | } 122 | 123 | dragEnd() { 124 | this.isDragging = false; 125 | this.position.emit({x: this.x, y: this.y}); 126 | } 127 | 128 | 129 | connectionStart(direction: string, isConditionYes: boolean, event: any) { 130 | 131 | // isConditionYes ? this.eventService.pushLinkEvent({x1: this.x, y1: +this.y + +50, fromId: this.id, 132 | // fromDirection: direction, isConditionYesId: isConditionYes}) : 133 | // this.eventService.pushLinkEvent({x1: +this.x + +50, y1: this.y, fromId: this.id, 134 | // fromDirection: direction, isConditionYesId: isConditionYes}); 135 | if (event.which === 1) { 136 | const startpoint = this.utility.getConectionStartPoint(ControlType.CONDITION, 137 | {x: this.x, y: this.y}, 138 | direction, 139 | this.height, 140 | this.width); 141 | 142 | const control = { type: ControlType.CONDITION, data: { height: this.height, width: this.width, 143 | isConnectionPointFixed: true, id: this.id, 144 | isConditionYesId: this.conditionMenu[direction] === 'Yes'}}; 145 | 146 | this.connectService.pushConnectStartPoint({ fromControl: control, 147 | fromDirection: direction, startPoint: startpoint }); 148 | } 149 | } 150 | 151 | toggleConditionDirection() { 152 | this.conditionMenu.down = this.conditionMenu.down === 'Yes' ? 'No' : 'Yes'; 153 | this.conditionMenu.right = this.conditionMenu.right === 'Yes' ? 'No' : 'Yes'; 154 | } 155 | 156 | } 157 | -------------------------------------------------------------------------------- /src/app/connector/connector.component.css: -------------------------------------------------------------------------------- 1 | .linkPointArrow { 2 | fill: #9e9a9a; 3 | cursor: pointer; 4 | } 5 | 6 | .linkPointArrow:hover { 7 | fill: black; 8 | cursor: pointer; 9 | } 10 | -------------------------------------------------------------------------------- /src/app/connector/connector.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/app/connector/connector.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ConnectorComponent } from './connector.component'; 4 | 5 | describe('ConnectorComponent', () => { 6 | let component: ConnectorComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ConnectorComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ConnectorComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/connector/connector.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input, OnChanges } from '@angular/core'; 2 | import { ControlType } from '../control-type/controlType'; 3 | import { ConnectionService } from '../services/connection.service'; 4 | import { UtililtyService } from '../services/utililty.service'; 5 | 6 | @Component({ 7 | selector: '[app-connector]', 8 | templateUrl: './connector.component.html', 9 | styleUrls: ['./connector.component.css'] 10 | }) 11 | export class ConnectorComponent implements OnInit, OnChanges { 12 | 13 | @Input() height: number; 14 | @Input() width: number; 15 | @Input() controlType: string; 16 | @Input() controlId: number; 17 | @Input() controlCordinates: any; 18 | 19 | leftPosition: string; 20 | upPosition: string; 21 | rightPosition: string; 22 | downPosition: string; 23 | 24 | constructor(private connectService: ConnectionService, 25 | private utility: UtililtyService) { } 26 | 27 | ngOnInit() { 28 | this.makeConnectorPosition(); 29 | } 30 | 31 | ngOnChanges(changes: any): void { 32 | if (changes.controlCordinates.currentValue !== changes.controlCordinates.previousValue) { 33 | this.makeConnectorPosition(); 34 | } 35 | } 36 | 37 | 38 | makeConnectorPosition() { 39 | switch (this.controlType) { 40 | case ControlType.CIRCLE: 41 | this.upPosition = this.controlCordinates.x + ' ' + (this.controlCordinates.y - 35) + ',' 42 | + (this.controlCordinates.x - 5) + ' ' + (this.controlCordinates.y - 25) + ',' 43 | + (+this.controlCordinates.x + +5) + ' ' + (this.controlCordinates.y - 25); 44 | this.leftPosition = (this.controlCordinates.x - 55) + ' ' + this.controlCordinates.y + ',' 45 | + (this.controlCordinates.x - 45) + ' ' + (this.controlCordinates.y - 5) + ',' 46 | + (this.controlCordinates.x - 45) + ' ' + (+this.controlCordinates.y + +5); 47 | this.downPosition = (this.controlCordinates.x) + ' ' + (+this.controlCordinates.y + +35) + ',' 48 | + (this.controlCordinates.x - 5) + ' ' + (+this.controlCordinates.y + +25) + ',' 49 | + (+this.controlCordinates.x + +5) + ' ' + (+this.controlCordinates.y + +25); 50 | this.rightPosition = (+this.controlCordinates.x + +55) + ' ' + (this.controlCordinates.y) + ',' 51 | + (+this.controlCordinates.x + +45) + ' ' + (this.controlCordinates.y - 5) + ',' 52 | + (+this.controlCordinates.x + +45) + ' ' + (+this.controlCordinates.y + +5); 53 | break; 54 | case ControlType.ACTIVITY: 55 | this.upPosition = (+this.controlCordinates.x + +75) + ' ' + (this.controlCordinates.y - 15) + ',' 56 | + (+this.controlCordinates.x + +70) + ' ' + (this.controlCordinates.y - 5) + ',' 57 | + (+this.controlCordinates.x + +80) + ' ' + (this.controlCordinates.y - 5); 58 | this.leftPosition = (this.controlCordinates.x - 15) + ' ' + (+this.controlCordinates.y + +25) + ',' 59 | + (this.controlCordinates.x - 5) + ' ' + (+this.controlCordinates.y + +20) + ',' 60 | + (this.controlCordinates.x - 5) + ' ' + (+this.controlCordinates.y + +30); 61 | this.downPosition = (+this.controlCordinates.x + +75) + ' ' + (+this.controlCordinates.y + +65) + ',' 62 | + (+this.controlCordinates.x + +70) + ' ' + (+this.controlCordinates.y + +55) + ',' 63 | + (+this.controlCordinates.x + +80) + ' ' + (+this.controlCordinates.y + +55); 64 | this.rightPosition = (+this.controlCordinates.x + +165) + ' ' + (+this.controlCordinates.y + +25) + ',' 65 | + (+this.controlCordinates.x + +155) + ' ' + (+this.controlCordinates.y + +20) + ',' 66 | + (+this.controlCordinates.x + +155) + ' ' + (+this.controlCordinates.y + +30); 67 | break; 68 | case ControlType.CONDITION: 69 | this.rightPosition = (+this.controlCordinates.x + +65) + ' ' + this.controlCordinates.y + ',' 70 | + (+this.controlCordinates.x + +60) + ' ' + (this.controlCordinates.y - 15) + ',' 71 | + (+this.controlCordinates.x + +60) + ' ' + (+this.controlCordinates.y + +15); 72 | this.downPosition = this.controlCordinates.x + ' ' + (+this.controlCordinates.y + +65) + ',' 73 | + (this.controlCordinates.x - 15) + ' ' + (+this.controlCordinates.y + +60) + ',' 74 | + (+this.controlCordinates.x + +15) + ' ' + (+this.controlCordinates.y + +60); 75 | break; 76 | } 77 | } 78 | 79 | startConnection(direction: string) { 80 | const startpoint = this.utility.getConectionStartPoint(this.controlType, this.controlCordinates, direction, this.height, this.width); 81 | const control = { type: this.controlType, data: { height: this.height, width: this.width, 82 | isConnectionPointFixed: false, id: this.controlId}}; 83 | this.connectService.pushConnectStartPoint({ fromControl: control, 84 | fromDirection: direction, startPoint: startpoint }); 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /src/app/control-bar/control-bar.component.css: -------------------------------------------------------------------------------- 1 | .list-group-item:hover { 2 | background-color: #7c7878; 3 | cursor: pointer; 4 | } 5 | 6 | .control { 7 | background-repeat: no-repeat; 8 | background-position: center; 9 | height: 70px; 10 | } 11 | 12 | #control-circle { 13 | background-image: url(../../assets/start.svg); 14 | } 15 | 16 | #control-activity { 17 | background-image: url(../../assets/activity.svg); 18 | } 19 | 20 | #control-condition { 21 | background-image: url(../../assets/condition.svg); 22 | } 23 | 24 | #control-arrow { 25 | background-image: url(../../assets/arrow.svg); 26 | } 27 | 28 | #control-body { 29 | padding: 0 !important 30 | } 31 | -------------------------------------------------------------------------------- /src/app/control-bar/control-bar.component.html: -------------------------------------------------------------------------------- 1 |
2 |
Toolbar
3 |
4 |
5 |
    6 |
  • 7 |
  • 8 |
  • 9 | 10 |
11 |
12 |
13 |
14 | -------------------------------------------------------------------------------- /src/app/control-bar/control-bar.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ControlBarComponent } from './control-bar.component'; 4 | 5 | describe('ControlBarComponent', () => { 6 | let component: ControlBarComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ControlBarComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ControlBarComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/control-bar/control-bar.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Output, EventEmitter } from '@angular/core'; 2 | import { ControlType } from './../control-type/controlType'; 3 | 4 | @Component({ 5 | selector: 'app-control-bar', 6 | templateUrl: './control-bar.component.html', 7 | styleUrls: ['./control-bar.component.css'] 8 | }) 9 | export class ControlBarComponent implements OnInit { 10 | 11 | @Output() select: EventEmitter = new EventEmitter(); 12 | 13 | CIRCLE = ControlType.CIRCLE; 14 | ACTIVITY = ControlType.ACTIVITY; 15 | CONDITION = ControlType.CONDITION; 16 | ARROW = ControlType.ARROW; 17 | 18 | drag_image: HTMLImageElement; 19 | 20 | constructor() { } 21 | 22 | ngOnInit() { 23 | this.drag_image = document.createElement('img'); 24 | } 25 | 26 | onClick(control: string) { 27 | this.select.emit(control); 28 | this.setDragImage(control); 29 | } 30 | 31 | drag(event: any, type: string) { 32 | event.dataTransfer.setDragImage(this.drag_image, 0, 0); 33 | event.dataTransfer.setData('type', type); 34 | } 35 | 36 | setDragImage(type: string) { 37 | switch (type) { 38 | case this.CIRCLE: 39 | this.drag_image.src = './../../assets/start.svg'; 40 | break; 41 | case this.ACTIVITY: 42 | this.drag_image.src = './../../assets/activity.svg'; 43 | break; 44 | case this.CONDITION: 45 | this.drag_image.src = './../../assets/condition.svg'; 46 | break; 47 | } 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/app/control-type/controlType.ts: -------------------------------------------------------------------------------- 1 | export enum ControlType { 2 | CIRCLE = 'circle', 3 | ACTIVITY = 'activity', 4 | CONDITION = 'condition', 5 | ARROW = 'arrow' 6 | } 7 | -------------------------------------------------------------------------------- /src/app/interface/IDraggableControl.ts: -------------------------------------------------------------------------------- 1 | export interface IDraggableControl { 2 | onSelect(event: any): void; 3 | dragStart(event: any): void; 4 | drag(event: any): void; 5 | dragEnd(): void; 6 | } 7 | -------------------------------------------------------------------------------- /src/app/link-bar/link-bar.component.css: -------------------------------------------------------------------------------- 1 | [data-toggle="collapse"].collapsed .if-not-collapsed { 2 | display: none; 3 | } 4 | [data-toggle="collapse"]:not(.collapsed) .if-collapsed { 5 | display: none; 6 | } 7 | -------------------------------------------------------------------------------- /src/app/link-bar/link-bar.component.html: -------------------------------------------------------------------------------- 1 |
2 |
Links 3 | 4 | 5 | 6 | 7 |
8 | 13 |
14 | -------------------------------------------------------------------------------- /src/app/link-bar/link-bar.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { LinkBarComponent } from './link-bar.component'; 4 | 5 | describe('LinkBarComponent', () => { 6 | let component: LinkBarComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ LinkBarComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(LinkBarComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/link-bar/link-bar.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-link-bar', 5 | templateUrl: './link-bar.component.html', 6 | styleUrls: ['./link-bar.component.css'] 7 | }) 8 | export class LinkBarComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/app/model/dataModel.ts: -------------------------------------------------------------------------------- 1 | export interface IDataModel { 2 | id: number; 3 | type: string; 4 | name?: string; 5 | dimension: { height: number, width: number}; 6 | cordinate: {x: number, y: number}; 7 | isSelected: boolean; 8 | nextId: number[]; 9 | data: IWorkflowStep; 10 | isIntial?: boolean; 11 | isMultiConnect: boolean; 12 | conditionYesId: number[]; 13 | } 14 | 15 | export interface IWorkflowStep { 16 | Id: string; 17 | StepType: string; 18 | NextStepId?: string; 19 | Inputs?: any; 20 | Outputs?: any; 21 | Do?: any[]; 22 | } 23 | 24 | export interface IWorkflow { 25 | Id: string; 26 | Version: string; 27 | DataType: string; 28 | Steps: IWorkflowStep[]; 29 | } 30 | 31 | export interface ILinkModel { 32 | id: number; 33 | fromId: number; 34 | toId: number; 35 | cordinate: {x1: number, y1: number, x2: number, y2: number}; 36 | fromDirection: string; 37 | toDirection: string; 38 | isSelected: boolean; 39 | fromControlData: IDataModel; 40 | } 41 | 42 | export interface IConnectionType { 43 | fromId: number; 44 | fromDirection: string; 45 | additionalData?: string; 46 | isConditionYesId?: boolean; 47 | x1: number; 48 | y1: number; 49 | x2?: number; 50 | y2?: number; 51 | } 52 | 53 | export interface IWorkflowRecord { 54 | // Id?: number; 55 | Name: string; 56 | JsonObject: string; 57 | // Status?: string; 58 | // AddedOn?: string; 59 | // UpdatedOn?: string; 60 | } 61 | 62 | export interface IWorkFlowReadRecords { 63 | id?: number; 64 | name: string; 65 | jsonObject: string; 66 | status?: string; 67 | addedOn?: string; 68 | updatedOn?: string; 69 | } 70 | 71 | -------------------------------------------------------------------------------- /src/app/navbar/navbar.component.css: -------------------------------------------------------------------------------- 1 | .navbar-custom-padding { 2 | padding: .2rem .5rem; 3 | background-color: #0e0e0e !important; 4 | } 5 | 6 | .alert-primary { 7 | margin-left: 10px; 8 | display: inherit; 9 | padding: .02rem 0.2rem !important; 10 | margin-bottom: 0 !important; 11 | } 12 | -------------------------------------------------------------------------------- /src/app/navbar/navbar.component.html: -------------------------------------------------------------------------------- 1 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/app/navbar/navbar.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { NavbarComponent } from './navbar.component'; 4 | 5 | describe('NavbarComponent', () => { 6 | let component: NavbarComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ NavbarComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(NavbarComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/navbar/navbar.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { EventServiceService } from './../services/event-service.service'; 3 | import { environment } from './../../environments/environment'; 4 | 5 | @Component({ 6 | selector: 'app-navbar', 7 | templateUrl: './navbar.component.html', 8 | styleUrls: ['./navbar.component.css'] 9 | }) 10 | export class NavbarComponent implements OnInit { 11 | 12 | savedOrSelectedWorkflowName = null; 13 | version: string = null; 14 | appName: string = null; 15 | 16 | constructor(private eventSerivce: EventServiceService) { 17 | this.version = environment.version; 18 | this.appName = environment.appName; 19 | } 20 | 21 | ngOnInit() { 22 | this.eventSerivce.workflowName.subscribe(name => this.savedOrSelectedWorkflowName = name ); 23 | } 24 | 25 | clickRun() { 26 | this.eventSerivce.pushRunEvent(); 27 | } 28 | 29 | clickSave() { 30 | this.eventSerivce.intimateSave(true); 31 | } 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/app/pipes/Keys.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | @Pipe({name: 'keys', pure: false}) 4 | export class Keys implements PipeTransform { 5 | transform(value: any, args?: any[]): any { 6 | const keys = []; 7 | if (Object.keys(value).length) { 8 | Object.keys(value).forEach((i, index) => keys.push({index: index + 1, key: i, value: value[i]})); 9 | } 10 | return keys; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/app/property-bar/property-bar.component.css: -------------------------------------------------------------------------------- 1 | .small-font { 2 | font-size: x-small; 3 | } 4 | 5 | i:hover { 6 | cursor: pointer; 7 | font-weight: bold; 8 | } 9 | 10 | [data-toggle="collapse"].collapsed .if-not-collapsed { 11 | display: none; 12 | } 13 | [data-toggle="collapse"]:not(.collapsed) .if-collapsed { 14 | display: none; 15 | } 16 | -------------------------------------------------------------------------------- /src/app/property-bar/property-bar.component.html: -------------------------------------------------------------------------------- 1 |
2 |
Property Inspector 3 | 4 | 5 | 6 | 7 |
8 |
9 |
{{ data && 'type: ' + data.type }} 10 | 11 | [ 12 | {{ 'no: ' + data.id + ', '}} 13 | {{ data.cordinate | json }} 14 | ] 15 | 16 |
17 | 18 |
19 |
20 |
21 |
22 | 23 | 27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | 36 | 37 |
38 |
39 | 40 | 48 |
49 | 50 |
51 |
52 | 54 | 55 |   56 | 58 |
59 |
60 |
61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 75 | 78 | 81 | 84 | 85 | 86 |
Input variableAssign variable
73 | {{ param.index }} 74 | 76 | {{ param.key}} 77 | 79 | {{ param.value }} 80 | 82 | 83 |
87 |
88 |
89 |
90 | 92 | 93 |   94 | 96 |
97 |
98 |
99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 113 | 116 | 119 | 122 | 123 | 124 |
Output variableAssign variable
111 | {{ outputparam.index }} 112 | 114 | {{ outputparam.key }} 115 | 117 | {{ outputparam.value }} 118 | 120 | 121 |
125 |
126 |
127 |
128 |
129 | 130 |
131 |
132 | 133 | 135 |
136 |
137 | 138 | 145 |
146 |
147 | 148 | 150 |
151 |
152 |
153 |
154 | 155 | 156 | 163 | 192 | 193 | 194 | 195 | 202 | 231 | 232 | -------------------------------------------------------------------------------- /src/app/property-bar/property-bar.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { PropertyBarComponent } from './property-bar.component'; 4 | 5 | describe('PropertyBarComponent', () => { 6 | let component: PropertyBarComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ PropertyBarComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(PropertyBarComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/property-bar/property-bar.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input, TemplateRef, AfterViewInit, OnChanges, Output } from '@angular/core'; 2 | import { IDataModel } from './../model/dataModel'; 3 | import { ApiClientService } from '../apiclient/api-client.service'; 4 | import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal'; 5 | 6 | @Component({ 7 | selector: 'app-property-bar', 8 | templateUrl: './property-bar.component.html', 9 | styleUrls: ['./property-bar.component.css'] 10 | }) 11 | export class PropertyBarComponent implements OnInit, AfterViewInit, OnChanges { 12 | 13 | 14 | 15 | @Input() data: IDataModel; 16 | inputCheck = false; 17 | outputCheck = false; 18 | steptypes: any; 19 | parameters: { inputs: string[], outputs: string[]} = { inputs: [], outputs: []}; 20 | inputoutputParam: { key: string, value: string} = { key: '', value: ''}; 21 | public modalRef: BsModalRef; 22 | isTextInpput = false; 23 | 24 | constructor(private apiClient: ApiClientService, private modalService: BsModalService) { } 25 | 26 | ngOnInit() { 27 | this.steptypes = this.apiClient.getStepTypes().subscribe(res => { 28 | this.steptypes = res; 29 | }, err => console.log(err)); 30 | 31 | this.apiClient.getParamaters().subscribe(res => { 32 | this.parameters.inputs = res.Input; 33 | this.parameters.outputs = res.Output; 34 | }, err => console.log(err)); 35 | } 36 | 37 | ngAfterViewInit(): void { 38 | this.inputCheck = this.data ? this.data.data.Inputs ? true : false : false; 39 | } 40 | 41 | ngOnChanges(changes: any): void { 42 | if (changes.data.currentValue !== changes.data.previousValue) { 43 | this.inputCheck = this.data ? this.data.data.Inputs ? true : false : false; 44 | } 45 | if (changes.data.currentValue !== changes.data.previousValue) { 46 | this.outputCheck = this.data ? this.data.data.Outputs ? true : false : false; 47 | } 48 | } 49 | 50 | onStepTypeChange(stepType: any) { 51 | this.data.data.StepType = stepType + ', ' + 'WorkflowCore'; 52 | } 53 | 54 | parseStepType(val: string) { 55 | return val.split(',')[0]; 56 | } 57 | 58 | onInputCheckedChange(val: any) { 59 | this.inputCheck = val; 60 | if (!this.inputCheck) { 61 | delete this.data.data.Inputs; 62 | } 63 | } 64 | 65 | onOutputCheckedChange(val: any) { 66 | this.outputCheck = val; 67 | if (!this.outputCheck) { 68 | delete this.data.data.Outputs; 69 | } 70 | } 71 | 72 | onTextCheckChanged(isChecked: boolean) { 73 | this.isTextInpput = isChecked; 74 | } 75 | 76 | addOutput() { 77 | this.data.data.Outputs['Value2'] = 'step.Result'; 78 | } 79 | 80 | deleteInputKey(key: string) { 81 | delete this.data.data.Inputs[key]; 82 | if (Object.keys(this.data.data.Inputs).length === 0) { 83 | this.onInputCheckedChange(false); 84 | } 85 | } 86 | 87 | deleteOutputKey(key: string) { 88 | delete this.data.data.Outputs[key]; 89 | if (Object.keys(this.data.data.Outputs).length === 0) { 90 | this.onOutputCheckedChange(false); 91 | } 92 | } 93 | 94 | 95 | openInputModal(template: TemplateRef) { 96 | this.modalRef = this.modalService.show(template); 97 | } 98 | 99 | addInputParam() { 100 | if (this.inputoutputParam.key && this.inputoutputParam.value) { 101 | if (this.validateParamaters(this.inputoutputParam)) { 102 | if (!this.data.data.Inputs) { 103 | this.data.data['Inputs'] = {}; 104 | } 105 | this.data.data.Inputs[this.inputoutputParam.key] = this.isTextInpput ? 106 | this.inputoutputParam.value : 'data.' + this.inputoutputParam.value; 107 | this.inputoutputParam = { key: '', value: ''}; 108 | this.modalRef.hide(); 109 | } else { 110 | alert(this.inputoutputParam.key + ' or ' + this.inputoutputParam.value + ' already used!'); 111 | } 112 | } 113 | } 114 | 115 | addOutputParam() { 116 | if (this.inputoutputParam.key && this.inputoutputParam.value) { 117 | if (this.validateParamaters(this.inputoutputParam)) { 118 | if (!this.data.data.Outputs) { 119 | this.data.data['Outputs'] = {}; 120 | } 121 | this.data.data.Outputs[this.inputoutputParam.key] = this.isTextInpput ? 122 | this.inputoutputParam.value : 'step.' + this.inputoutputParam.value; 123 | this.inputoutputParam = { key: '', value: ''}; 124 | this.modalRef.hide(); 125 | } else { 126 | alert(this.inputoutputParam.key + ' or ' + this.inputoutputParam.value + ' already used!'); 127 | } 128 | } 129 | } 130 | 131 | checkIfInputOutputEmpty(obj: any): boolean { 132 | return obj ? Object.keys(obj).length > 0 : false; 133 | } 134 | 135 | validateParamaters(newParam: any): boolean { 136 | const keyValueArr = new Set(); 137 | if (this.data.data.Inputs) { 138 | Object.keys(this.data.data.Inputs).forEach((key) => { 139 | keyValueArr.add(key); 140 | keyValueArr.add(this.data.data.Inputs[key]); 141 | }); 142 | 143 | } else if (this.data.data.Outputs) { 144 | Object.keys(this.data.data.Outputs).forEach((key) => { 145 | keyValueArr.add(key); 146 | keyValueArr.add(this.data.data.Outputs[key]); 147 | }); 148 | } 149 | 150 | return !(keyValueArr.has(newParam.key) || keyValueArr.has(newParam.value)); 151 | 152 | } 153 | 154 | } 155 | -------------------------------------------------------------------------------- /src/app/receiver/receiver.component.css: -------------------------------------------------------------------------------- 1 | /* .receiver-dot { 2 | fill: #9e9a9a; 3 | cursor: pointer; 4 | } 5 | 6 | .receiver-dot:hover { 7 | fill: black; 8 | cursor: pointer; 9 | 10 | } */ 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/app/receiver/receiver.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/app/receiver/receiver.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ReceiverComponent } from './receiver.component'; 4 | 5 | describe('ReceiverComponent', () => { 6 | let component: ReceiverComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ReceiverComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ReceiverComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/receiver/receiver.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input, OnChanges } from '@angular/core'; 2 | import { ControlType } from '../control-type/controlType'; 3 | import { ConnectionService } from '../services/connection.service'; 4 | import { UtililtyService } from '../services/utililty.service'; 5 | 6 | @Component({ 7 | selector: '[app-receiver]', 8 | templateUrl: './receiver.component.html', 9 | styleUrls: ['./receiver.component.css'] 10 | }) 11 | 12 | export class ReceiverComponent implements OnInit, OnChanges { 13 | 14 | @Input() controlType: string; 15 | @Input() controlId: number; 16 | @Input() controlCordinates: any; 17 | @Input() height: number; 18 | @Input() width: number; 19 | 20 | 21 | cordinate: any = { left: {x: 0, y: 0}, up: {x: 0, y: 0}, right: {x: 0, y: 0}, down: {x: 0, y: 0} }; 22 | 23 | constructor(private connectionService: ConnectionService, private utility: UtililtyService) { 24 | } 25 | 26 | ngOnInit() { 27 | this.makeCordinates(); 28 | } 29 | 30 | ngOnChanges(changes: any): void { 31 | if (changes.controlCordinates.currentValue !== changes.controlCordinates.previousValue) { 32 | this.makeCordinates(); 33 | } 34 | } 35 | 36 | makeCordinates() { 37 | this.cordinate.up = this.utility.getUpConnection(this.controlType, this.controlCordinates, this.height, this.width); 38 | this.cordinate.left = this.utility.getLeftConnection(this.controlType, this.controlCordinates, this.height, this.width); 39 | if (this.controlType !== ControlType.CONDITION) { 40 | this.cordinate.right = this.utility.getRightConnection(this.controlType, this.controlCordinates, this.height, this.width); 41 | this.cordinate.down = this.utility.getDownConnection(this.controlType, this.controlCordinates, this.height, this.width); 42 | } 43 | 44 | } 45 | 46 | 47 | connectionArrive(cordinates, direction) { 48 | const data = { control: this.controlType, endPoint: cordinates, id: this.controlId, direction: direction }; 49 | this.connectionService.pushReceiverPoint(data); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/app/services/connection.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { ConnectionService } from './connection.service'; 4 | 5 | describe('ConnectionService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [ConnectionService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject([ConnectionService], (service: ConnectionService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /src/app/services/connection.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Subject } from 'rxjs'; 3 | 4 | @Injectable({ 5 | providedIn: 'root' 6 | }) 7 | export class ConnectionService { 8 | 9 | connect: Subject = new Subject(); 10 | receive: Subject = new Subject(); 11 | 12 | constructor() { } 13 | 14 | pushConnectStartPoint(startPoint: any) { 15 | this.connect.next(startPoint); 16 | } 17 | 18 | pushReceiverPoint(endPoint: any) { 19 | this.receive.next(endPoint); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/app/services/event-service.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { EventServiceService } from './event-service.service'; 4 | 5 | describe('EventServiceService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [EventServiceService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject([EventServiceService], (service: EventServiceService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /src/app/services/event-service.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Subject } from 'rxjs'; 3 | import { IConnectionType } from '../model/dataModel'; 4 | 5 | @Injectable({ 6 | providedIn: 'root' 7 | }) 8 | export class EventServiceService { 9 | 10 | runEvent: Subject = new Subject(); 11 | workflowName: Subject = new Subject(); 12 | // linkEvent: Subject = new Subject(); 13 | intimateClickSave: Subject = new Subject(); 14 | 15 | constructor() { } 16 | 17 | pushRunEvent() { 18 | this.runEvent.next(true); 19 | } 20 | 21 | pushSaveEvent(name: string) { 22 | this.workflowName.next(name); 23 | } 24 | 25 | // pushLinkEvent(event: IConnectionType) { 26 | // this.linkEvent.next(event); 27 | // } 28 | 29 | intimateSave(event: boolean) { 30 | this.intimateClickSave.next(event); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/app/services/state.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { StateService } from './state.service'; 4 | 5 | describe('StateService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [StateService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject([StateService], (service: StateService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /src/app/services/state.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { IDataModel, ILinkModel } from './../model/dataModel'; 3 | 4 | @Injectable({ 5 | providedIn: 'root' 6 | }) 7 | export class StateService { 8 | 9 | activityListState = []; 10 | activityListPoppedState = []; 11 | 12 | linkListState = []; 13 | linkListPoppedState = []; 14 | 15 | constructor() { } 16 | 17 | saveState(activity: IDataModel[], linkList: ILinkModel[]) { 18 | // tslint:disable-next-line:no-unused-expression 19 | activity.length && this.activityListState.push(activity); 20 | // tslint:disable-next-line:no-unused-expression 21 | linkList.length && this.linkListState.push(linkList); 22 | } 23 | 24 | clearState() { 25 | this.activityListState = []; 26 | this.activityListPoppedState = []; 27 | 28 | this.linkListState = []; 29 | this.linkListPoppedState = []; 30 | } 31 | 32 | unDo() { 33 | // tslint:disable-next-line:no-unused-expression 34 | this.activityListState.length && this.activityListPoppedState.push(this.activityListState.pop()); 35 | // tslint:disable-next-line:no-unused-expression 36 | this.linkListState && this.linkListPoppedState.push(this.linkListState.pop()); 37 | } 38 | 39 | reDo() { 40 | // tslint:disable-next-line:no-unused-expression 41 | this.activityListPoppedState.length && this.activityListState.push(this.activityListPoppedState.pop()); 42 | // tslint:disable-next-line:no-unused-expression 43 | this.linkListPoppedState.length && this.linkListState.push(this.linkListPoppedState.pop()); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/app/services/svg-arrowUtility.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { SvgArrowUtilityService } from './svg-arrowUtility.service'; 4 | 5 | describe('SvgArrowutilityService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [SvgArrowUtilityService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject([SvgArrowUtilityService], (service: SvgArrowUtilityService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /src/app/services/svg-arrowUtility.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { ControlType } from '../control-type/controlType'; 3 | import { IDataModel } from './../model/dataModel'; 4 | import { UtililtyService } from './utililty.service'; 5 | 6 | @Injectable({ 7 | providedIn: 'root' 8 | }) 9 | export class SvgArrowUtilityService { 10 | 11 | constructor(private utility: UtililtyService) { } 12 | 13 | public CalculateArrowPoint(x: number, y: number, direction: string, arrowWidth: number): string { 14 | let points = ''; 15 | points = x + ' ' + y + ','; 16 | 17 | if (direction === 'down') { 18 | points += (x - arrowWidth) + ' ' + (y - arrowWidth) + ','; 19 | points += (+x + +arrowWidth) + ' ' + (y - arrowWidth); 20 | } else if (direction === 'right') { 21 | points += (x - arrowWidth) + ' ' + (y - arrowWidth) + ','; 22 | points += (x - arrowWidth) + ' ' + (+y + +arrowWidth); 23 | } else if (direction === 'left') { 24 | points += (+x + +arrowWidth) + ' ' + (y - arrowWidth) + ','; 25 | points += (+x + +arrowWidth) + ' ' + (+y + +arrowWidth); 26 | } else if (direction === 'up') { 27 | points += (x - arrowWidth) + ' ' + (+y + +arrowWidth) + ','; 28 | points += (+x + +arrowWidth) + ' ' + (+y + +arrowWidth); 29 | } 30 | return points; 31 | } 32 | 33 | public CalculateArrowFromPoint(x: number, y: number, controlType: string, 34 | controlDimension: {height: number, width: number}, 35 | direction: string, linkCordinate: any): {x: number, y: number} { 36 | if (direction === 'left') { 37 | return this.utility.getLeftConnection(controlType, {x: x, y: y}, controlDimension.height, controlDimension.width); 38 | } else if (direction === 'right') { 39 | return this.utility.getRightConnection(controlType, {x: x, y: y}, controlDimension.height, controlDimension.width); 40 | } else if (direction === 'down') { 41 | return this.utility.getDownConnection(controlType, {x: x, y: y}, controlDimension.height, controlDimension.width); 42 | } else if (direction === 'up') { 43 | return this.utility.getUpConnection(controlType, {x: x, y: y}, controlDimension.height, controlDimension.width); 44 | } 45 | } 46 | 47 | public CalculateArrowToPoint(x: number, y: number, controlType: string, controlDimension: {height: number, width: number}, 48 | toDirection: string, linkCordinate: any): {x: number, y: number} { 49 | 50 | if (toDirection === 'right') { 51 | return this.utility.getLeftConnection(controlType, {x: x, y: y}, controlDimension.height, controlDimension.width); 52 | } else if (toDirection === 'left') { 53 | return this.utility.getRightConnection(controlType, {x: x, y: y}, controlDimension.height, controlDimension.width); 54 | } else if (toDirection === 'up') { 55 | return this.utility.getDownConnection(controlType, {x: x, y: y}, controlDimension.height, controlDimension.width); 56 | } else if (toDirection === 'down') { 57 | return this.utility.getUpConnection(controlType, {x: x, y: y}, controlDimension.height, controlDimension.width); 58 | } 59 | } 60 | 61 | public CalculatePolyLinePoints(x1: number, y1: number, x2: number, y2: number): string { 62 | let points = x1 + ' ' + y1 + ','; 63 | 64 | if (x2 !== x1 && y2 > y1) { 65 | const ym = (+y1 + +y2) / 2; 66 | points += x1 + ' ' + ym + ','; 67 | points += x2 + ' ' + ym + ','; 68 | } else if (x2 !== x1 && y2 < y1) { 69 | const xm = Math.ceil((+x1 + +x2) / 2); 70 | points += x1 + ' ' + (+y1 + +20) + ','; 71 | points += xm + ' ' + (+y1 + +20) + ','; 72 | points += xm + ' ' + (y2 - 20) + ','; 73 | points += x2 + ' ' + (y2 - 20) + ','; 74 | } 75 | 76 | return points + x2 + ' ' + y2; 77 | } 78 | 79 | public CalculateArrowLines(x1: number, y1: number, x2: number, y2: number, fromDir: string, 80 | toDir: string, fromControlRange: any, toControlRange: any): string { 81 | let points = x1 + ' ' + y1 + ','; 82 | 83 | switch (fromDir) { 84 | case 'right': 85 | if (toDir === 'right') { 86 | points += (+x1 + +x2) / 2 + ' ' + y1 + ','; 87 | points += (+x1 + +x2) / 2 + ' ' + y2 + ','; 88 | } else if (toDir === 'down' || toDir === 'up') { 89 | points += x2 + ' ' + y1 + ','; 90 | } 91 | break; 92 | case 'down': 93 | if (toDir === 'down') { 94 | points += x1 + ' ' + (+y1 + +y2) / 2 + ','; 95 | points += x2 + ' ' + (+y1 + +y2) / 2 + ','; 96 | } else if (toDir === 'left' || toDir === 'right') { 97 | points += x1 + ' ' + y2 + ','; 98 | } 99 | break; 100 | case 'left': 101 | if (toDir === 'left') { 102 | points += (+x1 + +x2) / 2 + ' ' + y1 + ','; 103 | points += (+x1 + +x2) / 2 + ' ' + y2 + ','; 104 | } else if (toDir === 'down' || toDir === 'up') { 105 | points += x2 + ' ' + y1 + ','; 106 | } 107 | break; 108 | case 'up': 109 | if (toDir === 'up') { 110 | points += x1 + ' ' + (+y1 + +y2) / 2 + ','; 111 | points += x2 + ' ' + (+y1 + +y2) / 2 + ','; 112 | } else if (toDir === 'left' || toDir === 'right') { 113 | points += x1 + ' ' + y2 + ','; 114 | } 115 | break; 116 | } 117 | 118 | return points + x2 + ' ' + y2; 119 | } 120 | 121 | public drawConnection(fromControl: any, fromDirection: string, 122 | x1: number, y1: number, x2: number, y2: number): string { 123 | let points = x1 + ' ' + y1 + ','; 124 | 125 | switch (fromControl.type) { 126 | case ControlType.CIRCLE: 127 | switch (fromDirection) { 128 | case 'left': 129 | if (y2 !== y1) { 130 | points += x2 + ' ' + y1 + ','; 131 | } 132 | break; 133 | case 'right': 134 | if (y2 !== y1) { 135 | points += x2 + ' ' + y1 + ','; 136 | } 137 | break; 138 | case 'up': 139 | if (y2 !== y1) { 140 | points += x1 + ' ' + y2 + ','; 141 | } 142 | break; 143 | case 'down': 144 | if (x2 !== x1) { 145 | points += x1 + ' ' + (+y2 + +y1) / 2 + ','; 146 | points += x2 + ' ' + (+y2 + +y1) / 2 + ','; 147 | } 148 | break; 149 | } 150 | break; 151 | case ControlType.ACTIVITY: 152 | switch (fromDirection) { 153 | case 'left': 154 | if (y2 !== y1) { 155 | points += x2 + ' ' + y1 + ','; 156 | } 157 | break; 158 | case 'right': 159 | if (y2 !== y1) { 160 | points += x2 + ' ' + y1 + ','; 161 | } 162 | break; 163 | case 'up': 164 | if (y2 !== y1) { 165 | points += x1 + ' ' + (+y2 + +y1) / 2 + ','; 166 | points += x2 + ' ' + (+y2 + +y1) / 2 + ','; 167 | } 168 | break; 169 | case 'down': 170 | if (x2 !== x1) { 171 | // points += x1 + ' ' + y2 + ','; 172 | points += x1 + ' ' + (+y2 + +y1) / 2 + ','; 173 | points += x2 + ' ' + (+y2 + +y1) / 2 + ','; 174 | } 175 | break; 176 | } 177 | break; 178 | } 179 | 180 | return points + x2 + ' ' + y2; 181 | } 182 | 183 | calculateStartPointBasedOnDrag(mouseX: number, mouseY: number, prevX: number, prevY: number, direction: string, controlData: any): 184 | {nextX: number, nextY: number, direction: string} { 185 | if (!controlData.isConnectionPointFixed) { 186 | const breakWidth = controlData.width / 2; 187 | const breakHeight = controlData.height / 2; 188 | 189 | if (direction === 'down') { 190 | if (mouseY > prevY) { 191 | if (mouseX > +prevX + +breakWidth) { 192 | return { nextX: +prevX + +breakWidth, nextY: prevY - breakHeight, direction: 'right'}; 193 | } else if (mouseX < prevX - breakWidth) { 194 | return { nextX: prevX - breakWidth, nextY: prevY - breakHeight, direction: 'left'}; 195 | } 196 | } else if (mouseY < prevY - controlData.height) { 197 | return { nextX: prevX , nextY: prevY - controlData.height, direction: 'up'}; 198 | } 199 | } else if (direction === 'left') { 200 | if (mouseX > prevX) { 201 | if (mouseY > prevY) { 202 | return { nextX: +prevX + +breakWidth, nextY: +prevY + +breakHeight, direction: 'down'}; 203 | } else { 204 | return { nextX: +prevX + +breakWidth, nextY: prevY - breakHeight, direction: 'up'}; 205 | } 206 | } 207 | 208 | } else if (direction === 'right') { 209 | if (mouseX < prevX) { 210 | if (mouseY > prevY) { 211 | return { nextX: prevX - breakWidth, nextY: +prevY + +breakHeight, direction: 'down'}; 212 | } else { 213 | return { nextX: prevX - breakWidth, nextY: prevY - breakHeight, direction: 'up'}; 214 | } 215 | } 216 | } else if (direction === 'up') { 217 | if (mouseY < prevY) { 218 | if (mouseX > +prevX + +breakWidth) { 219 | return { nextX: +prevX + +breakWidth, nextY: +prevY + +breakHeight, direction: 'right'}; 220 | } else if (mouseX < prevX - breakWidth) { 221 | return { nextX: prevX - breakWidth, nextY: +prevY + +breakHeight, direction: 'left'}; 222 | } 223 | } else if (mouseY < +prevY + +controlData.height) { 224 | return { nextX: prevX , nextY: +prevY + +controlData.height, direction: 'down'}; 225 | } 226 | } 227 | } 228 | return null; 229 | } 230 | 231 | } 232 | -------------------------------------------------------------------------------- /src/app/services/utililty.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { UtililtyService } from './utililty.service'; 4 | 5 | describe('UtililtyService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [UtililtyService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject([UtililtyService], (service: UtililtyService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /src/app/services/utililty.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { ControlType } from '../control-type/controlType'; 3 | import { IWorkflowStep } from '../model/dataModel'; 4 | import { IDataModel, IWorkflow } from './../model/dataModel'; 5 | 6 | @Injectable({ 7 | providedIn: 'root' 8 | }) 9 | export class UtililtyService { 10 | 11 | REQUIRED_STEP_TYPES = [ControlType.ACTIVITY.toString(), ControlType.CONDITION.toString()]; 12 | START = 'Start'; 13 | END = 'End'; 14 | 15 | constructor() { } 16 | 17 | removeFromArray(arr: number[], item: number) { 18 | // tslint:disable-next-line:triple-equals 19 | arr.splice(arr.findIndex(i => i == item), 1); 20 | return arr; 21 | } 22 | 23 | makeDefaultWorkflowStep(type: string, activityList: IDataModel[]): IWorkflowStep { 24 | if (type === ControlType.ACTIVITY) { 25 | return {Id: 'Activity' + (+activityList.filter(f => f.type === ControlType.ACTIVITY).length + +1), StepType: '', NextStepId: ''}; 26 | } else if (type === ControlType.CIRCLE) { 27 | return {Id: this.START, StepType: this.START, NextStepId: ''}; 28 | } else if (type === ControlType.CONDITION) { 29 | return {Id: 'Condition' + (+activityList.filter(f => f.type === ControlType.CONDITION).length + +1), 30 | StepType: '', 31 | NextStepId: '', Inputs: { Condition : ''} , Do: []}; 32 | } 33 | } 34 | 35 | makeJsonForWorkflowEngine(activityList: IDataModel[], id: string, dataType: string): IWorkflow { 36 | const workflowJSON: IWorkflow = { Id: id, DataType: dataType, Version: '1', Steps: [] }; 37 | 38 | // find start step by filterring 'Start' 39 | const nextStepId: number[] = activityList.find(a => a.data.StepType === this.START).nextId; 40 | 41 | // continue finding till last step 42 | const parsedSteps: IWorkflowStep[] = this.iterateActivityList(nextStepId, activityList); 43 | 44 | workflowJSON.Steps = (parsedSteps); 45 | 46 | return workflowJSON; 47 | } 48 | 49 | 50 | getStepName(activityList: IDataModel[], nextId: number[]): string { 51 | if (nextId.length) { 52 | // tslint:disable-next-line:triple-equals 53 | const step = activityList.find(f => f.id == nextId[0]); 54 | return step && this.REQUIRED_STEP_TYPES.includes(step.type) ? step.data.Id : ''; 55 | } 56 | return ''; 57 | } 58 | 59 | 60 | iterateActivityList(nextStepId: number[], activityList: IDataModel[]): IWorkflowStep[] { 61 | const workflowSteps: IWorkflowStep[] = []; 62 | while (nextStepId.length) { 63 | // tslint:disable-next-line:triple-equals 64 | const step = activityList.find(a => a.id == nextStepId[0]); 65 | if (this.REQUIRED_STEP_TYPES.includes(step.type)) { 66 | if (step.type === ControlType.ACTIVITY) { 67 | step.data.NextStepId = this.getStepName(activityList, step.nextId); 68 | step.isSelected = false; 69 | workflowSteps.push(step.data); 70 | 71 | nextStepId = step.nextId; 72 | } else if (step.type === ControlType.CONDITION) { 73 | // tslint:disable-next-line:triple-equals 74 | step.data.NextStepId = this.getStepName(activityList, step.nextId.filter(f => f != step.conditionYesId[0])); 75 | step.isSelected = false; 76 | // tslint:disable-next-line:triple-equals 77 | step.data.Do.push(this.iterateActivityList(step.conditionYesId, activityList)); 78 | workflowSteps.push(step.data); 79 | // assign the nextId to else condition 80 | nextStepId = step.nextId.filter(f => f !== step.conditionYesId[0]); 81 | } 82 | } else { 83 | nextStepId = []; 84 | } 85 | } 86 | return workflowSteps; 87 | } 88 | 89 | refreshConditionDoList(activityList: IDataModel[]): IDataModel[] { 90 | activityList.forEach(val => { 91 | if (val.type === ControlType.CONDITION) { 92 | val.data.Do = []; 93 | } 94 | }); 95 | return activityList; 96 | } 97 | 98 | validateWorkflow(activityList: IDataModel[]): string { 99 | let errorMessage = ''; 100 | if (activityList.filter(f => f.data.Id === this.START).length > 1) { 101 | return 'More than one Start control can not present !'; 102 | } else if (activityList.filter(f => f.data.Id === this.END).length === 0) { 103 | return 'End control is not available !'; 104 | } 105 | activityList.forEach(f => { 106 | if (f.type === ControlType.ACTIVITY && !f.data.StepType) { 107 | errorMessage = 'No property set to control ' + f.data.Id; 108 | return; 109 | } 110 | }); 111 | if (!errorMessage) { 112 | activityList.forEach(f => { 113 | if (f.type === ControlType.CONDITION) { 114 | if (!f.data.StepType) { 115 | errorMessage = 'No StepType set to control ' + f.data.Id; 116 | return; 117 | } else if (f.data.Inputs && !f.data.Inputs.Condition) { 118 | errorMessage = 'No condition expression set to control ' + f.data.Id; 119 | return; 120 | } else if (!f.conditionYesId.length || !f.nextId.length) { 121 | errorMessage = 'Either link Yes/No of control ' + f.data.Id; 122 | return; 123 | } 124 | } 125 | }); 126 | } 127 | return errorMessage; 128 | } 129 | 130 | collectInputParams(activityList: IDataModel[]): Set { 131 | const inputList = new Set(); 132 | activityList.forEach(a => { 133 | if (a.type === ControlType.ACTIVITY && a.data.Inputs && Object.keys(a.data.Inputs).length > 0) { 134 | Object.keys(a.data.Inputs).forEach(key => { 135 | if (key !== 'message') { 136 | inputList.add({ key: key, value: ''}); 137 | } 138 | }); 139 | } 140 | }); 141 | return inputList; 142 | } 143 | 144 | getConectionStartPoint(control: string, cordinates: any, fromDirection: string, 145 | height: number, width: number): {x: number, y: number} { 146 | switch (fromDirection) { 147 | case 'left': 148 | return this.getLeftConnection(control, cordinates, height, width); 149 | case 'right': 150 | return this.getRightConnection(control, cordinates, height, width); 151 | case 'up': 152 | return this.getUpConnection(control, cordinates, height, width); 153 | case 'down': 154 | return this.getDownConnection(control, cordinates, height, width); 155 | } 156 | } 157 | 158 | getHeightAndWidthOfControl(type: string): { height: number, width: number} { 159 | const control = function (height, width) { 160 | return { height: height, width: width}; 161 | }; 162 | let data = null; 163 | switch (type) { 164 | case ControlType.ACTIVITY: 165 | data = control(50, 150); 166 | break; 167 | case ControlType.CIRCLE: 168 | data = control(40, 80); 169 | break; 170 | case ControlType.CONDITION: 171 | data = control(100, 100); 172 | break; 173 | } 174 | 175 | return data; 176 | } 177 | 178 | getLeftConnection(controlType: string, controlCordinates: {x: number, y: number}, height: number, width: number) 179 | : {x: number, y: number} { 180 | const cordinate = {x: 0, y: 0}; 181 | switch (controlType) { 182 | case ControlType.ACTIVITY: 183 | cordinate.x = +controlCordinates.x; 184 | cordinate.y = +controlCordinates.y + +(height / 2); 185 | break; 186 | case ControlType.CIRCLE: 187 | cordinate.x = +controlCordinates.x - (width / 2); 188 | cordinate.y = controlCordinates.y ; 189 | break; 190 | case ControlType.CONDITION: 191 | cordinate.x = +controlCordinates.x - (width / 2); 192 | cordinate.y = controlCordinates.y ; 193 | break; 194 | } 195 | return cordinate; 196 | } 197 | 198 | getRightConnection(controlType: string, controlCordinates: {x: number, y: number}, height: number, width: number) 199 | : {x: number, y: number} { 200 | const cordinate = {x: 0, y: 0}; 201 | switch (controlType) { 202 | case ControlType.ACTIVITY: 203 | cordinate.x = +controlCordinates.x + +width; 204 | cordinate.y = +controlCordinates.y + +(height / 2); 205 | break; 206 | case ControlType.CIRCLE: 207 | cordinate.x = +controlCordinates.x + + (width / 2); 208 | cordinate.y = controlCordinates.y; 209 | break; 210 | case ControlType.CONDITION: 211 | cordinate.x = +controlCordinates.x + +(width / 2); 212 | cordinate.y = controlCordinates.y; 213 | break; 214 | } 215 | return cordinate; 216 | } 217 | 218 | getDownConnection(controlType: string, controlCordinates: {x: number, y: number}, height: number, width: number) 219 | : {x: number, y: number} { 220 | const cordinate = {x: 0, y: 0}; 221 | switch (controlType) { 222 | case ControlType.ACTIVITY: 223 | cordinate.x = +controlCordinates.x + +(width / 2); 224 | cordinate.y = +controlCordinates.y + +height; 225 | break; 226 | case ControlType.CIRCLE: 227 | cordinate.x = controlCordinates.x; 228 | cordinate.y = +controlCordinates.y + + (height / 2); 229 | break; 230 | case ControlType.CONDITION: 231 | cordinate.x = +controlCordinates.x; 232 | cordinate.y = +controlCordinates.y + +(height / 2); 233 | break; 234 | } 235 | return cordinate; 236 | } 237 | 238 | getUpConnection(controlType: string, controlCordinates: {x: number, y: number}, height: number, width: number) 239 | : {x: number, y: number} { 240 | const cordinate = {x: 0, y: 0}; 241 | switch (controlType) { 242 | case ControlType.ACTIVITY: 243 | cordinate.x = +controlCordinates.x + + (width / 2); 244 | cordinate.y = +controlCordinates.y; 245 | break; 246 | case ControlType.CIRCLE: 247 | cordinate.x = controlCordinates.x; 248 | cordinate.y = controlCordinates.y - (height / 2); 249 | break; 250 | case ControlType.CONDITION: 251 | cordinate.x = controlCordinates.x; 252 | cordinate.y = controlCordinates.y - (height / 2); 253 | break; 254 | } 255 | return cordinate; 256 | } 257 | 258 | 259 | } 260 | -------------------------------------------------------------------------------- /src/app/startend/startend.component.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | .ellipse:hover { 5 | fill: white; 6 | cursor: move; 7 | stroke: green; 8 | stroke-width: 2; 9 | } 10 | 11 | .ellipse { 12 | fill: white; 13 | stroke: #333; 14 | stroke-width: 0.75; 15 | } 16 | 17 | .ellipse-select { 18 | stroke: #000000; 19 | stroke-width: 1; 20 | stroke-dasharray: 8 2; 21 | animation: dash 20s linear alternate infinite; 22 | } 23 | 24 | @keyframes dash { 25 | from { 26 | stroke-dashoffset: 1000; 27 | } 28 | to { 29 | stroke-dashoffset: 0; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/app/startend/startend.component.html: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | {{name}} 23 | 24 | 28 | 29 | 36 | 37 | -------------------------------------------------------------------------------- /src/app/startend/startend.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { StartendComponent } from './startend.component'; 4 | 5 | describe('StartendComponent', () => { 6 | let component: StartendComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ StartendComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(StartendComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/startend/startend.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input, Output, EventEmitter, OnChanges } from '@angular/core'; 2 | import { IDraggableControl } from '../interface/IDraggableControl'; 3 | 4 | 5 | @Component({ 6 | selector: '[app-startend]', 7 | templateUrl: './startend.component.html', 8 | styleUrls: ['./startend.component.css'] 9 | }) 10 | export class StartendComponent implements OnInit, OnChanges, IDraggableControl { 11 | 12 | @Input() id: number; 13 | @Input() x: number; 14 | @Input() y: number; 15 | @Input() rx: number; 16 | @Input() ry: number; 17 | @Input() text; 18 | @Input() isSelected: boolean; 19 | @Input() nextId: number[]; 20 | @Input() name: string; 21 | @Input() height: number; 22 | @Input() width: number; 23 | 24 | @Output() select: EventEmitter = new EventEmitter(); 25 | @Output() position: EventEmitter = new EventEmitter(); 26 | 27 | 28 | connector_cordinates: string; 29 | 30 | selectX: number; 31 | selectY: number; 32 | isDragging = false; 33 | 34 | 35 | connectorCordinate: any; 36 | 37 | isHovered = false; 38 | 39 | x_diff = 0; 40 | y_diff = 0; 41 | 42 | constructor() { 43 | } 44 | 45 | ngOnInit() { 46 | this.connectorCordinate = {x: this.x, y: this.y}; 47 | 48 | } 49 | 50 | ngOnChanges(changes: any) { 51 | if (changes.isSelected && (changes.isSelected.currentValue !== changes.isSelected.previousvalue)) { 52 | this.isSelected = (changes.isSelected.currentValue === 'true'); 53 | } 54 | if (changes.nextId && (changes.nextId.currentValue !== changes.nextId.previousValue)) { 55 | this.nextId = changes.nextId.currentValue; 56 | } 57 | this.connectorCordinate = {x: this.x, y: this.y}; 58 | } 59 | 60 | 61 | onSelect(event: any) { 62 | event.stopPropagation(); 63 | this.isSelected = true; 64 | this.select.emit(this.id); 65 | } 66 | 67 | dragStart(event: any) { 68 | this.isDragging = true; 69 | this.x_diff = event.layerX - this.x; 70 | this.y_diff = event.layerY - this.y; 71 | } 72 | 73 | drag(event: any) { 74 | if (this.isDragging) { 75 | this.x = event.layerX - this.x_diff; 76 | this.y = event.layerY - this.y_diff; 77 | this.position.emit({x: this.x, y: this.y}); 78 | } 79 | } 80 | 81 | dragEnd() { 82 | this.isDragging = false; 83 | this.position.emit({x: this.x, y: this.y}); 84 | } 85 | 86 | 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/app/subaction/subaction.component.css: -------------------------------------------------------------------------------- 1 | .navbar-subaction-padding { 2 | padding: .2rem .5rem; 3 | margin-left: -15px; 4 | margin-right: -15px; 5 | height: 2.4rem; 6 | } 7 | 8 | .list-separator{ 9 | padding-left: 0.5rem; 10 | padding-right: 0.5rem; 11 | border-left: 1px solid #7b7b7b; 12 | } 13 | 14 | .nav-item:hover { 15 | background-color: #7b7b7b; 16 | } 17 | -------------------------------------------------------------------------------- /src/app/subaction/subaction.component.html: -------------------------------------------------------------------------------- 1 | 42 | -------------------------------------------------------------------------------- /src/app/subaction/subaction.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SubactionComponent } from './subaction.component'; 4 | 5 | describe('SubactionComponent', () => { 6 | let component: SubactionComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ SubactionComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(SubactionComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/subaction/subaction.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Output, EventEmitter } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-subaction', 5 | templateUrl: './subaction.component.html', 6 | styleUrls: ['./subaction.component.css'] 7 | }) 8 | export class SubactionComponent implements OnInit { 9 | 10 | @Output() move: EventEmitter = new EventEmitter(); 11 | 12 | @Output() delete: EventEmitter = new EventEmitter(); 13 | 14 | @Output() toggleUnDo: EventEmitter = new EventEmitter(); 15 | 16 | constructor() { } 17 | 18 | ngOnInit() { 19 | } 20 | 21 | onMove(direction: string) { 22 | this.move.emit(direction); 23 | } 24 | 25 | onDelete() { 26 | this.delete.emit(); 27 | } 28 | 29 | onDo(action: string) { 30 | this.toggleUnDo.emit(action); 31 | } 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/app/svg/start.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/app/template/template.component.css: -------------------------------------------------------------------------------- 1 | .svg-control:hover { 2 | cursor: pointer; 3 | } 4 | 5 | 6 | .card { 7 | /* box-shadow: 0px 3px #888585; */ 8 | } 9 | 10 | 11 | .modal-dialog { 12 | max-width: 700px !important; 13 | } 14 | 15 | 16 | .success-loader { 17 | position: fixed; 18 | top: 50%; 19 | left: 50%; 20 | transform: translate(-50%, -50%); 21 | } 22 | -------------------------------------------------------------------------------- /src/app/template/template.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | 9 | 10 |
11 |
12 | 13 | 14 | 24 | 25 | 26 | 39 | 40 | 41 | 48 | 49 | 50 |
51 |
52 |
53 | 54 | 55 |
56 |
57 | 58 | {{ activityList | json }} 59 | 60 |
61 |
62 | 63 | {{ linkList | json }} 64 | 65 |
66 |
67 |
68 | 69 | 70 | 77 | 99 | 100 | 101 | 102 | 109 | 130 | 131 | 132 | 133 | 140 | 166 | 167 | 168 |
169 | successfuly executed 170 |
171 |
172 | loading... 173 |
174 | 175 | -------------------------------------------------------------------------------- /src/app/template/template.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { TemplateComponent } from './template.component'; 4 | 5 | describe('TemplateComponent', () => { 6 | let component: TemplateComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ TemplateComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(TemplateComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/template/template.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, HostListener, TemplateRef, OnDestroy, Inject, ViewChild } from '@angular/core'; 2 | import { IDataModel, ILinkModel, IWorkflowRecord, IWorkflow, IWorkFlowReadRecords } from '../model/dataModel'; 3 | import { ControlType } from '../control-type/controlType'; 4 | import { UtililtyService } from './../services/utililty.service'; 5 | import { ApiClientService } from './../apiclient/api-client.service'; 6 | import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal'; 7 | import { EventServiceService } from './../services/event-service.service'; 8 | import { SvgArrowUtilityService } from './../services/svg-arrowUtility.service'; 9 | import { timer } from 'rxjs'; 10 | import { environment } from './../../environments/environment'; 11 | import { StateService } from '../services/state.service'; 12 | import { ConnectionService } from '../services/connection.service'; 13 | 14 | @Component({ 15 | selector: 'app-template', 16 | templateUrl: './template.component.html', 17 | styleUrls: ['./template.component.css'] 18 | }) 19 | export class TemplateComponent implements OnInit, OnDestroy { 20 | 21 | @ViewChild('runModal') private runModal; 22 | @ViewChild('saveModal') private saveModal; 23 | 24 | // selected data 25 | selectedControlData: IDataModel; 26 | selectedLinkData: ILinkModel; 27 | 28 | // selected control 29 | selectedControlFromBar: string; 30 | selectedControlonGraph: boolean; 31 | 32 | // data list 33 | existingWorkflows: IWorkFlowReadRecords[]; 34 | activityList: IDataModel[] = []; 35 | linkList: ILinkModel[] = []; 36 | // workflowId: any; 37 | inputList: any[]; 38 | selectedWorkFlow: IWorkFlowReadRecords; 39 | 40 | // isEditMode = false; 41 | showSuccessImage = false; 42 | showLoadingImage = false; 43 | 44 | // virtualLink: IConnectionType = null; 45 | errorMessage = null; 46 | 47 | // modal window 48 | public modalRef: BsModalRef; 49 | 50 | drawingLink: any = null; 51 | 52 | 53 | constructor(@Inject(SvgArrowUtilityService) private svgArrow: SvgArrowUtilityService, 54 | @Inject(UtililtyService) private utility: UtililtyService, 55 | private database: ApiClientService, 56 | private modalService: BsModalService, 57 | private eventService: EventServiceService, 58 | private state: StateService, 59 | private connection: ConnectionService) { } 60 | 61 | ngOnInit() { 62 | // Subscribed RUN button click event 63 | this.eventService.runEvent.subscribe(event => { 64 | if (event) { 65 | this.runWorkflowModal(); 66 | } 67 | }); 68 | 69 | // Subscribed SAVE button click event 70 | this.eventService.intimateClickSave.subscribe(event => { 71 | if (event) { 72 | this.openSaveModal(this.saveModal); 73 | } 74 | }); 75 | 76 | // NEW CODE 77 | // Subscribed connection start event 78 | this.connection.connect.subscribe(event => { 79 | console.log('started connection from ' + JSON.stringify(event)); 80 | this.drawingLink = { x1: event.startPoint.x, y1: event.startPoint.y, x2: 0, y2: 0, 81 | fromDirection: event.fromDirection, control: event.fromControl.data }; 82 | }); 83 | 84 | // Subscribed connection end event 85 | this.connection.receive.subscribe(event => { 86 | // console.log('end connection on ' + JSON.stringify(event)); 87 | if (this.drawingLink) { 88 | this.connectLink(this.drawingLink, event); 89 | } 90 | }); 91 | } 92 | 93 | connectLink(from, to) { 94 | if (from.control.id !== to.id) { 95 | 96 | // remove if it is existing link 97 | if (from.control.linkId) { 98 | this.linkList.splice(this.linkList.findIndex(l => l.id === from.control.linkId), 1); 99 | } 100 | // tslint:disable-next-line:triple-equals 101 | const fromControl = this.activityList.find(f => f.id == from.control.id); 102 | fromControl.nextId.push(to.id); 103 | if (from.control.isConditionYesId) { fromControl.conditionYesId.push(to.id); } 104 | const nextLinkId = this.linkList.length ? Math.max.apply(Math, this.linkList.map(l => l.id)) + 1 : 1; 105 | const linkObj: ILinkModel = {id: nextLinkId, 106 | fromId: from.control.id, 107 | toId: to.id, 108 | cordinate: {x1: from.x1, y1: from.y1, x2: to.endPoint.x, y2: to.endPoint.y }, 109 | fromDirection: from.fromDirection, 110 | toDirection: to.direction, 111 | isSelected: false, 112 | fromControlData: fromControl}; 113 | 114 | this.linkList.push(linkObj); 115 | // this.virtualLink = null; 116 | this.drawingLink = null; 117 | this.state.saveState(this.activityList, this.linkList); 118 | } 119 | } 120 | 121 | 122 | 123 | ngOnDestroy(): void { 124 | this.eventService.runEvent.unsubscribe(); 125 | this.eventService.intimateClickSave.unsubscribe(); 126 | this.connection.connect.unsubscribe(); 127 | this.connection.receive.unsubscribe(); 128 | } 129 | 130 | 131 | selectControlFromToolBar(type: string) { 132 | this.unSelectAllControl(); 133 | this.unSelectAllLinks(); 134 | this.selectedControlFromBar = type; 135 | } 136 | 137 | onDrop(event: any) { 138 | this.selectedControlFromBar = event.dataTransfer.getData('type'); 139 | this.createObject(event); 140 | } 141 | 142 | createObject(event: any) { 143 | if (this.selectedControlFromBar) { 144 | const nextId = this.activityList.length ? Math.max.apply(Math, this.activityList.map(o => o.id)) + 1 : 1; 145 | const obj: IDataModel = { id: nextId, 146 | type: this.selectedControlFromBar, 147 | cordinate: {x: event.layerX, y: event.layerY}, 148 | dimension: this.utility.getHeightAndWidthOfControl(this.selectedControlFromBar), 149 | nextId: [], 150 | data: this.utility.makeDefaultWorkflowStep(this.selectedControlFromBar, this.activityList), 151 | isSelected: true, 152 | isMultiConnect: this.selectedControlFromBar === ControlType.CONDITION, 153 | conditionYesId: [] }; 154 | 155 | this.activityList.push(obj); 156 | this.selectedControlData = obj; 157 | this.selectedControlFromBar = ''; 158 | this.selectedControlonGraph = true; 159 | this.state.saveState(this.activityList, this.linkList); 160 | } else { 161 | if (this.selectedControlonGraph) { 162 | this.unSelectAllControl(); 163 | this.unSelectAllLinks(); 164 | this.selectedControlonGraph = false; 165 | this.selectedControlData = null; 166 | } if (this.drawingLink && this.drawingLink.x2) { 167 | this.drawingLink = null; 168 | } 169 | } 170 | } 171 | 172 | unSelectAllControl() { 173 | this.activityList.forEach(f => f.isSelected = false); 174 | } 175 | 176 | unSelectAllLinks() { 177 | this.linkList.forEach(f => f.isSelected = false); 178 | } 179 | 180 | 181 | 182 | @HostListener('document:keydown', ['$event']) 183 | handleKeyboardEvent(event: KeyboardEvent) { 184 | if (this.selectedControlonGraph) { 185 | if (event.key === 'Delete') { 186 | this.delete(); 187 | } else { 188 | if (event.key === 'ArrowDown') { 189 | event.preventDefault(); 190 | } 191 | this.onActionMove(event.key); 192 | } 193 | } 194 | } 195 | 196 | delete() { 197 | const selectedControlIndex = this.activityList.findIndex(f => f.isSelected === true); 198 | if (selectedControlIndex !== -1) { 199 | this.deleteControl(selectedControlIndex); 200 | } else { 201 | const selectedLinkIndex = this.linkList.findIndex(f => f.isSelected === true); 202 | if (selectedLinkIndex !== -1) { 203 | this.deleteLink(selectedLinkIndex); 204 | } 205 | } 206 | } 207 | 208 | onActionToggle(event) { 209 | if (event === 'undo') { 210 | this.state.unDo(); 211 | } else { 212 | this.state.reDo(); 213 | } 214 | this.activityList = this.state.activityListState[this.state.activityListState.length - 1]; 215 | this.linkList = this.state.linkListState[this.state.linkListState.length - 1]; 216 | } 217 | 218 | 219 | onActionMove(direction: string) { 220 | let selectedControl: IDataModel = null; 221 | switch (direction) { 222 | case 'ArrowUp': 223 | selectedControl = this.activityList.find(f => f.isSelected === true); 224 | this.onPositionChange(selectedControl.id, {x: selectedControl.cordinate.x, 225 | y: selectedControl.cordinate.y - environment.keyMovemntSpeed }); 226 | break; 227 | case 'ArrowDown': 228 | selectedControl = this.activityList.find(f => f.isSelected === true); 229 | this.onPositionChange(selectedControl.id, {x: selectedControl.cordinate.x, 230 | y: +selectedControl.cordinate.y + +environment.keyMovemntSpeed}); 231 | break; 232 | case 'ArrowLeft': 233 | selectedControl = this.activityList.find(f => f.isSelected === true); 234 | this.onPositionChange(selectedControl.id, {x: selectedControl.cordinate.x - environment.keyMovemntSpeed, 235 | y: selectedControl.cordinate.y}); 236 | break; 237 | case 'ArrowRight': 238 | selectedControl = this.activityList.find(f => f.isSelected === true); 239 | this.onPositionChange(selectedControl.id, {x: +selectedControl.cordinate.x + +environment.keyMovemntSpeed, 240 | y: selectedControl.cordinate.y}); 241 | break; 242 | } 243 | } 244 | 245 | onSelectControl(event: number) { 246 | if (!this.selectedControlFromBar) { 247 | this.selectedControlonGraph = true; 248 | 249 | for (let i = 0; i < this.activityList.length; i++) { 250 | // tslint:disable-next-line:triple-equals 251 | if (this.activityList[i].id == event) { 252 | this.activityList[i].isSelected = true; 253 | this.selectedControlData = this.activityList[i]; 254 | } else { 255 | this.activityList[i].isSelected = false; 256 | } 257 | } 258 | } 259 | this.unSelectAllLinks(); 260 | } 261 | 262 | onSelectLink(id: number) { 263 | this.linkList.forEach(l => { 264 | this.selectedControlonGraph = true; 265 | // tslint:disable-next-line:triple-equals 266 | if (l.id == id) { 267 | l.isSelected = true; 268 | this.selectedLinkData = l; 269 | } else { 270 | l.isSelected = false; 271 | } 272 | }); 273 | this.unSelectAllControl(); 274 | } 275 | 276 | onPositionChange(id: number, event: any) { 277 | const movingControl = this.activityList.find(i => i.id === id); 278 | movingControl.cordinate.x = event.x; 279 | movingControl.cordinate.y = event.y; 280 | this.updateLinkArrow(movingControl); 281 | } 282 | 283 | 284 | 285 | updateLinkArrow(control: IDataModel) { 286 | // tslint:disable-next-line:triple-equals 287 | const fromArrow: ILinkModel = this.linkList.find(l => l.fromId == control.id); 288 | if (fromArrow) { 289 | this.linkList.forEach(l => { 290 | // tslint:disable-next-line:triple-equals 291 | if (l.fromId == control.id) { 292 | const points = this.svgArrow.CalculateArrowFromPoint(control.cordinate.x, 293 | control.cordinate.y, 294 | control.type, 295 | control.dimension, 296 | l.fromDirection, 297 | l.cordinate); 298 | l.cordinate.x1 = points.x; 299 | l.cordinate.y1 = points.y; 300 | } 301 | }); 302 | } 303 | 304 | // tslint:disable-next-line:triple-equals 305 | const toArrow: ILinkModel = this.linkList.find(l => l.toId == control.id); 306 | if (toArrow) { 307 | this.linkList.forEach(l => { 308 | // tslint:disable-next-line:triple-equals 309 | if (l.toId == control.id) { 310 | const points = this.svgArrow.CalculateArrowToPoint(control.cordinate.x, 311 | control.cordinate.y, 312 | control.type, 313 | control.dimension, 314 | l.toDirection, 315 | l.cordinate); 316 | l.cordinate.x2 = points.x; 317 | l.cordinate.y2 = points.y; 318 | } 319 | }); 320 | } 321 | } 322 | 323 | 324 | drawVirtualLink(event: any) { 325 | // NEW CODE BELOW 326 | if (this.drawingLink) { 327 | this.drawingLink.x2 = event.offsetX; 328 | this.drawingLink.y2 = event.offsetY; 329 | 330 | // except condition control 331 | const changedDragStartPoint = this.svgArrow. 332 | calculateStartPointBasedOnDrag(event.offsetX, event.offsetY, 333 | this.drawingLink.x1, this.drawingLink.y1, 334 | this.drawingLink.fromDirection, 335 | this.drawingLink.control); 336 | if (changedDragStartPoint) { 337 | this.drawingLink.x1 = changedDragStartPoint.nextX; 338 | this.drawingLink.y1 = changedDragStartPoint.nextY; 339 | this.drawingLink.fromDirection = changedDragStartPoint.direction; 340 | } 341 | } 342 | } 343 | 344 | 345 | deleteLink(linkIndex: number) { 346 | const link = this.linkList[linkIndex]; 347 | this.activityList.forEach(a => { 348 | // tslint:disable-next-line:triple-equals 349 | if (a.id == link.fromId) { 350 | a.nextId = this.utility.removeFromArray(a.nextId, link.toId); 351 | } 352 | }); 353 | this.linkList.splice(linkIndex, 1); 354 | } 355 | 356 | deleteControl(controlIndex: number) { 357 | const control = this.activityList[controlIndex]; 358 | const linkIndexTobeDelete: number[] = []; 359 | this.linkList.forEach((l, i) => { 360 | // tslint:disable-next-line:triple-equals 361 | if (l.toId == control.id || l.fromId == control.id) { 362 | linkIndexTobeDelete.push(i); 363 | } 364 | }); 365 | 366 | // delete selected control 367 | this.activityList.splice(controlIndex, 1); 368 | 369 | // delete associated links connected to deleted control 370 | while (linkIndexTobeDelete.length) { 371 | const linkIndex = linkIndexTobeDelete.pop(); 372 | const link = this.linkList[linkIndex]; 373 | this.activityList.forEach(a => { 374 | // tslint:disable-next-line:triple-equals 375 | if (a.nextId.includes(link.toId)) { 376 | a.nextId = this.utility.removeFromArray(a.nextId, link.toId); 377 | if (a.type === ControlType.CONDITION) { 378 | a.conditionYesId = []; 379 | } 380 | } 381 | }); 382 | this.linkList.splice(linkIndex, 1); 383 | } 384 | } 385 | 386 | onClear() { 387 | this.activityList = []; 388 | this.linkList = []; 389 | this.selectedControlData = null; 390 | this.selectedLinkData = null; 391 | this.selectedControlFromBar = null; 392 | this.selectedWorkFlow = null; 393 | this.drawingLink = null; 394 | this.eventService.pushSaveEvent(null); 395 | this.errorMessage = null; 396 | this.state.clearState(); 397 | if (this.modalRef) { this.modalRef.hide(); } 398 | } 399 | 400 | 401 | openLoadModal(template: TemplateRef) { 402 | this.modalRef = this.modalService.show(template); 403 | this.database.getAll().subscribe(res => { 404 | this.existingWorkflows = res; 405 | }, err => console.log(err)); 406 | } 407 | 408 | 409 | load(val: any) { 410 | // tslint:disable-next-line:triple-equals 411 | this.selectedWorkFlow = this.existingWorkflows.find(f => f.id == val); 412 | this.activityList = this.utility.refreshConditionDoList(JSON.parse(this.selectedWorkFlow.jsonObject).controls); 413 | this.linkList = JSON.parse(this.selectedWorkFlow.jsonObject).links; 414 | // this.isEditMode = true; 415 | this.modalRef.hide(); 416 | this.eventService.pushSaveEvent(this.selectedWorkFlow.name); 417 | } 418 | 419 | openSaveModal(template: TemplateRef) { 420 | if (this.activityList.length > 0) { 421 | const isValid = this.utility.validateWorkflow(this.activityList); 422 | if (!isValid) { 423 | this.modalRef = this.modalService.show(template); 424 | } else { 425 | alert(isValid); 426 | } 427 | } 428 | } 429 | 430 | save(templateId: any) { 431 | const activityCloneList: IDataModel[] = JSON.parse(JSON.stringify(this.activityList)); 432 | // activityCloneList = Object.assign(activityCloneList, this.activityList); 433 | const output: IWorkflow = this.utility. 434 | makeJsonForWorkflowEngine(activityCloneList, templateId, environment.workflowDataType); 435 | 436 | const dataToSave: IWorkflowRecord = {Name: templateId, JsonObject: JSON.stringify({ 437 | controls: this.activityList, 438 | links: this.linkList, 439 | parsedData: output 440 | })}; 441 | 442 | this.database.save(dataToSave).subscribe(res => { 443 | console.log(res); 444 | if (res) { 445 | // this.selectedWorkFlow = { id: res, name: templateId, jsonObject: dataToSave.JsonObject}; 446 | // this.eventService.pushSaveEvent(this.selectedWorkFlow.name); 447 | this.modalRef.hide(); 448 | // this.isEditMode = false; 449 | this.showSuccessTimer(); 450 | this.onClear(); 451 | } 452 | }, err => { 453 | console.log(err); 454 | this.errorMessage = err.message; 455 | }); 456 | } 457 | 458 | runWorkflowModal() { 459 | if (this.selectedWorkFlow) { 460 | // TODO run 461 | this.inputList = Array.from(this.utility.collectInputParams(this.activityList)); 462 | this.modalRef = this.modalService.show(this.runModal); 463 | } else { 464 | alert('Workflow is not saved ! Save before you run'); 465 | } 466 | } 467 | 468 | executeWorkflow(params: any[]) { 469 | this.showLoadingImage = true; 470 | const queryparam = JSON.stringify({ query: JSON.stringify(this.parseInputParamater(params)), Id: this.selectedWorkFlow.id }); 471 | console.log(queryparam); 472 | this.database.run(queryparam).subscribe(res => { 473 | this.modalRef.hide(); 474 | this.inputList = []; 475 | this.showLoadingImage = false; 476 | this.showSuccessTimer(); 477 | this.errorMessage = null; 478 | }, err => { 479 | this.showLoadingImage = false; 480 | console.log(err); 481 | this.errorMessage = err.message; 482 | }); 483 | } 484 | 485 | parseInputParamater(params: any[]): any { 486 | if (params && params.length) { 487 | const parsedParams = {}; 488 | params.forEach(i => { 489 | parsedParams[i.key] = i.value; 490 | }); 491 | return parsedParams; 492 | } else { 493 | return ''; 494 | } 495 | } 496 | 497 | showSuccessTimer() { 498 | this.showSuccessImage = true; 499 | const clock = timer(2000); 500 | clock.subscribe(() => this.showSuccessImage = false); 501 | } 502 | 503 | modalHide() { 504 | this.errorMessage = null; 505 | this.modalRef.hide(); 506 | } 507 | 508 | 509 | } 510 | -------------------------------------------------------------------------------- /src/app/twodimension-arrow/twodimension-arrow.component.css: -------------------------------------------------------------------------------- 1 | .linkArrow { 2 | fill:none; 3 | stroke:#8d8b8b; 4 | stroke-width:2; 5 | } 6 | 7 | .linkArrow:hover { 8 | cursor: col-resize; 9 | stroke-width:4; 10 | stroke: #070707; 11 | } 12 | 13 | .linkPointArrow { 14 | fill:#8d8b8b; 15 | } 16 | 17 | .linkPointArrow:hover { 18 | cursor: pointer; 19 | fill: #070707; 20 | } 21 | 22 | .linkArrow-select { 23 | stroke: #0a0a0a; 24 | stroke-width: 2; 25 | stroke-dasharray: 8 2; 26 | animation: dash 20s linear alternate infinite; 27 | } 28 | 29 | @keyframes dash { 30 | from { 31 | stroke-dashoffset: 1000; 32 | } 33 | to { 34 | stroke-dashoffset: 0; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/app/twodimension-arrow/twodimension-arrow.component.html: -------------------------------------------------------------------------------- 1 | 7 | 8 | -------------------------------------------------------------------------------- /src/app/twodimension-arrow/twodimension-arrow.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { TwodimensionArrowComponent } from './twodimension-arrow.component'; 4 | 5 | describe('TwodimensionArrowComponent', () => { 6 | let component: TwodimensionArrowComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ TwodimensionArrowComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(TwodimensionArrowComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/twodimension-arrow/twodimension-arrow.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input, OnChanges, Output, EventEmitter, Inject } from '@angular/core'; 2 | import { SvgArrowUtilityService } from '../services/svg-arrowUtility.service'; 3 | import { ConnectionService } from '../services/connection.service'; 4 | import { IDataModel } from '../model/dataModel'; 5 | 6 | @Component({ 7 | selector: '[app-twodimension-arrow]', 8 | templateUrl: './twodimension-arrow.component.html', 9 | styleUrls: ['./twodimension-arrow.component.css'] 10 | }) 11 | export class TwodimensionArrowComponent implements OnInit, OnChanges { 12 | 13 | @Input() id: number; 14 | 15 | @Input() x1: number; 16 | @Input() y1: number; 17 | 18 | @Input() x2: number; 19 | @Input() y2: number; 20 | 21 | @Input() fromDirection: string; 22 | @Input() pointDirection: string; 23 | 24 | @Input() isSelected: boolean; 25 | 26 | @Input() fromId: number; 27 | @Input() toId: number; 28 | 29 | @Input() fromControlData: IDataModel; 30 | 31 | @Output() select: EventEmitter = new EventEmitter(); 32 | 33 | linePoints: string; 34 | arrowPoints: string; 35 | isDragging = false; 36 | 37 | x_diff = 0; 38 | y_diff = 0; 39 | 40 | constructor(private svgArrow: SvgArrowUtilityService, 41 | private connectService: ConnectionService) { } 42 | 43 | ngOnChanges(changes: any): void { 44 | if (changes.x1) { this.x1 = changes.x1.currentValue; } 45 | if (changes.y1) { this.y1 = changes.y1.currentValue; } 46 | if (changes.x2) { this.x2 = changes.x2.currentValue; } 47 | if (changes.y2) { this.y2 = changes.y2.currentValue; } 48 | if (changes.isSelected) { 49 | this.isSelected = changes.isSelected.currentValue; 50 | } 51 | 52 | this.linePoints = this.svgArrow.CalculateArrowLines(this.x1, this.y1, this.x2, this.y2, 53 | this.fromDirection, this.pointDirection, null, null); 54 | this.arrowPoints = this.svgArrow.CalculateArrowPoint(this.x2, this.y2, this.pointDirection, 7); 55 | } 56 | 57 | ngOnInit() { 58 | 59 | this.linePoints = this.svgArrow.CalculateArrowLines(this.x1, this.y1, this.x2, this.y2, 60 | this.fromDirection, this.pointDirection, null, null); 61 | this.arrowPoints = this.svgArrow.CalculateArrowPoint(this.x2, this.y2, this.pointDirection, 7); 62 | } 63 | 64 | onSelect(event: any) { 65 | event.stopPropagation(); 66 | this.isSelected = true; 67 | this.select.emit(this.id); 68 | } 69 | 70 | moveStart(event: any) { 71 | const startpoint = { x: this.x1, y: this.y1 }; 72 | const control = { type: this.fromControlData.type, data: { height: this.fromControlData.dimension.height, 73 | width: this.fromControlData.dimension.width, 74 | isConnectionPointFixed: false, 75 | id: this.fromId, 76 | linkId: this.id}}; 77 | 78 | this.connectService.pushConnectStartPoint({ fromControl: control, 79 | fromDirection: this.fromDirection, startPoint: startpoint }); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/app/under-construction/under-construction.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saswat-pramati/workflow-ui/a95f539c1b7ea82a7e7ab5b68d6cf96773b875c0/src/app/under-construction/under-construction.component.css -------------------------------------------------------------------------------- /src/app/under-construction/under-construction.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/app/under-construction/under-construction.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { UnderConstructionComponent } from './under-construction.component'; 4 | 5 | describe('UnderConstructionComponent', () => { 6 | let component: UnderConstructionComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ UnderConstructionComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(UnderConstructionComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/under-construction/under-construction.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-under-construction', 5 | templateUrl: './under-construction.component.html', 6 | styleUrls: ['./under-construction.component.css'] 7 | }) 8 | export class UnderConstructionComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/app/virtual-arrow/virtual-arrow.component.css: -------------------------------------------------------------------------------- 1 | .virtual-arrow { 2 | fill:none; 3 | stroke: #121312; 4 | stroke-width: 1; 5 | stroke-dasharray: 8 2; 6 | } 7 | -------------------------------------------------------------------------------- /src/app/virtual-arrow/virtual-arrow.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/app/virtual-arrow/virtual-arrow.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { VirtualArrowComponent } from './virtual-arrow.component'; 4 | 5 | describe('VirtualArrowComponent', () => { 6 | let component: VirtualArrowComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ VirtualArrowComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(VirtualArrowComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/virtual-arrow/virtual-arrow.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input, OnChanges } from '@angular/core'; 2 | import { SvgArrowUtilityService } from '../services/svg-arrowUtility.service'; 3 | 4 | @Component({ 5 | selector: '[app-virtual-arrow]', 6 | templateUrl: './virtual-arrow.component.html', 7 | styleUrls: ['./virtual-arrow.component.css'] 8 | }) 9 | export class VirtualArrowComponent implements OnInit, OnChanges { 10 | 11 | @Input() x1: number; 12 | @Input() y1: number; 13 | @Input() x2: number; 14 | @Input() y2: number; 15 | // @Input() points: string; 16 | @Input() pointDirection: string; 17 | @Input() isSelected: boolean; 18 | 19 | linePoints: string; 20 | // arrowPoints: string; 21 | 22 | constructor(private svgArrow: SvgArrowUtilityService) { } 23 | 24 | ngOnInit() { 25 | this.linePoints = this.svgArrow.CalculatePolyLinePoints(this.x1, this.y1, this.x2, this.y2); 26 | // this.arrowPoints = this.svgArrow.CalculateArrowPoint(this.x2, this.y2, this.pointDirection, 10); 27 | } 28 | 29 | ngOnChanges(changes: any): void { 30 | if (changes.x1) { this.x1 = changes.x1.currentValue; } 31 | if (changes.y1) { this.y1 = changes.y1.currentValue; } 32 | if (changes.x2) { this.x2 = changes.x2.currentValue; } 33 | if (changes.y2) { this.y2 = changes.y2.currentValue; } 34 | 35 | // this.linePoints = this.svgArrow.CalculatePolyLinePoints(this.x1, this.y1, this.x2, this.y2); 36 | this.linePoints = this.svgArrow.drawConnection({ type: 'activity'}, this.pointDirection, this.x1, this.y1, this.x2, this.y2); 37 | // this.arrowPoints = this.svgArrow.CalculateArrowPoint(this.x2, this.y2, this.pointDirection, 10); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saswat-pramati/workflow-ui/a95f539c1b7ea82a7e7ab5b68d6cf96773b875c0/src/assets/.gitkeep -------------------------------------------------------------------------------- /src/assets/activity.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/condition.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/end.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/grid.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saswat-pramati/workflow-ui/a95f539c1b7ea82a7e7ab5b68d6cf96773b875c0/src/assets/grid.gif -------------------------------------------------------------------------------- /src/assets/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saswat-pramati/workflow-ui/a95f539c1b7ea82a7e7ab5b68d6cf96773b875c0/src/assets/loading.gif -------------------------------------------------------------------------------- /src/assets/start.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/success.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saswat-pramati/workflow-ui/a95f539c1b7ea82a7e7ab5b68d6cf96773b875c0/src/assets/success.gif -------------------------------------------------------------------------------- /src/assets/success_transparentbg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saswat-pramati/workflow-ui/a95f539c1b7ea82a7e7ab5b68d6cf96773b875c0/src/assets/success_transparentbg.gif -------------------------------------------------------------------------------- /src/assets/under_construction_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saswat-pramati/workflow-ui/a95f539c1b7ea82a7e7ab5b68d6cf96773b875c0/src/assets/under_construction_icon.png -------------------------------------------------------------------------------- /src/assets/workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saswat-pramati/workflow-ui/a95f539c1b7ea82a7e7ab5b68d6cf96773b875c0/src/assets/workflow.png -------------------------------------------------------------------------------- /src/browserslist: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # For IE 9-11 support, please uncomment the last line of the file and adjust as needed 5 | > 0.5% 6 | last 2 versions 7 | Firefox ESR 8 | not dead 9 | # IE 9-11 -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | keyMovemntSpeed: 6, 4 | workflowDataType: 'WorkflowCore.Models.DataContext, WorkflowCore', 5 | version: '2.0.0', 6 | appName: 'Workflow-UI' 7 | }; 8 | -------------------------------------------------------------------------------- /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 | keyMovemntSpeed: 6, 8 | workflowDataType: 'WorkflowCore.Models.DataContext, WorkflowCore', 9 | version: '2.0.0', 10 | appName: 'Workflow-UI' 11 | }; 12 | 13 | /* 14 | * In development mode, to ignore zone related error stack frames such as 15 | * `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can 16 | * import the following file, but please comment it out in production mode 17 | * because it will have performance impact when throw error 18 | */ 19 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 20 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saswat-pramati/workflow-ui/a95f539c1b7ea82a7e7ab5b68d6cf96773b875c0/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Workflow 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../coverage'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false 30 | }); 31 | }; -------------------------------------------------------------------------------- /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.log(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/docs/ts/latest/guide/browser-support.html 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 22 | // import 'core-js/es6/symbol'; 23 | // import 'core-js/es6/object'; 24 | // import 'core-js/es6/function'; 25 | // import 'core-js/es6/parse-int'; 26 | // import 'core-js/es6/parse-float'; 27 | // import 'core-js/es6/number'; 28 | // import 'core-js/es6/math'; 29 | // import 'core-js/es6/string'; 30 | // import 'core-js/es6/date'; 31 | // import 'core-js/es6/array'; 32 | // import 'core-js/es6/regexp'; 33 | // import 'core-js/es6/map'; 34 | // import 'core-js/es6/weak-map'; 35 | // import 'core-js/es6/set'; 36 | 37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 38 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 39 | 40 | /** IE10 and IE11 requires the following for the Reflect API. */ 41 | // import 'core-js/es6/reflect'; 42 | 43 | 44 | /** Evergreen browsers require these. **/ 45 | // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. 46 | import 'core-js/es7/reflect'; 47 | 48 | 49 | /** 50 | * Web Animations `@angular/platform-browser/animations` 51 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 52 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 53 | **/ 54 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 55 | 56 | /** 57 | * By default, zone.js will patch all possible macroTask and DomEvents 58 | * user can disable parts of macroTask/DomEvents patch by setting following flags 59 | */ 60 | 61 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 62 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 63 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 64 | 65 | /* 66 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 67 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 68 | */ 69 | // (window as any).__Zone_enable_cross_context_check = true; 70 | 71 | /*************************************************************************************************** 72 | * Zone JS is required by default for Angular itself. 73 | */ 74 | import 'zone.js/dist/zone'; // Included with Angular CLI. 75 | 76 | 77 | 78 | /*************************************************************************************************** 79 | * APPLICATION IMPORTS 80 | */ 81 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | @import "~@angular/cdk/overlay-prebuilt.css"; 2 | /* You can add global styles to this file, and also import other style files */ 3 | .svg { 4 | height: 1000px; 5 | width: 100%; 6 | display: block; 7 | /* background-color: #fbf9f9; */ 8 | } 9 | 10 | .graph { 11 | background: url(assets/grid.gif); 12 | } 13 | 14 | .bordered { 15 | border: 1; 16 | } 17 | 18 | .div-pannel { 19 | /* padding: 3px; */ 20 | } 21 | 22 | 23 | .connector { 24 | fill: #a8a6a6; 25 | visibility: hidden; 26 | } 27 | 28 | .visible { 29 | visibility: visible; 30 | } 31 | 32 | .connector:hover { 33 | fill: #000000; 34 | cursor: pointer; 35 | } 36 | 37 | .receiver { 38 | fill: #818382; 39 | visibility: hidden; 40 | } 41 | 42 | .receiver:hover { 43 | stroke: #0c8d28; 44 | stroke-width: 5; 45 | fill: #df6c6c; 46 | cursor: pointer; 47 | } 48 | 49 | .control-text { 50 | fill: black; 51 | font-size: 12px; 52 | font-weight: bold; 53 | text-anchor: middle; 54 | } 55 | 56 | .control-text { 57 | cursor: move; 58 | } 59 | 60 | svg text { 61 | -webkit-user-select: none; 62 | -moz-user-select: none; 63 | -ms-user-select: none; 64 | user-select: none; 65 | } 66 | svg text::selection { 67 | background: none; 68 | } 69 | 70 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "types": [] 6 | }, 7 | "exclude": [ 8 | "src/test.ts", 9 | "**/*.spec.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "test.ts", 12 | "polyfills.ts" 13 | ], 14 | "include": [ 15 | "**/*.spec.ts", 16 | "**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /src/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "app", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "app", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "module": "es2015", 9 | "moduleResolution": "node", 10 | "emitDecoratorMetadata": true, 11 | "experimentalDecorators": true, 12 | "target": "es5", 13 | "typeRoots": [ 14 | "node_modules/@types" 15 | ], 16 | "lib": [ 17 | "es2017", 18 | "dom" 19 | ] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "arrow-return-shorthand": true, 7 | "callable-types": true, 8 | "class-name": true, 9 | "comment-format": [ 10 | true, 11 | "check-space" 12 | ], 13 | "curly": true, 14 | "deprecation": { 15 | "severity": "warn" 16 | }, 17 | "eofline": true, 18 | "forin": true, 19 | "import-blacklist": [ 20 | true, 21 | "rxjs/Rx" 22 | ], 23 | "import-spacing": true, 24 | "indent": [ 25 | true, 26 | "spaces" 27 | ], 28 | "interface-over-type-literal": true, 29 | "label-position": true, 30 | "max-line-length": [ 31 | true, 32 | 140 33 | ], 34 | "member-access": false, 35 | "member-ordering": [ 36 | true, 37 | { 38 | "order": [ 39 | "static-field", 40 | "instance-field", 41 | "static-method", 42 | "instance-method" 43 | ] 44 | } 45 | ], 46 | "no-arg": true, 47 | "no-bitwise": true, 48 | "no-console": [ 49 | true, 50 | "debug", 51 | "info", 52 | "time", 53 | "timeEnd", 54 | "trace" 55 | ], 56 | "no-construct": true, 57 | "no-debugger": true, 58 | "no-duplicate-super": true, 59 | "no-empty": false, 60 | "no-empty-interface": true, 61 | "no-eval": true, 62 | "no-inferrable-types": [ 63 | true, 64 | "ignore-params" 65 | ], 66 | "no-misused-new": true, 67 | "no-non-null-assertion": true, 68 | "no-shadowed-variable": true, 69 | "no-string-literal": false, 70 | "no-string-throw": true, 71 | "no-switch-case-fall-through": true, 72 | "no-trailing-whitespace": true, 73 | "no-unnecessary-initializer": true, 74 | "no-unused-expression": true, 75 | "no-use-before-declare": true, 76 | "no-var-keyword": true, 77 | "object-literal-sort-keys": false, 78 | "one-line": [ 79 | true, 80 | "check-open-brace", 81 | "check-catch", 82 | "check-else", 83 | "check-whitespace" 84 | ], 85 | "prefer-const": true, 86 | "quotemark": [ 87 | true, 88 | "single" 89 | ], 90 | "radix": true, 91 | "semicolon": [ 92 | true, 93 | "always" 94 | ], 95 | "triple-equals": [ 96 | true, 97 | "allow-null-check" 98 | ], 99 | "typedef-whitespace": [ 100 | true, 101 | { 102 | "call-signature": "nospace", 103 | "index-signature": "nospace", 104 | "parameter": "nospace", 105 | "property-declaration": "nospace", 106 | "variable-declaration": "nospace" 107 | } 108 | ], 109 | "unified-signatures": true, 110 | "variable-name": false, 111 | "whitespace": [ 112 | true, 113 | "check-branch", 114 | "check-decl", 115 | "check-operator", 116 | "check-separator", 117 | "check-type" 118 | ], 119 | "no-output-on-prefix": true, 120 | "use-input-property-decorator": true, 121 | "use-output-property-decorator": true, 122 | "use-host-property-decorator": true, 123 | "no-input-rename": true, 124 | "no-output-rename": true, 125 | "use-life-cycle-interface": true, 126 | "use-pipe-transform-interface": true, 127 | "component-class-suffix": true, 128 | "directive-class-suffix": true 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /vsstart.bat: -------------------------------------------------------------------------------- 1 | code . -------------------------------------------------------------------------------- /workflow-ui.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saswat-pramati/workflow-ui/a95f539c1b7ea82a7e7ab5b68d6cf96773b875c0/workflow-ui.gif --------------------------------------------------------------------------------