├── .editorconfig ├── .github ├── FUNDING.yml └── workflows │ ├── build-push.yml │ ├── ci.yml │ ├── codeql.yml │ ├── commit-linter.yml │ ├── release.yml │ ├── stale.yml │ └── update-docs.yml ├── .gitignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── DEV_NOTES.md ├── LICENSE ├── README.md ├── ng-package.js ├── ng12 ├── ng-nested.js ├── package-lock.json ├── package.json ├── src └── tsconfig.json ├── ng13 ├── ng-nested.js ├── package-lock.json ├── package.json ├── src └── tsconfig.json ├── ng14 ├── ng-nested.js ├── package-lock.json ├── package.json ├── src └── tsconfig.json ├── ng15 ├── ng-nested.js ├── package-lock.json ├── package.json ├── src └── tsconfig.json ├── ng16 ├── ng-nested.js ├── package-lock.json ├── package.json ├── patch │ ├── module.ts │ └── presets │ │ ├── context-menu │ │ └── module.ts │ │ ├── minimap │ │ └── module.ts │ │ └── reroute │ │ └── module.ts ├── src └── tsconfig.json ├── ng17 ├── ng-nested.js ├── package-lock.json ├── package.json ├── patch │ ├── module.ts │ └── presets │ │ ├── context-menu │ │ └── module.ts │ │ ├── minimap │ │ └── module.ts │ │ └── reroute │ │ └── module.ts ├── src └── tsconfig.json ├── ng18 ├── ng-nested.js ├── package-lock.json ├── package.json ├── patch │ ├── module.ts │ └── presets │ │ ├── context-menu │ │ └── module.ts │ │ ├── minimap │ │ └── module.ts │ │ └── reroute │ │ └── module.ts ├── src └── tsconfig.json ├── ng19 ├── ng-nested.js ├── package-lock.json ├── package.json ├── patch │ ├── module.ts │ └── presets │ │ ├── classic │ │ └── components │ │ │ └── node │ │ │ └── .node.component.ts │ │ ├── context-menu │ │ ├── components │ │ │ ├── item │ │ │ │ └── .item.component.ts │ │ │ └── menu │ │ │ │ └── .menu.component.ts │ │ └── module.ts │ │ ├── minimap │ │ ├── components │ │ │ └── minimap │ │ │ │ └── .minimap.component.ts │ │ └── module.ts │ │ └── reroute │ │ ├── components │ │ └── pins │ │ │ └── .pins.component.ts │ │ └── module.ts ├── src └── tsconfig.json ├── package-lock.json ├── package.json ├── scripts └── patch.js ├── src ├── core.ts ├── index.ts ├── module.ts ├── presets │ ├── classic │ │ ├── components │ │ │ ├── connection │ │ │ │ ├── connection-wrapper.component.ts │ │ │ │ ├── connection.component.html │ │ │ │ ├── connection.component.sass │ │ │ │ └── connection.component.ts │ │ │ ├── control │ │ │ │ ├── control.component.html │ │ │ │ ├── control.component.sass │ │ │ │ └── control.component.ts │ │ │ ├── index.ts │ │ │ ├── node │ │ │ │ ├── node.component.html │ │ │ │ ├── node.component.sass │ │ │ │ └── node.component.ts │ │ │ └── socket │ │ │ │ ├── socket.component.sass │ │ │ │ └── socket.component.ts │ │ ├── index.ts │ │ ├── types.ts │ │ └── vars.sass │ ├── context-menu │ │ ├── block.sass │ │ ├── components │ │ │ ├── index.ts │ │ │ ├── item │ │ │ │ ├── item.component.html │ │ │ │ ├── item.component.sass │ │ │ │ └── item.component.ts │ │ │ ├── menu │ │ │ │ ├── menu.component.html │ │ │ │ ├── menu.component.sass │ │ │ │ └── menu.component.ts │ │ │ └── search │ │ │ │ ├── search.component.html │ │ │ │ ├── search.component.sass │ │ │ │ └── search.component.ts │ │ ├── context-vars.sass │ │ ├── debounce.ts │ │ ├── index.ts │ │ ├── module.ts │ │ └── types.ts │ ├── index.ts │ ├── minimap │ │ ├── components │ │ │ ├── index.ts │ │ │ ├── mini-node │ │ │ │ ├── mini-node.component.html │ │ │ │ ├── mini-node.component.sass │ │ │ │ └── mini-node.component.ts │ │ │ ├── mini-viewport │ │ │ │ ├── mini-viewport.component.html │ │ │ │ ├── mini-viewport.component.sass │ │ │ │ └── mini-viewport.component.ts │ │ │ └── minimap │ │ │ │ ├── minimap.component.html │ │ │ │ ├── minimap.component.sass │ │ │ │ └── minimap.component.ts │ │ ├── index.ts │ │ ├── module.ts │ │ └── types.ts │ ├── reroute │ │ ├── components │ │ │ ├── index.ts │ │ │ ├── pin │ │ │ │ ├── pin.component.sass │ │ │ │ └── pin.component.ts │ │ │ └── pins │ │ │ │ ├── pins.component.html │ │ │ │ └── pins.component.ts │ │ ├── index.ts │ │ ├── module.ts │ │ └── types.ts │ └── types.ts ├── ref.ts ├── reflect.ts ├── shared │ └── drag.ts └── types.ts ├── tsconfig.json └── tslint.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | patreon: ni55an 2 | open_collective: rete 3 | -------------------------------------------------------------------------------- /.github/workflows/build-push.yml: -------------------------------------------------------------------------------- 1 | name: Build and Push 2 | run-name: Build and Push to dist/${{ github.ref_name }} 3 | 4 | on: 5 | workflow_dispatch: 6 | 7 | jobs: 8 | push: 9 | uses: retejs/.github/.github/workflows/build-push.yml@main 10 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | branches: [ "main", "beta" ] 7 | 8 | jobs: 9 | ci: 10 | uses: retejs/.github/.github/workflows/ci.yml@main 11 | secrets: inherit 12 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: CodeQL 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [ "main", "beta" ] 7 | pull_request: 8 | branches: [ "main", "beta" ] 9 | 10 | jobs: 11 | codeql: 12 | uses: retejs/.github/.github/workflows/codeql.yml@main 13 | -------------------------------------------------------------------------------- /.github/workflows/commit-linter.yml: -------------------------------------------------------------------------------- 1 | name: Commit linter 2 | 3 | on: 4 | pull_request: 5 | branches: [ "main", "beta" ] 6 | 7 | jobs: 8 | lint: 9 | uses: retejs/.github/.github/workflows/commit-linter.yml@main 10 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [ "main", "beta" ] 7 | 8 | jobs: 9 | release: 10 | uses: retejs/.github/.github/workflows/release.yml@main 11 | secrets: inherit 12 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: Close stale issues and PRs 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: '30 1 * * 4' 7 | 8 | jobs: 9 | stale: 10 | uses: retejs/.github/.github/workflows/stale.yml@main 11 | secrets: inherit 12 | -------------------------------------------------------------------------------- /.github/workflows/update-docs.yml: -------------------------------------------------------------------------------- 1 | name: Update docs 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: ["main"] 7 | 8 | jobs: 9 | pull: 10 | uses: retejs/.github/.github/workflows/update-docs.yml@main 11 | secrets: inherit 12 | with: 13 | filename: "7.rete-angular-plugin" 14 | package: rete-angular-plugin 15 | -------------------------------------------------------------------------------- /.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 | # Only exists if Bazel was run 8 | /bazel-out 9 | 10 | # dependencies 11 | node_modules 12 | 13 | # profiling files 14 | chrome-profiler-events.json 15 | speed-measure-plugin.json 16 | 17 | # IDEs and editors 18 | /.idea 19 | .project 20 | .classpath 21 | .c9/ 22 | *.launch 23 | .settings/ 24 | *.sublime-workspace 25 | 26 | # IDE - VSCode 27 | .vscode/* 28 | !.vscode/settings.json 29 | !.vscode/tasks.json 30 | !.vscode/launch.json 31 | !.vscode/extensions.json 32 | .history/* 33 | 34 | # misc 35 | /.sass-cache 36 | /connect.lock 37 | /coverage 38 | /libpeerconnection.log 39 | npm-debug.log 40 | yarn-error.log 41 | testem.log 42 | /typings 43 | 44 | # System Files 45 | .DS_Store 46 | Thumbs.db 47 | 48 | .src 49 | docs 50 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [2.3.2](https://github.com/retejs/angular-plugin/compare/v2.3.1...v2.3.2) (2025-02-09) 2 | 3 | 4 | ### Bug Fixes 5 | 6 | * update injector for re-mounted components ([f8e5b62](https://github.com/retejs/angular-plugin/commit/f8e5b626b9116d45785c176ffd2f9506a1107155)) 7 | 8 | ## [2.3.1](https://github.com/retejs/angular-plugin/compare/v2.3.0...v2.3.1) (2025-01-04) 9 | 10 | 11 | ### Bug Fixes 12 | 13 | * context menu subitems for v19 ([1e0abed](https://github.com/retejs/angular-plugin/commit/1e0abedbd0e7d30e9e16ca133f6195b824708021)) 14 | 15 | # [2.3.0](https://github.com/retejs/angular-plugin/compare/v2.2.1...v2.3.0) (2024-12-30) 16 | 17 | 18 | ### Bug Fixes 19 | 20 | * update in angular 19 ([e77caef](https://github.com/retejs/angular-plugin/commit/e77caef983586bf1fe808337421a43f2ff9a72b3)) 21 | 22 | 23 | ### Features 24 | 25 | * support angular 19 ([3b517a7](https://github.com/retejs/angular-plugin/commit/3b517a7a729334d2cfd264018cc9739619f50911)) 26 | 27 | ## [2.2.1](https://github.com/retejs/angular-plugin/compare/v2.2.0...v2.2.1) (2024-09-14) 28 | 29 | 30 | ### Bug Fixes 31 | 32 | * fix peer dependencies constraints ([5870f77](https://github.com/retejs/angular-plugin/commit/5870f773b4029d211f99af1490af12196e3b0ccb)) 33 | 34 | # [2.2.0](https://github.com/retejs/angular-plugin/compare/v2.1.2...v2.2.0) (2024-09-14) 35 | 36 | 37 | ### Features 38 | 39 | * add support for angular 18 ([3189913](https://github.com/retejs/angular-plugin/commit/31899136770001f8ebde4fe51d7ac2437dfb630d)) 40 | 41 | ## [2.1.2](https://github.com/retejs/angular-plugin/compare/v2.1.1...v2.1.2) (2024-08-23) 42 | 43 | 44 | ### Bug Fixes 45 | 46 | * node sizing ([ce0d8f2](https://github.com/retejs/angular-plugin/commit/ce0d8f27a9c88662d501b3f9e1916dd2ae54da5e)) 47 | 48 | ## [2.1.1](https://github.com/retejs/angular-plugin/compare/v2.1.0...v2.1.1) (2024-01-27) 49 | 50 | 51 | ### Bug Fixes 52 | 53 | * **build:** source maps ([d9d21ec](https://github.com/retejs/angular-plugin/commit/d9d21ec6b6b53f1a4b25fc8f71a87511fb4cefc3)) 54 | 55 | # [2.1.0](https://github.com/retejs/angular-plugin/compare/v2.0.2...v2.1.0) (2023-12-15) 56 | 57 | 58 | ### Features 59 | 60 | * support Angular 17 ([b8c3250](https://github.com/retejs/angular-plugin/commit/b8c32500b4df952fd22907d0f3e31dd1edaef2c9)) 61 | 62 | ## [2.0.2](https://github.com/retejs/angular-plugin/compare/v2.0.1...v2.0.2) (2023-07-16) 63 | 64 | 65 | ### Bug Fixes 66 | 67 | * install deps for v16 ([fd667a2](https://github.com/retejs/angular-plugin/commit/fd667a2c3e37fa45bc9df41febf66bd39a84552c)) 68 | 69 | ## [2.0.1](https://github.com/retejs/angular-plugin/compare/v2.0.0...v2.0.1) (2023-07-16) 70 | 71 | 72 | ### Bug Fixes 73 | 74 | * emit 'unmount' ([4d17a2e](https://github.com/retejs/angular-plugin/commit/4d17a2e6d05567ff83c48881ba3edcf5dfe87801)) 75 | 76 | ## v2.0.0-beta.19 77 | 78 | Breaking changes: 79 | 80 | ```ts 81 | render.addPreset(Presets.reroute.setup({ 82 | translate(id, dx, dy) { 83 | // const { k } = rea.area.transform 84 | // dividing by k isn't needed 85 | reroutePlugin.translate(id, dx, dy); 86 | } 87 | })) 88 | ``` 89 | 90 | ## 2.0.0-beta.18 91 | 92 | Breaking changes: `area` property omitted from `Presets.classic.setup({ area })` 93 | 94 | ## 2.0.0-beta.17 95 | 96 | Support Angular 16 97 | 98 | ## 2.0.0-beta.14 99 | 100 | Implemented presets for Minimap and Reroute 101 | 102 | ## 2.0.0-beta.13 103 | 104 | Implemented preset for Context menu 105 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | Check out the [Code of Conduct](https://retejs.org/docs/code-of-conduct) 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Check out the [Contribution guide](https://retejs.org/docs/contribution) 2 | -------------------------------------------------------------------------------- /DEV_NOTES.md: -------------------------------------------------------------------------------- 1 | - nested `ng-packagr`'s config shouldn't be named ng-packagr.js(.json) to prevent it from being included when building from root 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 "Ni55aN" Vitaliy Stoliarov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Rete.js Angular plugin 2 | ==== 3 | [![Made in Ukraine](https://img.shields.io/badge/made_in-ukraine-ffd700.svg?labelColor=0057b7)](https://stand-with-ukraine.pp.ua) 4 | [![Discord](https://img.shields.io/discord/1081223198055604244?color=%237289da&label=Discord)](https://discord.gg/cxSFkPZdsV) 5 | 6 | **Rete.js plugin** 7 | 8 | ## Key features 9 | 10 | - **Render elements**: visualize an elements such as nodes and connections using Angular components 11 | - **Customization**: modify appearance and behavior for a personalized workflow 12 | - **Presets**: predefined Angular components for different types of features 13 | - **[Classic](https://retejs.org/docs/guides/renderers/angular#connect-plugin)**: provides a classic visualization of nodes, connections, and controls 14 | - **[Context menu](https://retejs.org/docs/guides/context-menu#render-context-menu)**: provides a classic appearance for `rete-context-menu-plugin` 15 | - **[Minimap](https://retejs.org/docs/guides/minimap#render)**: provides a classic appearance for `rete-minimap-plugin` 16 | - **[Reroute](https://retejs.org/docs/guides/reroute#rendering)**: provides a classic appearance for `rete-connection-reroute-plugin` 17 | 18 | ## Getting Started 19 | 20 | Please refer to the [guide](https://retejs.org/docs/guides/renderers/angular) and [example](https://retejs.org/examples/angular/basic) using this plugin 21 | 22 | ## Contribution 23 | 24 | Please refer to the [Contribution](https://retejs.org/docs/contribution) guide 25 | 26 | ## License 27 | 28 | [MIT](https://github.com/retejs/angular-render-plugin/blob/main/LICENSE) 29 | -------------------------------------------------------------------------------- /ng-package.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | module.exports = { 4 | "$schema": "./node_modules/ng-packagr/ng-package.schema.json", 5 | "dest": "./dist", 6 | "lib": { 7 | "entryFile": "src/index.ts" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ng12/ng-nested.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | module.exports = { 4 | "$schema": "./node_modules/ng-packagr/ng-package.schema.json", 5 | "dest": "../dist/12", 6 | "lib": { 7 | "entryFile": "./src/index.ts" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ng12/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rete-angular-plugin-ng12", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "build": "ng-packagr -p ng-nested.js -c tsconfig.json" 7 | }, 8 | "devDependencies": { 9 | "@angular-devkit/build-angular": "~12.2.18", 10 | "@angular/cli": "~12.2.0", 11 | "@angular/common": "~12.2.0", 12 | "@angular/compiler": "^12.2.17", 13 | "@angular/compiler-cli": "~12.2.0", 14 | "@angular/core": "~12.2.0", 15 | "@angular/elements": "~12.2.0", 16 | "@types/estree": "^1.0.0", 17 | "@types/node": "^12.11.1", 18 | "ng-packagr": "^12.1.1", 19 | "rxjs": "~6.6.0", 20 | "tslib": "^2.3.0", 21 | "typescript": "~4.3.5", 22 | "zone.js": "~0.11.4" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ng12/src: -------------------------------------------------------------------------------- 1 | ../src -------------------------------------------------------------------------------- /ng12/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "angularCompilerOptions": { 4 | "fullTemplateTypeCheck": true, 5 | "strictInjectionParameters": true, 6 | "enableIvy": true, 7 | "compilationMode": "full" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ng13/ng-nested.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | module.exports = { 4 | "$schema": "./node_modules/ng-packagr/ng-package.schema.json", 5 | "dest": "../dist/13", 6 | "lib": { 7 | "entryFile": "./src/index.ts" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ng13/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rete-angular-plugin-ng13", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "build": "ng-packagr -p ng-nested.js -c tsconfig.json" 7 | }, 8 | "devDependencies": { 9 | "@angular-devkit/build-angular": "^13.3.10", 10 | "@angular/cli": "^13.3.10", 11 | "@angular/common": "^13.3.12", 12 | "@angular/compiler": "^13.3.12", 13 | "@angular/compiler-cli": "^13.3.12", 14 | "@angular/core": "^13.3.12", 15 | "@angular/elements": "^13.3.12", 16 | "@types/estree": "^1.0.0", 17 | "@types/node": "^12.11.1", 18 | "ng-packagr": "^13.3.1", 19 | "rxjs": "~6.6.0", 20 | "tslib": "^2.3.0", 21 | "typescript": "~4.6.4", 22 | "zone.js": "~0.11.4" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ng13/src: -------------------------------------------------------------------------------- 1 | ../src -------------------------------------------------------------------------------- /ng13/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "angularCompilerOptions": { 4 | "fullTemplateTypeCheck": true, 5 | "strictInjectionParameters": true, 6 | "enableIvy": true, 7 | "compilationMode": "full" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ng14/ng-nested.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | module.exports = { 4 | "$schema": "./node_modules/ng-packagr/ng-package.schema.json", 5 | "dest": "../dist/14", 6 | "lib": { 7 | "entryFile": "./src/index.ts" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ng14/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rete-angular-plugin-ng14", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "build": "ng-packagr -p ng-nested.js -c tsconfig.json" 7 | }, 8 | "devDependencies": { 9 | "@angular-devkit/build-angular": "^14.2.10", 10 | "@angular/cli": "^14.2.10", 11 | "@angular/common": "^14.2.12", 12 | "@angular/compiler": "^14.2.12", 13 | "@angular/compiler-cli": "^14.2.12", 14 | "@angular/core": "^14.2.12", 15 | "@angular/elements": "^14.2.12", 16 | "@types/estree": "^1.0.0", 17 | "@types/node": "^12.11.1", 18 | "ng-packagr": "^14.2.2", 19 | "rxjs": "~6.6.0", 20 | "tslib": "^2.3.0", 21 | "typescript": "~4.8.4", 22 | "zone.js": "~0.11.4" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ng14/src: -------------------------------------------------------------------------------- 1 | ../src -------------------------------------------------------------------------------- /ng14/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "angularCompilerOptions": { 4 | "fullTemplateTypeCheck": true, 5 | "strictInjectionParameters": true, 6 | "enableIvy": true, 7 | "compilationMode": "full" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ng15/ng-nested.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | module.exports = { 4 | "$schema": "./node_modules/ng-packagr/ng-package.schema.json", 5 | "dest": "../dist/15", 6 | "lib": { 7 | "entryFile": "./src/index.ts" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ng15/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rete-angular-plugin-ng15", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "build": "ng-packagr -p ng-nested.js -c tsconfig.json" 7 | }, 8 | "devDependencies": { 9 | "@angular-devkit/build-angular": "^15.2.0", 10 | "@angular/cli": "^15.2.0", 11 | "@angular/common": "^15.2.0", 12 | "@angular/compiler": "^15.2.0", 13 | "@angular/compiler-cli": "^15.2.0", 14 | "@angular/core": "^15.2.0", 15 | "@angular/elements": "^15.2.0", 16 | "@types/estree": "^1.0.0", 17 | "@types/node": "^12.11.1", 18 | "ng-packagr": "^15.2.2", 19 | "rxjs": "^7.8.0", 20 | "tslib": "^2.3.0", 21 | "typescript": "~4.8.4", 22 | "zone.js": "^0.12.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ng15/src: -------------------------------------------------------------------------------- 1 | ../src -------------------------------------------------------------------------------- /ng15/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "angularCompilerOptions": { 4 | "fullTemplateTypeCheck": true, 5 | "strictInjectionParameters": true, 6 | "enableIvy": true, 7 | "compilationMode": "full" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ng16/ng-nested.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | module.exports = { 4 | "$schema": "./node_modules/ng-packagr/ng-package.schema.json", 5 | "dest": "../dist/16", 6 | "lib": { 7 | "entryFile": "./.src/index.ts" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ng16/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rete-angular-plugin-ng16", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "prebuild": "node ../scripts/patch.js", 7 | "build": "ng-packagr -p ng-nested.js -c tsconfig.json" 8 | }, 9 | "devDependencies": { 10 | "@angular-devkit/build-angular": "^16.0.0", 11 | "@angular/cli": "^16.0.0", 12 | "@angular/common": "^16.0.0", 13 | "@angular/compiler": "^16.0.0", 14 | "@angular/compiler-cli": "^16.0.0", 15 | "@angular/core": "^16.0.0", 16 | "@angular/elements": "^16.0.0", 17 | "@types/estree": "^1.0.0", 18 | "@types/node": "^16.18.25", 19 | "ng-packagr": "^16.0.0", 20 | "rxjs": "^7.8.0", 21 | "tslib": "^2.3.0", 22 | "typescript": "~4.9.3", 23 | "zone.js": "^0.13.0" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ng16/patch/module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { NodeComponent } from './presets/classic/components/node/node.component'; 5 | import { ConnectionComponent } from './presets/classic/components/connection/connection.component'; 6 | import { SocketComponent } from './presets/classic/components/socket/socket.component'; 7 | import { ConnectionWrapperComponent } from './presets/classic/components/connection/connection-wrapper.component'; 8 | import { ControlComponent } from './presets/classic/components/control/control.component'; 9 | import { RefDirective } from './ref'; 10 | 11 | @NgModule({ 12 | declarations: [ 13 | RefDirective, 14 | NodeComponent, 15 | ConnectionComponent, 16 | ConnectionWrapperComponent, 17 | SocketComponent, 18 | ControlComponent 19 | ], 20 | imports: [ 21 | CommonModule 22 | ], 23 | exports: [ 24 | RefDirective, 25 | NodeComponent, 26 | ConnectionComponent, 27 | ConnectionWrapperComponent, 28 | SocketComponent, 29 | ControlComponent 30 | ] 31 | }) 32 | export class ReteModule { } 33 | -------------------------------------------------------------------------------- /ng16/patch/presets/context-menu/module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { ContextMenuComponent } from './components/menu/menu.component' 5 | import { ContextMenuSearchComponent } from './components/search/search.component' 6 | import { ContextMenuItemComponent } from './components/item/item.component' 7 | 8 | @NgModule({ 9 | declarations: [ 10 | ContextMenuComponent, 11 | ContextMenuSearchComponent, 12 | ContextMenuItemComponent 13 | ], 14 | imports: [ 15 | CommonModule 16 | ], 17 | exports: [ 18 | ContextMenuComponent, 19 | ContextMenuSearchComponent, 20 | ContextMenuItemComponent, 21 | ] 22 | }) 23 | export class ReteContextMenuModule {} 24 | -------------------------------------------------------------------------------- /ng16/patch/presets/minimap/module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { MinimapComponent } from './components/minimap/minimap.component'; 5 | import { MiniViewportComponent } from './components/mini-viewport/mini-viewport.component'; 6 | import { MiniNodeComponent } from './components/mini-node/mini-node.component'; 7 | 8 | @NgModule({ 9 | declarations: [ 10 | MinimapComponent, 11 | MiniViewportComponent, 12 | MiniNodeComponent 13 | ], 14 | imports: [ 15 | CommonModule 16 | ], 17 | exports: [ 18 | MinimapComponent, 19 | MiniViewportComponent, 20 | MiniNodeComponent 21 | ] 22 | }) 23 | export class ReteMinimapModule {} 24 | -------------------------------------------------------------------------------- /ng16/patch/presets/reroute/module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { PinsComponent } from './components/pins/pins.component'; 5 | import { PinComponent } from './components/pin/pin.component'; 6 | 7 | @NgModule({ 8 | declarations: [ 9 | PinsComponent, 10 | PinComponent, 11 | ], 12 | imports: [ 13 | CommonModule 14 | ], 15 | exports: [ 16 | PinsComponent, 17 | PinComponent, 18 | ] 19 | }) 20 | export class ReteRerouteModule {} 21 | -------------------------------------------------------------------------------- /ng16/src: -------------------------------------------------------------------------------- 1 | ../src -------------------------------------------------------------------------------- /ng16/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "angularCompilerOptions": { 4 | "fullTemplateTypeCheck": true, 5 | "strictInjectionParameters": true, 6 | "enableIvy": true, 7 | "compilationMode": "full" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ng17/ng-nested.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | module.exports = { 4 | "$schema": "./node_modules/ng-packagr/ng-package.schema.json", 5 | "dest": "../dist/17", 6 | "lib": { 7 | "entryFile": "./.src/index.ts" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ng17/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rete-angular-plugin-ng17", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "prebuild": "node ../scripts/patch.js", 7 | "build": "ng-packagr -p ng-nested.js -c tsconfig.json", 8 | "postbuild": "rm ../dist/17/.npmignore" 9 | }, 10 | "devDependencies": { 11 | "@angular-devkit/build-angular": "^17.0.0", 12 | "@angular/cli": "^17.0.0", 13 | "@angular/common": "^17.0.0", 14 | "@angular/compiler": "^17.0.0", 15 | "@angular/compiler-cli": "^17.0.0", 16 | "@angular/core": "^17.0.0", 17 | "@angular/elements": "^17.0.0", 18 | "@types/estree": "^1.0.0", 19 | "@types/node": "^16.18.25", 20 | "ng-packagr": "^17.0.0", 21 | "rxjs": "^7.8.0", 22 | "tslib": "^2.3.0", 23 | "typescript": "~5.2.2", 24 | "zone.js": "~0.14.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ng17/patch/module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { NodeComponent } from './presets/classic/components/node/node.component'; 5 | import { ConnectionComponent } from './presets/classic/components/connection/connection.component'; 6 | import { SocketComponent } from './presets/classic/components/socket/socket.component'; 7 | import { ConnectionWrapperComponent } from './presets/classic/components/connection/connection-wrapper.component'; 8 | import { ControlComponent } from './presets/classic/components/control/control.component'; 9 | import { RefDirective } from './ref'; 10 | 11 | @NgModule({ 12 | declarations: [ 13 | RefDirective, 14 | NodeComponent, 15 | ConnectionComponent, 16 | ConnectionWrapperComponent, 17 | SocketComponent, 18 | ControlComponent 19 | ], 20 | imports: [ 21 | CommonModule 22 | ], 23 | exports: [ 24 | RefDirective, 25 | NodeComponent, 26 | ConnectionComponent, 27 | ConnectionWrapperComponent, 28 | SocketComponent, 29 | ControlComponent 30 | ] 31 | }) 32 | export class ReteModule { } 33 | -------------------------------------------------------------------------------- /ng17/patch/presets/context-menu/module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { ContextMenuComponent } from './components/menu/menu.component' 5 | import { ContextMenuSearchComponent } from './components/search/search.component' 6 | import { ContextMenuItemComponent } from './components/item/item.component' 7 | 8 | @NgModule({ 9 | declarations: [ 10 | ContextMenuComponent, 11 | ContextMenuSearchComponent, 12 | ContextMenuItemComponent 13 | ], 14 | imports: [ 15 | CommonModule 16 | ], 17 | exports: [ 18 | ContextMenuComponent, 19 | ContextMenuSearchComponent, 20 | ContextMenuItemComponent, 21 | ] 22 | }) 23 | export class ReteContextMenuModule {} 24 | -------------------------------------------------------------------------------- /ng17/patch/presets/minimap/module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { MinimapComponent } from './components/minimap/minimap.component'; 5 | import { MiniViewportComponent } from './components/mini-viewport/mini-viewport.component'; 6 | import { MiniNodeComponent } from './components/mini-node/mini-node.component'; 7 | 8 | @NgModule({ 9 | declarations: [ 10 | MinimapComponent, 11 | MiniViewportComponent, 12 | MiniNodeComponent 13 | ], 14 | imports: [ 15 | CommonModule 16 | ], 17 | exports: [ 18 | MinimapComponent, 19 | MiniViewportComponent, 20 | MiniNodeComponent 21 | ] 22 | }) 23 | export class ReteMinimapModule {} 24 | -------------------------------------------------------------------------------- /ng17/patch/presets/reroute/module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { PinsComponent } from './components/pins/pins.component'; 5 | import { PinComponent } from './components/pin/pin.component'; 6 | 7 | @NgModule({ 8 | declarations: [ 9 | PinsComponent, 10 | PinComponent, 11 | ], 12 | imports: [ 13 | CommonModule 14 | ], 15 | exports: [ 16 | PinsComponent, 17 | PinComponent, 18 | ] 19 | }) 20 | export class ReteRerouteModule {} 21 | -------------------------------------------------------------------------------- /ng17/src: -------------------------------------------------------------------------------- 1 | ../src -------------------------------------------------------------------------------- /ng17/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "angularCompilerOptions": { 4 | "fullTemplateTypeCheck": true, 5 | "strictInjectionParameters": true, 6 | "enableIvy": true, 7 | "compilationMode": "full" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ng18/ng-nested.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | module.exports = { 4 | "$schema": "./node_modules/ng-packagr/ng-package.schema.json", 5 | "dest": "../dist/18", 6 | "lib": { 7 | "entryFile": "./.src/index.ts" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ng18/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rete-angular-plugin-ng18", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "prebuild": "node ../scripts/patch.js", 7 | "build": "ng-packagr -p ng-nested.js -c tsconfig.json", 8 | "postbuild": "rm ../dist/18/.npmignore" 9 | }, 10 | "devDependencies": { 11 | "@angular-devkit/build-angular": "^18.2.4", 12 | "@angular/cli": "^18.2.4", 13 | "@angular/common": "^18.2.4", 14 | "@angular/compiler": "^18.2.4", 15 | "@angular/compiler-cli": "^18.2.4", 16 | "@angular/core": "^18.2.4", 17 | "@angular/elements": "^18.2.4", 18 | "@types/estree": "^1.0.5", 19 | "@types/node": "^22.5.5", 20 | "ng-packagr": "^18.2.1", 21 | "rxjs": "^7.8.1", 22 | "tslib": "^2.7.0", 23 | "typescript": "~5.5.4", 24 | "zone.js": "~0.14.8" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ng18/patch/module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { NodeComponent } from './presets/classic/components/node/node.component'; 5 | import { ConnectionComponent } from './presets/classic/components/connection/connection.component'; 6 | import { SocketComponent } from './presets/classic/components/socket/socket.component'; 7 | import { ConnectionWrapperComponent } from './presets/classic/components/connection/connection-wrapper.component'; 8 | import { ControlComponent } from './presets/classic/components/control/control.component'; 9 | import { RefDirective } from './ref'; 10 | 11 | @NgModule({ 12 | declarations: [ 13 | RefDirective, 14 | NodeComponent, 15 | ConnectionComponent, 16 | ConnectionWrapperComponent, 17 | SocketComponent, 18 | ControlComponent 19 | ], 20 | imports: [ 21 | CommonModule 22 | ], 23 | exports: [ 24 | RefDirective, 25 | NodeComponent, 26 | ConnectionComponent, 27 | ConnectionWrapperComponent, 28 | SocketComponent, 29 | ControlComponent 30 | ] 31 | }) 32 | export class ReteModule { } 33 | -------------------------------------------------------------------------------- /ng18/patch/presets/context-menu/module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { ContextMenuComponent } from './components/menu/menu.component' 5 | import { ContextMenuSearchComponent } from './components/search/search.component' 6 | import { ContextMenuItemComponent } from './components/item/item.component' 7 | 8 | @NgModule({ 9 | declarations: [ 10 | ContextMenuComponent, 11 | ContextMenuSearchComponent, 12 | ContextMenuItemComponent 13 | ], 14 | imports: [ 15 | CommonModule 16 | ], 17 | exports: [ 18 | ContextMenuComponent, 19 | ContextMenuSearchComponent, 20 | ContextMenuItemComponent, 21 | ] 22 | }) 23 | export class ReteContextMenuModule {} 24 | -------------------------------------------------------------------------------- /ng18/patch/presets/minimap/module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { MinimapComponent } from './components/minimap/minimap.component'; 5 | import { MiniViewportComponent } from './components/mini-viewport/mini-viewport.component'; 6 | import { MiniNodeComponent } from './components/mini-node/mini-node.component'; 7 | 8 | @NgModule({ 9 | declarations: [ 10 | MinimapComponent, 11 | MiniViewportComponent, 12 | MiniNodeComponent 13 | ], 14 | imports: [ 15 | CommonModule 16 | ], 17 | exports: [ 18 | MinimapComponent, 19 | MiniViewportComponent, 20 | MiniNodeComponent 21 | ] 22 | }) 23 | export class ReteMinimapModule {} 24 | -------------------------------------------------------------------------------- /ng18/patch/presets/reroute/module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { PinsComponent } from './components/pins/pins.component'; 5 | import { PinComponent } from './components/pin/pin.component'; 6 | 7 | @NgModule({ 8 | declarations: [ 9 | PinsComponent, 10 | PinComponent, 11 | ], 12 | imports: [ 13 | CommonModule 14 | ], 15 | exports: [ 16 | PinsComponent, 17 | PinComponent, 18 | ] 19 | }) 20 | export class ReteRerouteModule {} 21 | -------------------------------------------------------------------------------- /ng18/src: -------------------------------------------------------------------------------- 1 | ../src -------------------------------------------------------------------------------- /ng18/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "angularCompilerOptions": { 4 | "fullTemplateTypeCheck": true, 5 | "strictInjectionParameters": true, 6 | "enableIvy": true, 7 | "compilationMode": "full" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ng19/ng-nested.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | module.exports = { 4 | "$schema": "./node_modules/ng-packagr/ng-package.schema.json", 5 | "dest": "../dist/19", 6 | "lib": { 7 | "entryFile": "./.src/index.ts" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ng19/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rete-angular-plugin-ng19", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "prebuild": "node ../scripts/patch.js", 7 | "build": "ng-packagr -p ng-nested.js -c tsconfig.json", 8 | "postbuild": "rm ../dist/19/.npmignore" 9 | }, 10 | "devDependencies": { 11 | "@angular-devkit/build-angular": "^19.0.6", 12 | "@angular/cli": "^19.0.6", 13 | "@angular/common": "^19.0.5", 14 | "@angular/compiler": "^19.0.5", 15 | "@angular/compiler-cli": "^19.0.5", 16 | "@angular/core": "^19.0.5", 17 | "@angular/elements": "^19.0.5", 18 | "@types/estree": "^1.0.5", 19 | "@types/node": "^22.5.5", 20 | "ng-packagr": "^19.0.1", 21 | "rxjs": "^7.8.1", 22 | "tslib": "^2.7.0", 23 | "typescript": "~5.5.4", 24 | "zone.js": "^0.15.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ng19/patch/module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { NodeComponent } from './presets/classic/components/node/node.component'; 5 | import { ConnectionComponent } from './presets/classic/components/connection/connection.component'; 6 | import { SocketComponent } from './presets/classic/components/socket/socket.component'; 7 | import { ConnectionWrapperComponent } from './presets/classic/components/connection/connection-wrapper.component'; 8 | import { ControlComponent } from './presets/classic/components/control/control.component'; 9 | import { RefDirective } from './ref'; 10 | 11 | @NgModule({ 12 | imports: [ 13 | CommonModule, 14 | RefDirective, 15 | NodeComponent, 16 | ConnectionComponent, 17 | ConnectionWrapperComponent, 18 | SocketComponent, 19 | ControlComponent 20 | ], 21 | exports: [ 22 | RefDirective, 23 | NodeComponent, 24 | ConnectionComponent, 25 | ConnectionWrapperComponent, 26 | SocketComponent, 27 | ControlComponent 28 | ] 29 | }) 30 | export class ReteModule { } 31 | -------------------------------------------------------------------------------- /ng19/patch/presets/classic/components/node/.node.component.ts: -------------------------------------------------------------------------------- 1 | // [imports] 2 | import { CommonModule } from '@angular/common'; 3 | import { RefDirective } from '../../../../ref'; 4 | // [component-directive] 5 | imports: [CommonModule, RefDirective], -------------------------------------------------------------------------------- /ng19/patch/presets/context-menu/components/item/.item.component.ts: -------------------------------------------------------------------------------- 1 | // [imports] 2 | import { CommonModule } from '@angular/common'; 3 | 4 | // [component-directive] 5 | imports: [CommonModule], -------------------------------------------------------------------------------- /ng19/patch/presets/context-menu/components/menu/.menu.component.ts: -------------------------------------------------------------------------------- 1 | // [imports] 2 | import { CommonModule } from '@angular/common'; 3 | import { ContextMenuSearchComponent } from '../search/search.component'; 4 | import { ContextMenuItemComponent } from '../item/item.component'; 5 | 6 | // [component-directive] 7 | imports: [CommonModule, ContextMenuSearchComponent, ContextMenuItemComponent], -------------------------------------------------------------------------------- /ng19/patch/presets/context-menu/module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { ContextMenuComponent } from './components/menu/menu.component' 5 | import { ContextMenuSearchComponent } from './components/search/search.component' 6 | import { ContextMenuItemComponent } from './components/item/item.component' 7 | 8 | @NgModule({ 9 | imports: [ 10 | CommonModule, 11 | ContextMenuComponent, 12 | ContextMenuSearchComponent, 13 | ContextMenuItemComponent 14 | ], 15 | exports: [ 16 | ContextMenuComponent, 17 | ContextMenuSearchComponent, 18 | ContextMenuItemComponent, 19 | ] 20 | }) 21 | export class ReteContextMenuModule {} 22 | -------------------------------------------------------------------------------- /ng19/patch/presets/minimap/components/minimap/.minimap.component.ts: -------------------------------------------------------------------------------- 1 | // [imports] 2 | import { CommonModule } from '@angular/common'; 3 | import { MiniNodeComponent } from '../mini-node/mini-node.component'; 4 | import { MiniViewportComponent } from '../mini-viewport/mini-viewport.component'; 5 | // [component-directive] 6 | imports: [CommonModule, MiniNodeComponent, MiniViewportComponent], -------------------------------------------------------------------------------- /ng19/patch/presets/minimap/module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { MinimapComponent } from './components/minimap/minimap.component'; 5 | import { MiniViewportComponent } from './components/mini-viewport/mini-viewport.component'; 6 | import { MiniNodeComponent } from './components/mini-node/mini-node.component'; 7 | 8 | @NgModule({ 9 | imports: [ 10 | CommonModule, 11 | MinimapComponent, 12 | MiniViewportComponent, 13 | MiniNodeComponent 14 | ], 15 | exports: [ 16 | MinimapComponent, 17 | MiniViewportComponent, 18 | MiniNodeComponent 19 | ] 20 | }) 21 | export class ReteMinimapModule {} 22 | -------------------------------------------------------------------------------- /ng19/patch/presets/reroute/components/pins/.pins.component.ts: -------------------------------------------------------------------------------- 1 | // [imports] 2 | import { CommonModule } from '@angular/common'; 3 | import { PinComponent } from '../pin/pin.component'; 4 | // [component-directive] 5 | imports: [CommonModule, PinComponent], -------------------------------------------------------------------------------- /ng19/patch/presets/reroute/module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { PinsComponent } from './components/pins/pins.component'; 5 | import { PinComponent } from './components/pin/pin.component'; 6 | 7 | @NgModule({ 8 | imports: [ 9 | CommonModule, 10 | PinsComponent, 11 | PinComponent, 12 | ], 13 | exports: [ 14 | PinsComponent, 15 | PinComponent, 16 | ] 17 | }) 18 | export class ReteRerouteModule {} 19 | -------------------------------------------------------------------------------- /ng19/src: -------------------------------------------------------------------------------- 1 | ../src -------------------------------------------------------------------------------- /ng19/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "angularCompilerOptions": { 4 | "fullTemplateTypeCheck": true, 5 | "strictInjectionParameters": true, 6 | "enableIvy": true, 7 | "compilationMode": "full" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rete-angular-plugin", 3 | "version": "2.3.2", 4 | "scripts": { 5 | "ng": "ng", 6 | "build": "npm run build:legacy && npm run build:12 && npm run build:13 && npm run build:14 && npm run build:15 && npm run build:16 && npm run build:17 && npm run build:18 && npm run build:19", 7 | "build:legacy": "ng-packagr -p ng-package.js", 8 | "build:12": "npm --prefix ng12 run build", 9 | "build:13": "npm --prefix ng13 run build", 10 | "build:14": "npm --prefix ng14 run build", 11 | "build:15": "npm --prefix ng15 run build", 12 | "build:16": "npm --prefix ng16 run build", 13 | "build:17": "npm --prefix ng17 run build", 14 | "build:18": "npm --prefix ng18 run build", 15 | "build:19": "npm --prefix ng19 run build", 16 | "postinstall": "npm i --prefix ng12 && npm i --prefix ng13 && npm i --prefix ng14 && npm i --prefix ng15 && npm i --prefix ng16 && npm i --prefix ng17 && npm i --prefix ng18 && npm i --prefix ng19", 17 | "doc": "rete doc" 18 | }, 19 | "author": "Vitaliy Stoliarov", 20 | "license": "MIT", 21 | "keywords": [ 22 | "angular", 23 | "plugin", 24 | "rete", 25 | "Rete.js" 26 | ], 27 | "homepage": "https://retejs.org", 28 | "repository": { 29 | "type": "git", 30 | "url": "git+https://github.com/retejs/angular-plugin.git" 31 | }, 32 | "bugs": { 33 | "url": "https://github.com/retejs/angular-plugin/issues" 34 | }, 35 | "peerDependencies": { 36 | "@angular/common": ">= 12 < 20", 37 | "@angular/core": ">= 12 < 20", 38 | "@angular/elements": ">= 12 < 20", 39 | "rete": "^2.0.1", 40 | "rete-area-plugin": "^2.0.0", 41 | "rete-render-utils": "^2.0.0", 42 | "rxjs": ">=6.6.0", 43 | "tslib": "^2.3.0", 44 | "zone.js": "~0.11.4 || ~0.12.0 || ~0.13.0 || ~0.14.0 || ~0.15.0" 45 | }, 46 | "devDependencies": { 47 | "@angular-devkit/build-angular": "~12.2.18", 48 | "@angular/cli": "~12.2.0", 49 | "@angular/common": "~12.2.0", 50 | "@angular/compiler": "^12.2.17", 51 | "@angular/compiler-cli": "~12.2.0", 52 | "@angular/core": "~12.2.0", 53 | "@angular/elements": "~12.2.0", 54 | "@types/estree": "^1.0.0", 55 | "@types/node": "^12.11.1", 56 | "ng-packagr": "^12.1.1", 57 | "rete": "^2.0.1", 58 | "rete-area-plugin": "^2.0.0", 59 | "rete-cli": "^1.0.2", 60 | "rete-render-utils": "^2.0.0", 61 | "rxjs": "~6.6.0", 62 | "tslib": "^2.3.0", 63 | "typescript": "~4.3.5", 64 | "zone.js": "~0.11.4" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /scripts/patch.js: -------------------------------------------------------------------------------- 1 | const { execSync } = require('child_process') 2 | const fs = require('fs') 3 | const { basename, dirname, join } = require('path') 4 | 5 | console.log('Patching source code...') 6 | 7 | execSync('mkdir -p .src && cp -r ./src/* .src', { stdio: 'inherit' }) 8 | 9 | const patchFiles = execSync('find ./patch -type f').toString().split('\n').filter(Boolean) 10 | 11 | for (const file of patchFiles) { 12 | const content = fs.readFileSync(file, { encoding: 'utf-8' }) 13 | const dir = dirname(file).replace(/^\.\/patch/, './.src') 14 | const filename = basename(file).replace(/^\./, '') 15 | const target = join(dir, filename) 16 | const isPartial = basename(file).startsWith('.') 17 | 18 | const nextContent = isPartial 19 | ? applyPartialPatch(target, partialPatchToData(content)) 20 | : content 21 | 22 | fs.writeFileSync(target, nextContent) 23 | } 24 | 25 | console.log('Source code patched!') 26 | 27 | /** 28 | * Applies a partial patch to a target file by replacing specific code segments annotated with IDs. 29 | * 30 | * @param {string} target - The path to the target file to be patched. 31 | * @param {Array<{id: string, code: string}>} parts - An array of objects, each containing an ID and the corresponding code segment. 32 | * @returns {string} The patched content of the target file. 33 | */ 34 | function applyPartialPatch(target, parts) { 35 | const content = fs.readFileSync(target, { encoding: 'utf-8' }) 36 | 37 | return parts.reduce((data, { id, code }) => { 38 | const match = data.match(new RegExp(`// \\[(${id})\\]`)) 39 | if (match) { 40 | return data.replace(match[0], code) 41 | } 42 | return data 43 | }, content) 44 | } 45 | 46 | /** 47 | * Parses the content of a partial patch file and extracts code segments annotated with specific IDs. 48 | * 49 | * @param {string} content - The content of the partial patch file. 50 | * @returns {Array<{id: string, code: string}>} An array of objects, each containing an ID and the corresponding code segment. 51 | * 52 | * @example 53 | * const content = ` 54 | * // [id1] 55 | * console.log('Hello, World!') 56 | * // [id2] 57 | * console.log('Goodbye, World!') 58 | * ` 59 | * const result = partialPatchToData(content) 60 | * // result will be: 61 | * // [ 62 | * // { id: 'id1', code: 'console.log(\'Hello, World!\')\n' }, 63 | * // { id: 'id2', code: 'console.log(\'Goodbye, World!\')\n' } 64 | * // ] 65 | */ 66 | function partialPatchToData(content) { 67 | return content.trim().split('\n').reduce((acc, line) => { 68 | const match = line.match(/\/\/ \[(.+?)\]/) 69 | if (match) { 70 | acc.push({ id: match[1], code: '' }) 71 | } else if (acc.length > 0) { 72 | acc[acc.length - 1].code += line + '\n' 73 | } 74 | return acc 75 | }, []) 76 | } 77 | -------------------------------------------------------------------------------- /src/core.ts: -------------------------------------------------------------------------------- 1 | import { Injector } from '@angular/core'; 2 | import { BaseSchemes, CanAssignSignal, Scope } from 'rete' 3 | import { createCustomElement } from '@angular/elements'; 4 | 5 | import { NgElement, NodeProps, Position, RenderSignal } from './types' 6 | import { RenderPreset } from './presets/types'; 7 | import { reflect } from './reflect'; 8 | 9 | type Item = { key: string, ngElement: NgElement } 10 | 11 | type Renderer = { 12 | get(element: HTMLElement): Item | undefined 13 | mount(element: HTMLElement, key: string, component: any, injector: Injector, props: Record): void 14 | update(item: Item, props: Record): void 15 | unmount(element: HTMLElement): void 16 | } 17 | function getRenderer(): Renderer { 18 | const elements = new WeakMap() 19 | 20 | return { 21 | get(element) { 22 | return elements.get(element) 23 | }, 24 | mount(element, key, component, injector, props) { 25 | // LIMITATION: If an element is remounted with the same identifier, the component cannot be replaced 26 | let CustomElement = customElements.get(key) 27 | 28 | if (!CustomElement) { 29 | CustomElement = createCustomElement(component, { injector }) 30 | customElements.define(key, CustomElement) 31 | } 32 | 33 | const ngElement = new CustomElement(injector) as NodeProps & NgElement & typeof props 34 | 35 | Object.keys(props).forEach(key => { 36 | ngElement[key] = props[key] 37 | }) 38 | 39 | element.appendChild(ngElement) 40 | elements.set(element, { key, ngElement }) 41 | }, 42 | update({ ngElement }, props) { 43 | Object.keys(props).forEach(key => { 44 | ngElement.ngElementStrategy.setInputValue(key, reflect(props[key])) 45 | }) 46 | }, 47 | unmount(element) { 48 | const existing = elements.get(element) 49 | 50 | if (existing) { 51 | existing.ngElement.remove() 52 | elements.delete(element) 53 | } 54 | } 55 | } 56 | } 57 | 58 | /** 59 | * Signals that can be emitted by the plugin 60 | * @priority 10 61 | */ 62 | export type Produces = 63 | | { type: 'connectionpath', data: { payload: Schemes['Connection'], path?: string, points: Position[] } } 64 | 65 | type Requires = 66 | | RenderSignal<'node', { payload: Schemes['Node'] }> 67 | | RenderSignal<'connection', { payload: Schemes['Connection'], start?: Position, end?: Position }> 68 | | { type: 'unmount', data: { element: HTMLElement } } 69 | 70 | /** 71 | * Angular plugin. Renders nodes, connections and other elements using React. 72 | * @priority 9 73 | * @emits connectionpath 74 | * @listens render 75 | * @listens unmount 76 | */ 77 | export class AngularPlugin> extends Scope, [Requires | T]> { 78 | presets: RenderPreset[] = [] 79 | renderer: Renderer 80 | owners = new WeakMap>() 81 | 82 | /** 83 | * @constructor 84 | * @param params Plugin properties 85 | * @param params.injector Angular's Injector instance 86 | */ 87 | constructor(private params: { injector: Injector }) { 88 | super('angular-render') 89 | this.renderer = getRenderer() 90 | 91 | this.addPipe(context => { 92 | if (!context || typeof context !== 'object' || !('type' in context)) return context 93 | if (context.type === 'unmount') { 94 | this.unmount(context.data.element) 95 | } else if (context.type === 'render') { 96 | if ('filled' in context.data && context.data.filled) { 97 | return context 98 | } 99 | if (this.mount(context.data.element, context)) { 100 | return { 101 | ...context, 102 | data: { 103 | ...context.data, 104 | filled: true 105 | } 106 | } as typeof context 107 | } 108 | } 109 | return context 110 | }) 111 | } 112 | 113 | setParent(scope: Scope | T>): void { 114 | super.setParent(scope) 115 | 116 | this.presets.forEach(preset => { 117 | if (preset.attach) preset.attach(this) 118 | }) 119 | } 120 | 121 | 122 | private unmount(element: HTMLElement) { 123 | this.owners.delete(element) 124 | this.renderer.unmount(element) 125 | } 126 | 127 | private mount(element: HTMLElement, context: Requires) { 128 | const existing = this.renderer.get(element) 129 | 130 | if (existing) { 131 | this.presets.forEach(preset => { 132 | if (this.owners.get(element) !== preset) return 133 | const result = preset.update(context as unknown as Extract, this) 134 | 135 | if (result) { 136 | this.renderer.update(existing, result) 137 | } 138 | }) 139 | return true 140 | } 141 | 142 | for (const preset of this.presets) { 143 | const result = preset.mount(context as unknown as Extract, this) 144 | 145 | if (!result) continue 146 | 147 | const { key, component, props } = result 148 | 149 | this.renderer.mount(element, key, component, this.params.injector, props) 150 | 151 | this.owners.set(element, preset) 152 | return true 153 | } 154 | return 155 | } 156 | 157 | /** 158 | * Adds a preset to the plugin. 159 | * @param preset Preset that can render nodes, connections and other elements. 160 | */ 161 | public addPreset(preset: RenderPreset extends true ? K : 'Cannot apply preset. Provided signals are not compatible'>) { 162 | const local = preset as unknown as RenderPreset 163 | 164 | if (local.attach) local.attach(this) 165 | this.presets.push(local) 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * as Presets from './presets' 2 | 3 | export { AngularArea2D } from './presets/classic/types' 4 | export { ReteModule } from './module' 5 | export { RenderPreset } from './presets/types' 6 | export { ReteContextMenuModule } from './presets/context-menu/module' 7 | export { ReteMinimapModule } from './presets/minimap/module' 8 | export { ReteRerouteModule } from './presets/reroute/module' 9 | export * from './presets/classic/components' 10 | export * from './presets/context-menu/components' 11 | export * from './presets/minimap/components' 12 | export * from './presets/reroute/components' 13 | export * from './ref' 14 | export * from './core' 15 | -------------------------------------------------------------------------------- /src/module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { NodeComponent } from './presets/classic/components/node/node.component'; 5 | import { ConnectionComponent } from './presets/classic/components/connection/connection.component'; 6 | import { SocketComponent } from './presets/classic/components/socket/socket.component'; 7 | import { ConnectionWrapperComponent } from './presets/classic/components/connection/connection-wrapper.component'; 8 | import { ControlComponent } from './presets/classic/components/control/control.component'; 9 | import { RefDirective } from './ref'; 10 | 11 | @NgModule({ 12 | declarations: [ 13 | RefDirective, 14 | NodeComponent, 15 | ConnectionComponent, 16 | ConnectionWrapperComponent, 17 | SocketComponent, 18 | ControlComponent 19 | ], 20 | imports: [ 21 | CommonModule 22 | ], 23 | exports: [ 24 | RefDirective, 25 | NodeComponent, 26 | ConnectionComponent, 27 | ConnectionWrapperComponent, 28 | SocketComponent, 29 | ControlComponent 30 | ], 31 | entryComponents: [ 32 | NodeComponent, 33 | ConnectionComponent, 34 | ConnectionWrapperComponent, 35 | SocketComponent, 36 | ControlComponent 37 | ] 38 | }) 39 | export class ReteModule { } 40 | -------------------------------------------------------------------------------- /src/presets/classic/components/connection/connection-wrapper.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, OnInit, ChangeDetectorRef, OnChanges, ViewContainerRef, ComponentFactoryResolver, ComponentRef } from '@angular/core'; 2 | import { ClassicPreset } from 'rete'; 3 | import { Position } from '../../../../types'; 4 | 5 | type PositionWatcher = (cb: (value: Position) => void) => (() => void) 6 | 7 | @Component({ 8 | template: '' 9 | }) 10 | export class ConnectionWrapperComponent implements OnInit, OnChanges{ 11 | @Input() data!: ClassicPreset.Connection; 12 | @Input() start!: Position | PositionWatcher 13 | @Input() end!: Position | PositionWatcher 14 | @Input() path!: (start: Position, end: Position) => Promise 15 | @Input() rendered!: any 16 | @Input() connectionComponent!: any 17 | 18 | ref!: ComponentRef 19 | 20 | startOb: Position | null = null 21 | get _start(): Position | null { 22 | return 'x' in this.start ? this.start : this.startOb 23 | } 24 | endOb: Position | null = null 25 | get _end(): Position | null { 26 | return 'x' in this.end ? this.end : this.endOb 27 | } 28 | _path: string 29 | 30 | constructor(private cdr: ChangeDetectorRef, public viewContainerRef: ViewContainerRef, private componentFactoryResolver: ComponentFactoryResolver) { 31 | this.cdr.detach() 32 | } 33 | 34 | async ngOnChanges(): Promise { 35 | await this.updatePath() 36 | requestAnimationFrame(() => this.rendered()) 37 | this.cdr.detectChanges() 38 | this.update() 39 | } 40 | 41 | async updatePath() { 42 | if (this._start && this._end) { 43 | this._path = await this.path(this._start, this._end) 44 | } 45 | } 46 | 47 | ngOnInit() { 48 | if (typeof this.start === 'function') { 49 | this.start(async value => { 50 | this.startOb = value 51 | await this.updatePath() 52 | this.cdr.detectChanges() 53 | this.update() 54 | }) 55 | } 56 | if (typeof this.end === 'function') { 57 | this.end(async value => { 58 | this.endOb = value 59 | await this.updatePath() 60 | this.cdr.detectChanges() 61 | this.update() 62 | }) 63 | } 64 | const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.connectionComponent); 65 | this.viewContainerRef.clear(); 66 | 67 | this.ref = this.viewContainerRef.createComponent(componentFactory); 68 | this.update() 69 | } 70 | 71 | update() { 72 | this.ref.instance.data = this.data 73 | this.ref.instance.start = this._start 74 | this.ref.instance.end = this._end 75 | this.ref.instance.path = this._path 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/presets/classic/components/connection/connection.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/presets/classic/components/connection/connection.component.sass: -------------------------------------------------------------------------------- 1 | @use "sass:math" 2 | @import "../../vars" 3 | 4 | svg 5 | overflow: visible !important 6 | position: absolute 7 | pointer-events: none 8 | width: 9999px 9 | height: 9999px 10 | path 11 | fill: none 12 | stroke-width: 5px 13 | stroke: steelblue 14 | pointer-events: auto 15 | -------------------------------------------------------------------------------- /src/presets/classic/components/connection/connection.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | import { ClassicPreset } from 'rete'; 3 | import { classicConnectionPath } from 'rete-render-utils' 4 | import { Position } from '../../../../types'; 5 | 6 | 7 | @Component({ 8 | selector: 'connection', 9 | templateUrl: './connection.component.html', 10 | styleUrls: ['./connection.component.sass'] 11 | }) 12 | export class ConnectionComponent { 13 | @Input() data!: ClassicPreset.Connection; 14 | @Input() start: Position 15 | @Input() end: Position 16 | @Input() path: string 17 | } 18 | -------------------------------------------------------------------------------- /src/presets/classic/components/control/control.component.html: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /src/presets/classic/components/control/control.component.sass: -------------------------------------------------------------------------------- 1 | @import "../../vars" 2 | 3 | input 4 | width: 100% 5 | border-radius: 30px 6 | background-color: white 7 | padding: 2px 6px 8 | border: 1px solid #999 9 | font-size: 110% 10 | box-sizing: border-box 11 | -------------------------------------------------------------------------------- /src/presets/classic/components/control/control.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, OnChanges, SimpleChanges, ChangeDetectorRef, HostListener } from '@angular/core'; 2 | import { ClassicPreset } from 'rete'; 3 | 4 | @Component({ 5 | templateUrl: `./control.component.html`, 6 | styleUrls: ['./control.component.sass'] 7 | }) 8 | export class ControlComponent implements OnChanges { 9 | @Input() data!: ClassicPreset.InputControl; 10 | @Input() rendered!: any; 11 | 12 | 13 | @HostListener('pointerdown', ['$event']) 14 | public pointerdown(event: any) { 15 | event.stopPropagation(); 16 | } 17 | 18 | constructor(private cdr: ChangeDetectorRef) { 19 | this.cdr.detach() 20 | } 21 | 22 | ngOnChanges(changes: SimpleChanges): void { 23 | const seed = changes['seed'] 24 | const data = changes['data'] 25 | 26 | if ((seed && seed.currentValue !== seed.previousValue) 27 | || (data && data.currentValue !== data.previousValue)) { 28 | this.cdr.detectChanges() 29 | } 30 | requestAnimationFrame(() => this.rendered()) 31 | } 32 | 33 | onChange(e: Event) { 34 | const target = e.target as HTMLInputElement 35 | const value = (this.data.type === 'number' 36 | ? +target.value 37 | : target.value) as ClassicPreset.InputControl['value'] 38 | 39 | this.data.setValue(value) 40 | this.cdr.detectChanges() 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/presets/classic/components/index.ts: -------------------------------------------------------------------------------- 1 | 2 | export * from './node/node.component' 3 | export * from './connection/connection-wrapper.component' 4 | export * from './connection/connection.component' 5 | export * from './control/control.component' 6 | export * from './socket/socket.component' 7 | -------------------------------------------------------------------------------- /src/presets/classic/components/node/node.component.html: -------------------------------------------------------------------------------- 1 |
{{data.label}}
2 |
3 |
{{output.value?.label}}
4 |
11 |
12 |
20 |
21 |
28 |
{{input.value?.label}}
29 |
37 |
38 | -------------------------------------------------------------------------------- /src/presets/classic/components/node/node.component.sass: -------------------------------------------------------------------------------- 1 | @use "sass:math" 2 | @import "../../vars" 3 | 4 | :host 5 | display: block 6 | background: $node-color 7 | border: 2px solid #4e58bf 8 | border-radius: 10px 9 | cursor: pointer 10 | box-sizing: border-box 11 | width: $node-width 12 | height: auto 13 | padding-bottom: 6px 14 | position: relative 15 | user-select: none 16 | line-height: initial 17 | font-family: Arial 18 | 19 | &:hover 20 | background: lighten($node-color,4%) 21 | &.selected 22 | background: $node-color-selected 23 | border-color: #e3c000 24 | .title 25 | color: white 26 | font-family: sans-serif 27 | font-size: 18px 28 | padding: 8px 29 | .output 30 | text-align: right 31 | .input 32 | text-align: left 33 | .input-title,.output-title 34 | vertical-align: middle 35 | color: white 36 | display: inline-block 37 | font-family: sans-serif 38 | font-size: 14px 39 | margin: $socket-margin 40 | line-height: $socket-size 41 | &[hidden] 42 | display: none 43 | .output-socket 44 | text-align: right 45 | margin-right: -(math.div($socket-size, 2) + $socket-margin) 46 | display: inline-block 47 | .input-socket 48 | text-align: left 49 | margin-left: -(math.div($socket-size, 2) + $socket-margin) 50 | display: inline-block 51 | .input-control 52 | z-index: 1 53 | width: calc(100% - #{$socket-size + 2*$socket-margin}) 54 | vertical-align: middle 55 | display: inline-block 56 | .control 57 | padding: $socket-margin math.div($socket-size, 2) + $socket-margin 58 | -------------------------------------------------------------------------------- /src/presets/classic/components/node/node.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, HostBinding, ChangeDetectorRef, OnChanges } from '@angular/core'; 2 | import { ClassicPreset as Classic } from 'rete'; 3 | import { KeyValue } from '@angular/common'; 4 | // [imports] 5 | 6 | type NodeExtraData = { width?: number, height?: number } 7 | type SortValue = (N['controls'] | N['inputs'] | N['outputs'])[string] 8 | 9 | @Component({ 10 | // [component-directive] 11 | templateUrl: './node.component.html', 12 | styleUrls: ['./node.component.sass'], 13 | host: { 14 | 'data-testid': 'node' 15 | } 16 | }) 17 | export class NodeComponent implements OnChanges { 18 | @Input() data!: Classic.Node & NodeExtraData; 19 | @Input() emit!: (data: any) => void 20 | @Input() rendered!: () => void 21 | 22 | seed = 0 23 | 24 | @HostBinding('style.width.px') get width() { 25 | return this.data.width 26 | } 27 | 28 | @HostBinding('style.height.px') get height() { 29 | return this.data.height 30 | } 31 | 32 | @HostBinding('class.selected') get selected() { 33 | return this.data.selected 34 | } 35 | 36 | constructor(private cdr: ChangeDetectorRef) { 37 | this.cdr.detach() 38 | } 39 | 40 | ngOnChanges(): void { 41 | this.cdr.detectChanges() 42 | requestAnimationFrame(() => this.rendered()) 43 | this.seed++ // force render sockets 44 | } 45 | 46 | sortByIndex>>(a: I, b: I) { 47 | const ai = a.value?.index || 0 48 | const bi = b.value?.index || 0 49 | 50 | return ai - bi 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/presets/classic/components/socket/socket.component.sass: -------------------------------------------------------------------------------- 1 | @import "../../vars" 2 | 3 | :host 4 | display: inline-block 5 | cursor: pointer 6 | border: 1px solid white 7 | border-radius: $socket-size/2.0 8 | width: $socket-size 9 | height: $socket-size 10 | margin: $socket-margin 11 | vertical-align: middle 12 | background: $socket-color 13 | z-index: 2 14 | box-sizing: border-box 15 | &:hover 16 | border-width: 4px 17 | &.multiple 18 | border-color: yellow 19 | &.output 20 | margin-right: - $socket-size / 2 21 | &.input 22 | margin-left: - $socket-size / 2 23 | -------------------------------------------------------------------------------- /src/presets/classic/components/socket/socket.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, HostBinding, AfterViewChecked, ChangeDetectorRef, OnChanges } from '@angular/core'; 2 | 3 | @Component({ 4 | template: ``, 5 | styleUrls: ['./socket.component.sass'] 6 | }) 7 | export class SocketComponent implements OnChanges { 8 | @Input() data!: any; 9 | @Input() rendered!: any; 10 | 11 | 12 | @HostBinding('title') get title() { 13 | return this.data.name 14 | } 15 | 16 | constructor(private cdr: ChangeDetectorRef) { 17 | this.cdr.detach() 18 | } 19 | 20 | ngOnChanges(): void { 21 | this.cdr.detectChanges() 22 | requestAnimationFrame(() => this.rendered()) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/presets/classic/index.ts: -------------------------------------------------------------------------------- 1 | import { Type } from '@angular/core'; 2 | import { ClassicPreset, getUID, Scope } from 'rete'; 3 | import { classicConnectionPath, loopConnectionPath, SocketPositionWatcher, getDOMSocketPosition } from 'rete-render-utils'; 4 | import { AngularArea2D, ClassicScheme, ExtractPayload } from './types'; 5 | import { NodeComponent } from './components/node/node.component'; 6 | import { SocketComponent } from './components/socket/socket.component'; 7 | import { ControlComponent } from './components/control/control.component'; 8 | import { ConnectionComponent } from './components/connection/connection.component'; 9 | import { ConnectionWrapperComponent } from './components/connection/connection-wrapper.component'; 10 | import { Position } from '../../types'; 11 | import { RenderPreset } from '../types' 12 | 13 | type AngularComponent = Type 14 | type CustomizationProps = { 15 | node?: (data: ExtractPayload) => AngularComponent | null 16 | connection?: (data: ExtractPayload) => AngularComponent | null 17 | socket?: (data: ExtractPayload) => AngularComponent | null 18 | control?: (data: ExtractPayload) => AngularComponent | null 19 | } 20 | 21 | type ClassicProps = { 22 | socketPositionWatcher?: SocketPositionWatcher> 23 | customize?: CustomizationProps 24 | } 25 | 26 | /** 27 | * Classic preset for rendering nodes, connections, controls and sockets. 28 | */ 29 | export function setup>( 30 | props?: ClassicProps 31 | ): RenderPreset { 32 | const positionWatcher = typeof props?.socketPositionWatcher === 'undefined' 33 | ? getDOMSocketPosition() // fix Type instantiation is excessively deep and possibly infinite. 34 | : props?.socketPositionWatcher 35 | const { node, connection, socket, control } = props?.customize || {} 36 | 37 | 38 | return { 39 | attach(plugin) { 40 | positionWatcher.attach(plugin as unknown as Scope) 41 | }, 42 | update(context) { 43 | const data = context.data.payload 44 | 45 | if (context.data.type === 'connection') { 46 | const { start, end } = context.data 47 | 48 | return { 49 | data, 50 | ...(start ? { start } : {}), 51 | ...(end ? { end } : {}), 52 | } 53 | } 54 | return { data } 55 | }, 56 | mount(context, plugin) { 57 | const parent = plugin.parentScope() 58 | const emit = parent.emit.bind(parent) 59 | const rendered = () => { 60 | emit({ type: 'rendered', data: context.data }) 61 | } 62 | 63 | if (context.data.type === 'node') { 64 | const component = node ? node(context.data) : NodeComponent 65 | 66 | return { 67 | key: `node-${context.data.payload.id}`, 68 | component, 69 | props: { 70 | data: context.data.payload, 71 | emit, 72 | rendered 73 | } 74 | } 75 | } 76 | if (context.data.type === 'connection') { 77 | const component = connection ? connection(context.data) : ConnectionComponent 78 | const id = context.data.payload.id 79 | const { sourceOutput, targetInput, source, target } = context.data.payload 80 | const { start, end, payload } = context.data 81 | 82 | return { 83 | key: `connection-${id}`, 84 | component: ConnectionWrapperComponent, 85 | props: { 86 | connectionComponent: component, 87 | data: payload, 88 | start: start || ((change: any) => positionWatcher.listen(source, 'output', sourceOutput, change)), 89 | end: end || ((change: any) => positionWatcher.listen(target, 'input', targetInput, change)), 90 | path: async (start, end) => { 91 | const response = await plugin.emit({ type: 'connectionpath', data: { payload, points: [start, end] } }) 92 | 93 | if (!response) return '' 94 | 95 | const { path, points } = response.data 96 | const curvature = 0.3 97 | 98 | if (!path && points.length !== 2) throw new Error('cannot render connection with a custom number of points') 99 | if (!path) return payload.isLoop 100 | ? loopConnectionPath(points as [Position, Position], curvature, 120) 101 | : classicConnectionPath(points as [Position, Position], curvature) 102 | 103 | return path 104 | }, 105 | rendered 106 | } 107 | } 108 | } 109 | if (context.data.type === 'socket') { 110 | const component = socket ? socket(context.data) : SocketComponent 111 | 112 | return { 113 | key: `socket-${getUID()}`, 114 | component, 115 | props: { 116 | data: context.data.payload, 117 | rendered 118 | } 119 | } 120 | } 121 | if (context.data.type === 'control') { 122 | const component = control 123 | ? control(context.data) 124 | : ( 125 | context.data.payload instanceof ClassicPreset.InputControl 126 | ? ControlComponent 127 | : null 128 | ) 129 | 130 | if (component) { 131 | return { 132 | key: `control-${context.data.payload.id}`, 133 | component, 134 | props: { 135 | data: context.data.payload, 136 | rendered 137 | } 138 | } 139 | } 140 | return 141 | } 142 | return 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/presets/classic/types.ts: -------------------------------------------------------------------------------- 1 | import { NodeId, ClassicPreset as Classic, GetSchemes } from 'rete' 2 | import { Position, RenderSignal } from '../../types' 3 | 4 | type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) 5 | ? I 6 | : never 7 | type GetControls< 8 | T extends ClassicScheme['Node'], 9 | Intersection = UnionToIntersection 10 | > = Intersection[keyof Intersection] extends Classic.Control ? Intersection[keyof Intersection] : Classic.Control 11 | type GetSockets< 12 | T extends ClassicScheme['Node'], 13 | Intersection = UnionToIntersection, 14 | Union = Exclude 15 | > = Union extends { socket: any } ? Union['socket'] : Classic.Socket 16 | 17 | 18 | export type Side = 'input' | 'output' 19 | export type AngularRenderData = 20 | | { 21 | element: HTMLElement, 22 | type: 'socket', 23 | payload: GetSockets 24 | nodeId: NodeId 25 | side: Side 26 | key: string 27 | } 28 | | { 29 | element: HTMLElement, 30 | type: 'control', 31 | payload: GetControls 32 | } 33 | 34 | export type ExtractPayload = Extract, { type: 'render', data: { type: K } }>['data'] 35 | export type AngularArea2D = 36 | | RenderSignal<'node', { payload: T['Node'] }> 37 | | RenderSignal<'connection', { payload: T['Connection'], start?: Position, end?: Position }> 38 | | RenderSignal<'socket', { 39 | payload: GetSockets 40 | nodeId: NodeId 41 | side: Side 42 | key: string 43 | }> 44 | | RenderSignal<'control', { 45 | payload: GetControls 46 | }> 47 | | { type: 'unmount', data: { element: HTMLElement } } 48 | 49 | export type ClassicScheme = GetSchemes & { isLoop?: boolean }> 50 | -------------------------------------------------------------------------------- /src/presets/classic/vars.sass: -------------------------------------------------------------------------------- 1 | $node-color: rgba(110,136,255,0.8) 2 | $node-color-selected: #ffd92c 3 | $group-color: rgba(15,80,255,0.2) 4 | $group-handler-size: 40px 5 | $group-handler-offset: -10px 6 | $socket-size: 24px 7 | $socket-margin: 6px 8 | $socket-color: #96b38a 9 | $node-width: 180px -------------------------------------------------------------------------------- /src/presets/context-menu/block.sass: -------------------------------------------------------------------------------- 1 | @use "sass:math" 2 | @import "./context-vars" 3 | 4 | .block 5 | display: block 6 | color: #fff 7 | padding: 4px 8 | border-bottom: 1px solid $context-color-dark 9 | background-color: $context-color 10 | cursor: pointer 11 | width: 100% 12 | position: relative 13 | 14 | &:first-child 15 | border-top-left-radius: $context-menu-round 16 | border-top-right-radius: $context-menu-round 17 | 18 | &:last-child 19 | border-bottom-left-radius: $context-menu-round 20 | border-bottom-right-radius: $context-menu-round 21 | 22 | &:hover 23 | background-color: $context-color-light 24 | -------------------------------------------------------------------------------- /src/presets/context-menu/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './item/item.component' 2 | export * from './menu/menu.component' 3 | export * from './search/search.component' 4 | -------------------------------------------------------------------------------- /src/presets/context-menu/components/item/item.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | {{ item.label }} 5 | 6 |
7 | -------------------------------------------------------------------------------- /src/presets/context-menu/components/item/item.component.sass: -------------------------------------------------------------------------------- 1 | @import "../../context-vars" 2 | 3 | :host(.hasSubitems) 4 | &:after 5 | content: '►' 6 | position: absolute 7 | opacity: 0.6 8 | right: 5px 9 | top: 5px 10 | font-family: initial 11 | 12 | .subitems 13 | position: absolute 14 | top: 0 15 | left: 100% 16 | width: $width 17 | -------------------------------------------------------------------------------- /src/presets/context-menu/components/item/item.component.ts: -------------------------------------------------------------------------------- 1 | import { ChangeDetectorRef, Component, EventEmitter, HostBinding, HostListener, Input, Output } from '@angular/core'; 2 | import { Item } from '../../types'; 3 | import { debounce } from '../../debounce'; 4 | // [imports] 5 | 6 | @Component({ 7 | // [component-directive] 8 | selector: 'context-menu-item', 9 | templateUrl: './item.component.html', 10 | styleUrls: ['./item.component.sass', '../../block.sass'], 11 | host: { 12 | 'data-testid': 'context-menu-item' 13 | } 14 | }) 15 | export class ContextMenuItemComponent { 16 | @Input() subitems?: Item[] 17 | @Input() delay!: number 18 | @Output() select = new EventEmitter(); 19 | @Output() hide = new EventEmitter(); 20 | 21 | @HostBinding('class.block') get block() { return true } 22 | @HostBinding('class.hasSubitems') get hasSubitems() { return this.subitems } 23 | 24 | @HostListener('click', ['$event']) click(event: MouseEvent) { 25 | event.stopPropagation() 26 | this.select.emit() 27 | this.hide.emit() 28 | } 29 | @HostListener('pointerdown', ['$event']) pointerdown(event: PointerEvent) { 30 | event.stopPropagation() 31 | } 32 | @HostListener('wheel', ['$event']) wheel(event: MouseEvent) { 33 | event.stopPropagation() 34 | } 35 | 36 | hideSubitems = debounce(() => { 37 | this.visibleSubitems = false 38 | this.cdr.detectChanges() 39 | }) 40 | visibleSubitems = false 41 | 42 | @HostListener('pointerover') pointerover() { 43 | this.hideSubitems.cancel() 44 | this.visibleSubitems = true 45 | this.cdr.detectChanges() 46 | } 47 | @HostListener('pointerleave') pointerleave() { 48 | this.hideSubitems.call(this.delay) 49 | this.cdr.detectChanges() 50 | } 51 | 52 | constructor(private cdr: ChangeDetectorRef) { 53 | this.cdr.detach() 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/presets/context-menu/components/menu/menu.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | 7 | {{ item.label }} 8 | 9 | -------------------------------------------------------------------------------- /src/presets/context-menu/components/menu/menu.component.sass: -------------------------------------------------------------------------------- 1 | @use "sass:math" 2 | @import "../../context-vars" 3 | 4 | :host 5 | display: block 6 | padding: 10px 7 | width: $width 8 | margin-top: -20px 9 | margin-left: - math.div($width, 2) 10 | -------------------------------------------------------------------------------- /src/presets/context-menu/components/menu/menu.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, ChangeDetectorRef, OnChanges, OnDestroy, HostListener, HostBinding } from '@angular/core'; 2 | import { Item } from '../../types'; 3 | import { debounce } from '../../debounce'; 4 | // [imports] 5 | 6 | @Component({ 7 | // [component-directive] 8 | templateUrl: './menu.component.html', 9 | styleUrls: ['./menu.component.sass', '../../block.sass'], 10 | host: { 11 | 'data-testid': 'context-menu' 12 | } 13 | }) 14 | export class ContextMenuComponent implements OnChanges, OnDestroy { 15 | @Input() items!: Item[] 16 | @Input() delay!: number 17 | @Input() searchBar?: boolean 18 | @Input() onHide!: () => void 19 | @Input() rendered!: () => void 20 | 21 | public filter: string = '' 22 | 23 | hide = debounce(() => { 24 | this.onHide() 25 | this.cdr.detectChanges() 26 | }) 27 | 28 | @HostBinding('attr.rete-context-menu') customAttribute = '' 29 | 30 | @HostListener('mouseover') pointerover() { 31 | this.hide.cancel() 32 | this.cdr.detectChanges() 33 | } 34 | @HostListener('mouseleave') pointerleave() { 35 | this.hide.call(this.delay) 36 | this.cdr.detectChanges() 37 | } 38 | 39 | constructor(private cdr: ChangeDetectorRef) { 40 | this.cdr.detach() 41 | } 42 | 43 | ngOnChanges(): void { 44 | this.cdr.detectChanges() 45 | requestAnimationFrame(() => this.rendered()) 46 | } 47 | 48 | setFilter(value: string) { 49 | this.filter = value 50 | this.cdr.detectChanges() 51 | } 52 | 53 | getItems() { 54 | const filterRegexp = new RegExp(this.filter, 'i') 55 | const filteredList = this.items.filter(item => ( 56 | item.label.match(filterRegexp) 57 | )) 58 | 59 | return filteredList 60 | } 61 | 62 | ngOnDestroy(): void { 63 | if (this.hide) this.hide.cancel() 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/presets/context-menu/components/search/search.component.html: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /src/presets/context-menu/components/search/search.component.sass: -------------------------------------------------------------------------------- 1 | .search 2 | color: white 3 | padding: 1px 8px 4 | border: 1px solid white 5 | border-radius: 10px 6 | font-size: 16px 7 | font-family: serif 8 | width: 100% 9 | box-sizing: border-box 10 | background: transparent 11 | -------------------------------------------------------------------------------- /src/presets/context-menu/components/search/search.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, EventEmitter, Input, Output } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'context-menu-search', 5 | templateUrl: './search.component.html', 6 | styleUrls: ['./search.component.sass'] 7 | }) 8 | export class ContextMenuSearchComponent { 9 | @Input() value!: string 10 | @Output() update = new EventEmitter() 11 | } 12 | -------------------------------------------------------------------------------- /src/presets/context-menu/context-vars.sass: -------------------------------------------------------------------------------- 1 | // this file cannot be called vars.sass, because then it cannot be imported, probably due to a collision with a file from a classic preset 2 | 3 | $context-color: rgba(110,136,255,0.8) 4 | $context-color-light: rgba(130, 153, 255, 0.8) 5 | $context-color-dark: rgba(69, 103, 255, 0.8) 6 | $context-menu-round: 5px 7 | $width: 120px 8 | 9 | $test: 23px 10 | -------------------------------------------------------------------------------- /src/presets/context-menu/debounce.ts: -------------------------------------------------------------------------------- 1 | export function debounce(cb: () => void) { 2 | return { 3 | timeout: null as null | number, 4 | cancel() { 5 | if (this.timeout) { 6 | window.clearTimeout(this.timeout) 7 | this.timeout = null 8 | } 9 | }, 10 | call(delay: number) { 11 | this.timeout = window.setTimeout(() => { 12 | cb() 13 | }, delay) 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/presets/context-menu/index.ts: -------------------------------------------------------------------------------- 1 | import { BaseSchemes } from 'rete'; 2 | 3 | import { ContextMenuRender } from './types'; 4 | import { ContextMenuComponent } from './components/menu/menu.component'; 5 | import { RenderPreset } from '../types'; 6 | 7 | /** 8 | * Preset for rendering context menu. 9 | */ 10 | export function setup(props?: { delay?: number }): RenderPreset { 11 | const delay = typeof props?.delay === 'undefined' ? 1000 : props.delay 12 | 13 | return { 14 | update(context) { 15 | if (context.data.type === 'contextmenu') { 16 | return { 17 | items: context.data.items, 18 | delay, 19 | searchBar: context.data.searchBar, 20 | onHide: context.data.onHide 21 | } 22 | } 23 | }, 24 | mount(context, plugin) { 25 | const parent = plugin.parentScope() 26 | const emit = parent.emit.bind(parent) 27 | const rendered = () => { 28 | emit({ type: 'rendered', data: context.data } as any) 29 | } 30 | 31 | if (context.data.type === 'contextmenu') { 32 | return { 33 | key: 'context-menu', 34 | component: ContextMenuComponent, 35 | props: { 36 | items: context.data.items, 37 | delay, 38 | searchBar: context.data.searchBar, 39 | onHide: context.data.onHide, 40 | rendered 41 | } 42 | } 43 | } 44 | return null 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/presets/context-menu/module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { ContextMenuComponent } from './components/menu/menu.component' 5 | import { ContextMenuSearchComponent } from './components/search/search.component' 6 | import { ContextMenuItemComponent } from './components/item/item.component' 7 | 8 | @NgModule({ 9 | declarations: [ 10 | ContextMenuComponent, 11 | ContextMenuSearchComponent, 12 | ContextMenuItemComponent 13 | ], 14 | imports: [ 15 | CommonModule 16 | ], 17 | exports: [ 18 | ContextMenuComponent, 19 | ContextMenuSearchComponent, 20 | ContextMenuItemComponent, 21 | ], 22 | entryComponents: [ 23 | ContextMenuComponent 24 | ] 25 | }) 26 | export class ReteContextMenuModule {} 27 | -------------------------------------------------------------------------------- /src/presets/context-menu/types.ts: -------------------------------------------------------------------------------- 1 | import { RenderSignal } from '../../types' 2 | 3 | export type Item = { 4 | label: string 5 | key: string 6 | handler(): void 7 | subitems?: Item[] 8 | } 9 | 10 | export type ContextMenuRender = 11 | | RenderSignal<'contextmenu', { items: Item[], onHide(): void, searchBar?: boolean }> 12 | -------------------------------------------------------------------------------- /src/presets/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Built-in presets, responsible for rendering different parts of the editor. 3 | * @module 4 | */ 5 | export * as classic from './classic' 6 | export * as contextMenu from './context-menu' 7 | export * as minimap from './minimap' 8 | export * as reroute from './reroute' 9 | -------------------------------------------------------------------------------- /src/presets/minimap/components/index.ts: -------------------------------------------------------------------------------- 1 | export { MinimapComponent } from './minimap/minimap.component'; 2 | export { MiniViewportComponent } from './mini-viewport/mini-viewport.component'; 3 | export { MiniNodeComponent } from './mini-node/mini-node.component'; 4 | -------------------------------------------------------------------------------- /src/presets/minimap/components/mini-node/mini-node.component.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retejs/angular-plugin/e9b0cfb6567e15fd935b6f2486c389651242c8cb/src/presets/minimap/components/mini-node/mini-node.component.html -------------------------------------------------------------------------------- /src/presets/minimap/components/mini-node/mini-node.component.sass: -------------------------------------------------------------------------------- 1 | :host 2 | display: block 3 | position: absolute 4 | background: rgba(110, 136, 255, 0.8) 5 | border: 1px solid rgb(192 206 212 / 60%) 6 | -------------------------------------------------------------------------------- /src/presets/minimap/components/mini-node/mini-node.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, HostBinding } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'minimap-mini-node', 5 | templateUrl: './mini-node.component.html', 6 | styleUrls: ['./mini-node.component.sass'], 7 | host: { 8 | 'data-testid': 'minimap-node' 9 | } 10 | }) 11 | export class MiniNodeComponent { 12 | @Input() left!: number 13 | @Input() top!: number 14 | @Input() width!: number 15 | @Input() height!: number 16 | 17 | @HostBinding('style.left') get styleLeft() { 18 | return this.px(this.left) 19 | } 20 | @HostBinding('style.top') get styleTop() { 21 | return this.px(this.top) 22 | } 23 | @HostBinding('style.width') get styleWidth() { 24 | return this.px(this.width) 25 | } 26 | @HostBinding('style.height') get styleHeight() { 27 | return this.px(this.height) 28 | } 29 | 30 | px(value: number) { 31 | return `${value}px` 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/presets/minimap/components/mini-viewport/mini-viewport.component.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retejs/angular-plugin/e9b0cfb6567e15fd935b6f2486c389651242c8cb/src/presets/minimap/components/mini-viewport/mini-viewport.component.html -------------------------------------------------------------------------------- /src/presets/minimap/components/mini-viewport/mini-viewport.component.sass: -------------------------------------------------------------------------------- 1 | :host 2 | display: block 3 | position: absolute 4 | background: rgba(255, 251, 128, 0.32) 5 | border: 1px solid #ffe52b 6 | -------------------------------------------------------------------------------- /src/presets/minimap/components/mini-viewport/mini-viewport.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, HostBinding, HostListener } from '@angular/core'; 2 | import { useDrag } from '../../../../shared/drag'; 3 | import { MinimapData } from '../../types'; 4 | 5 | @Component({ 6 | selector: 'minimap-mini-viewport', 7 | templateUrl: './mini-viewport.component.html', 8 | styleUrls: ['./mini-viewport.component.sass'], 9 | host: { 10 | 'data-testid': 'minimap-viewport' 11 | } 12 | }) 13 | export class MiniViewportComponent { 14 | @Input() left!: number 15 | @Input() top!: number 16 | @Input() width!: number 17 | @Input() height!: number 18 | @Input() containerWidth!: number 19 | @Input() translate!: MinimapData['translate'] 20 | 21 | drag = useDrag((dx, dy) => this.onDrag(dx, dy), e => ({ x: e.pageX, y: e.pageY })) 22 | 23 | @HostBinding('style.left') get styleLeft() { 24 | return this.px(this.scale(this.left)) 25 | } 26 | @HostBinding('style.top') get styleTop() { 27 | return this.px(this.scale(this.top)) 28 | } 29 | @HostBinding('style.width') get styleWidth() { 30 | return this.px(this.scale(this.width)) 31 | } 32 | @HostBinding('style.height') get styleHeight() { 33 | return this.px(this.scale(this.height)) 34 | } 35 | 36 | @HostListener('pointerdown', ['$event']) pointerdown(event: PointerEvent) { 37 | event.stopPropagation() 38 | this.drag.start(event) 39 | } 40 | 41 | px(value: number) { 42 | return `${value}px` 43 | } 44 | 45 | scale(v: number) { 46 | return v * this.containerWidth 47 | } 48 | 49 | invert(v: number) { 50 | return v / this.containerWidth 51 | } 52 | 53 | onDrag(dx: number, dy: number) { 54 | this.translate(this.invert(-dx), this.invert(-dy)) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/presets/minimap/components/minimap/minimap.component.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 7 | -------------------------------------------------------------------------------- /src/presets/minimap/components/minimap/minimap.component.sass: -------------------------------------------------------------------------------- 1 | :host 2 | position: absolute 3 | right: 24px 4 | bottom: 24px 5 | background: rgba(229, 234, 239, 0.65) 6 | padding: 20px 7 | overflow: hidden 8 | border: 1px solid #b1b7ff 9 | border-radius: 8px 10 | box-sizing: border-box 11 | -------------------------------------------------------------------------------- /src/presets/minimap/components/minimap/minimap.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, ChangeDetectorRef, OnChanges, HostListener, ElementRef, HostBinding } from '@angular/core'; 2 | import { MinimapData } from '../../types'; 3 | // [imports] 4 | 5 | @Component({ 6 | // [component-directive] 7 | templateUrl: './minimap.component.html', 8 | styleUrls: ['./minimap.component.sass'], 9 | host: { 10 | 'data-testid': 'minimap' 11 | } 12 | }) 13 | export class MinimapComponent implements OnChanges { 14 | @Input() rendered!: () => void 15 | @Input() size!: number 16 | @Input() ratio!: MinimapData['ratio'] 17 | @Input() nodes!: MinimapData['nodes'] 18 | @Input() viewport!: MinimapData['viewport'] 19 | @Input() translate!: MinimapData['translate'] 20 | @Input() point!: MinimapData['point'] 21 | 22 | @HostBinding('style.width') get width() { 23 | return this.px(this.size * this.ratio) 24 | } 25 | @HostBinding('style.height') get height() { 26 | return this.px(this.size) 27 | } 28 | 29 | @HostListener('pointerdown', ['$event']) pointerdown(event: PointerEvent) { 30 | event.stopPropagation() 31 | event.preventDefault() 32 | } 33 | 34 | @HostListener('dblclick', ['$event']) dblclick(event: MouseEvent) { 35 | event.stopPropagation() 36 | event.preventDefault() 37 | 38 | if (!this.el.nativeElement) return 39 | const box = this.el.nativeElement.getBoundingClientRect() 40 | const x = (event.clientX - box.left) / (this.size * this.ratio) 41 | const y = (event.clientY - box.top) / (this.size * this.ratio) 42 | 43 | this.point(x, y) 44 | } 45 | 46 | constructor(public el: ElementRef, private cdr: ChangeDetectorRef) { 47 | this.cdr.detach() 48 | } 49 | 50 | ngOnChanges(): void { 51 | this.cdr.detectChanges() 52 | requestAnimationFrame(() => this.rendered()) 53 | } 54 | 55 | px(value: number) { 56 | return `${value}px` 57 | } 58 | 59 | scale(value: number) { 60 | if (!this.el.nativeElement) return 0 61 | 62 | return value * this.el.nativeElement.clientWidth 63 | } 64 | 65 | identifyMiniNode(_: number, item: MinimapData['nodes'][number]) { 66 | return [item.top, item.left].join('_') 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/presets/minimap/index.ts: -------------------------------------------------------------------------------- 1 | import { BaseSchemes } from 'rete'; 2 | 3 | import { RenderPreset } from '../types'; 4 | import { MinimapRender } from './types'; 5 | import { MinimapComponent } from './components/minimap/minimap.component'; 6 | 7 | /** 8 | * Preset for rendering minimap. 9 | */ 10 | export function setup(props?: { size?: number }): RenderPreset { 11 | return { 12 | update(context) { 13 | if (context.data.type === 'minimap') { 14 | return { 15 | nodes: context.data.nodes, 16 | size: props?.size || 200, 17 | ratio: context.data.ratio, 18 | viewport: context.data.viewport, 19 | translate: context.data.translate, 20 | point: context.data.point 21 | } 22 | } 23 | return null 24 | }, 25 | mount(context, plugin) { 26 | const parent = plugin.parentScope() 27 | const emit = parent.emit.bind(parent) 28 | const rendered = () => { 29 | emit({ type: 'rendered', data: context.data } as any) 30 | } 31 | 32 | if (context.data.type === 'minimap') { 33 | return { 34 | key: 'rete-minimap', 35 | component: MinimapComponent, 36 | props: { 37 | nodes: context.data.nodes, 38 | size: props?.size || 200, 39 | ratio: context.data.ratio, 40 | viewport: context.data.viewport, 41 | translate: context.data.translate, 42 | point: context.data.point, 43 | rendered 44 | } 45 | } 46 | } 47 | return null 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/presets/minimap/module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { MinimapComponent } from './components/minimap/minimap.component'; 5 | import { MiniViewportComponent } from './components/mini-viewport/mini-viewport.component'; 6 | import { MiniNodeComponent } from './components/mini-node/mini-node.component'; 7 | 8 | @NgModule({ 9 | declarations: [ 10 | MinimapComponent, 11 | MiniViewportComponent, 12 | MiniNodeComponent 13 | ], 14 | imports: [ 15 | CommonModule 16 | ], 17 | exports: [ 18 | MinimapComponent, 19 | MiniViewportComponent, 20 | MiniNodeComponent 21 | ], 22 | entryComponents: [ 23 | MinimapComponent 24 | ] 25 | }) 26 | export class ReteMinimapModule {} 27 | -------------------------------------------------------------------------------- /src/presets/minimap/types.ts: -------------------------------------------------------------------------------- 1 | import { RenderSignal } from '../../types' 2 | 3 | export type Rect = { 4 | width: number 5 | height: number 6 | left: number, 7 | top: number 8 | } 9 | export type Transform = { 10 | x: number 11 | y: number 12 | k: number 13 | } 14 | export type Translate = (dx: number, dy: number) => void 15 | 16 | export type MinimapData = { 17 | ratio: number 18 | nodes: Rect[] 19 | viewport: Rect 20 | start(): Transform 21 | translate: Translate 22 | point(x: number, y: number): void 23 | } 24 | 25 | export type MinimapRender = 26 | | RenderSignal<'minimap', MinimapData> 27 | -------------------------------------------------------------------------------- /src/presets/reroute/components/index.ts: -------------------------------------------------------------------------------- 1 | export { PinsComponent } from './pins/pins.component'; 2 | export { PinComponent } from './pin/pin.component'; 3 | -------------------------------------------------------------------------------- /src/presets/reroute/components/pin/pin.component.sass: -------------------------------------------------------------------------------- 1 | $size: 20px 2 | 3 | :host 4 | display: block 5 | width: $size 6 | height: $size 7 | box-sizing: border-box 8 | background: steelblue 9 | border: 2px solid white 10 | border-radius: $size 11 | position: absolute 12 | 13 | &.selected 14 | background: #ffd92c 15 | -------------------------------------------------------------------------------- /src/presets/reroute/components/pin/pin.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, ChangeDetectorRef, OnChanges, HostListener, HostBinding, Output, EventEmitter } from '@angular/core'; 2 | import { Position } from '../../types'; 3 | import { useDrag } from '../../../../shared/drag' 4 | 5 | const pinSize = 20 6 | 7 | @Component({ 8 | selector: 'reroute-pin', 9 | template: '', 10 | styleUrls: ['./pin.component.sass'], 11 | host: { 12 | 'data-testid': 'pin' 13 | } 14 | }) 15 | export class PinComponent implements OnChanges { 16 | @Input() position!: Position 17 | @Input() selected?: boolean 18 | @Input() getPointer!: () => Position 19 | @Output() menu = new EventEmitter() 20 | @Output() translate = new EventEmitter<{ dx: number, dy: number }>() 21 | @Output() down = new EventEmitter() 22 | 23 | drag = useDrag((dx, dy) => { 24 | this.translate.emit({ dx, dy }) 25 | }, () => this.getPointer()) 26 | 27 | @HostBinding('class.selected') get _selected() { 28 | return this.selected 29 | } 30 | @HostBinding('style.top') get top() { 31 | return `${this.position.y - pinSize / 2}px` 32 | } 33 | @HostBinding('style.left') get left() { 34 | return `${this.position.x - pinSize / 2}px` 35 | } 36 | @HostListener('pointerdown', ['$event']) pointerdown(event: PointerEvent) { 37 | event.stopPropagation() 38 | event.preventDefault() 39 | 40 | this.drag.start(event) 41 | this.down.emit() 42 | } 43 | @HostListener('contextmenu', ['$event']) contextmenu(event: MouseEvent) { 44 | event.stopPropagation() 45 | event.preventDefault() 46 | 47 | this.menu.emit() 48 | } 49 | 50 | constructor(private cdr: ChangeDetectorRef) { 51 | // this.cdr.detach() 52 | } 53 | 54 | ngOnChanges(): void { 55 | // this.cdr.detectChanges() 56 | // requestAnimationFrame(() => this.rendered()) 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/presets/reroute/components/pins/pins.component.html: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/presets/reroute/components/pins/pins.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, ChangeDetectorRef, OnChanges } from '@angular/core'; 2 | import { Pin, PinData, Position } from '../../types'; 3 | // [imports] 4 | 5 | @Component({ 6 | // [component-directive] 7 | templateUrl: './pins.component.html' 8 | }) 9 | export class PinsComponent implements OnChanges { 10 | @Input() rendered!: () => void 11 | @Input() pins!: PinData['pins'] 12 | @Input() down?: (id: string) => void 13 | @Input() translate?: (id: string, dx: number, dy: number) => void 14 | @Input() menu?: (id: string) => void 15 | @Input() getPointer?: () => Position 16 | 17 | constructor(private cdr: ChangeDetectorRef) { 18 | this.cdr.detach() 19 | } 20 | 21 | ngOnChanges(): void { 22 | this.cdr.detectChanges() 23 | requestAnimationFrame(() => this.rendered()) 24 | } 25 | 26 | track(_: number, item: Pin) { 27 | return item.id 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/presets/reroute/index.ts: -------------------------------------------------------------------------------- 1 | import { BaseSchemes } from 'rete'; 2 | import { BaseAreaPlugin } from 'rete-area-plugin'; 3 | 4 | import { RenderPreset } from '../types' 5 | import { PinsRender } from './types'; 6 | import { PinsComponent } from './components/pins/pins.component'; 7 | 8 | type Props = { 9 | translate?: (id: string, dx: number, dy: number) => void 10 | contextMenu?: (id: string) => void 11 | pointerdown?: (id: string) => void 12 | } 13 | 14 | /** 15 | * Preset for rendering pins. 16 | */ 17 | export function setup(props?: Props): RenderPreset { 18 | const getProps = () => ({ 19 | menu: props?.contextMenu || (() => null), 20 | translate: props?.translate || (() => null), 21 | down: props?.pointerdown || (() => null) 22 | }) 23 | 24 | return { 25 | update(context) { 26 | if (context.data.type === 'reroute-pins') { 27 | return { 28 | ...getProps(), 29 | pins: context.data.data.pins 30 | } 31 | } 32 | return null 33 | }, 34 | mount(context, plugin) { 35 | const area = plugin.parentScope>(BaseAreaPlugin) 36 | const rendered = () => { 37 | area.emit({ type: 'rendered', data: context.data }) 38 | } 39 | 40 | if (context.data.type === 'reroute-pins') { 41 | return { 42 | key: 'rete-reroute', 43 | component: PinsComponent, 44 | props: { 45 | ...getProps(), 46 | pins: context.data.data.pins, 47 | rendered, 48 | getPointer: () => area.area.pointer 49 | } 50 | } 51 | } 52 | return null 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/presets/reroute/module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { PinsComponent } from './components/pins/pins.component'; 5 | import { PinComponent } from './components/pin/pin.component'; 6 | 7 | @NgModule({ 8 | declarations: [ 9 | PinsComponent, 10 | PinComponent, 11 | ], 12 | imports: [ 13 | CommonModule 14 | ], 15 | exports: [ 16 | PinsComponent, 17 | PinComponent, 18 | ], 19 | entryComponents: [ 20 | PinsComponent 21 | ] 22 | }) 23 | export class ReteRerouteModule {} 24 | -------------------------------------------------------------------------------- /src/presets/reroute/types.ts: -------------------------------------------------------------------------------- 1 | import { ConnectionId } from 'rete' 2 | import { RenderSignal } from '../../types' 3 | 4 | export type Position = { 5 | x: number 6 | y: number 7 | } 8 | export type Pin = { 9 | id: string 10 | position: Position 11 | selected?: boolean 12 | } 13 | export type PinData = { 14 | id: ConnectionId 15 | pins: Pin[] 16 | } 17 | 18 | export type PinsRender = 19 | | RenderSignal<'reroute-pins', { data: PinData }> 20 | -------------------------------------------------------------------------------- /src/presets/types.ts: -------------------------------------------------------------------------------- 1 | import { BaseSchemes } from 'rete' 2 | import { AngularPlugin } from '../core' 3 | 4 | export type RenderPreset = { 5 | attach?: (plugin: AngularPlugin) => void 6 | update: (context: Extract, plugin: AngularPlugin) => Record | undefined | null | void 7 | mount: (context: Extract, plugin: AngularPlugin) => { key: string, component: any, props: Record } | undefined | null | void 8 | } 9 | -------------------------------------------------------------------------------- /src/ref.ts: -------------------------------------------------------------------------------- 1 | import { Input, ElementRef, OnChanges, OnDestroy } from '@angular/core'; 2 | import { Directive } from '@angular/core'; 3 | 4 | @Directive({ 5 | selector: '[refComponent]' 6 | }) 7 | export class RefDirective implements OnChanges, OnDestroy { 8 | @Input() data!: any 9 | @Input() emit!: any 10 | 11 | constructor(private el: ElementRef) { } 12 | 13 | ngOnChanges() { 14 | this.emit({ type: 'render', data: { ...this.data, element: this.el.nativeElement } }) 15 | } 16 | 17 | ngOnDestroy() { 18 | this.emit({ type: 'unmount', data: { element: this.el.nativeElement } }) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/reflect.ts: -------------------------------------------------------------------------------- 1 | export function reflect(obj: unknown) { 2 | if (typeof obj !== 'object' || obj === null) { 3 | return obj; 4 | } 5 | 6 | return new Proxy(obj, { 7 | get(target, prop) { 8 | return target[prop]; 9 | }, 10 | set(target, prop, value) { 11 | target[prop] = value; 12 | return true; 13 | }, 14 | has: (target, prop) => prop in target, 15 | deleteProperty: (target, prop) => delete target[prop], 16 | ownKeys: target => Reflect.ownKeys(target) 17 | }); 18 | } -------------------------------------------------------------------------------- /src/shared/drag.ts: -------------------------------------------------------------------------------- 1 | import { Position } from '../types' 2 | 3 | type Translate = (dx: number, dy: number) => void 4 | type StartEvent = { pageX: number, pageY: number } 5 | 6 | export function useDrag(translate: Translate, getPointer: (e: StartEvent) => Position) { 7 | return { 8 | start(e: StartEvent) { 9 | let previous = { ...getPointer(e) } 10 | 11 | function move(moveEvent: MouseEvent) { 12 | const current = { ...getPointer(moveEvent) } 13 | const dx = current.x - previous.x 14 | const dy = current.y - previous.y 15 | 16 | previous = current 17 | 18 | translate(dx, dy) 19 | } 20 | function up() { 21 | window.removeEventListener('pointermove', move) 22 | window.removeEventListener('pointerup', up) 23 | window.removeEventListener('pointercancel', up) 24 | } 25 | 26 | window.addEventListener('pointermove', move) 27 | window.addEventListener('pointerup', up) 28 | window.addEventListener('pointercancel', up) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import { NgElement as NgEl, NgElementStrategy } from '@angular/elements'; 2 | 3 | export type NgElement = NgEl & { ngElementStrategy: NgElementStrategy & { setInputValue(key: string, value: any): void } } 4 | export type NodeProps = { data: any, rendered: any, emit: any } & NgElement 5 | 6 | export type Position = { x: number, y: number } 7 | 8 | export type RenderSignal = 9 | | { type: 'render', data: { element: HTMLElement, filled?: boolean, type: Type } & Data } 10 | | { type: 'rendered', data: { element: HTMLElement, type: Type } & Data } 11 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": true, 8 | "module": "esnext", 9 | "moduleResolution": "node", 10 | "emitDecoratorMetadata": true, 11 | "experimentalDecorators": true, 12 | "importHelpers": true, 13 | "inlineSources": true, 14 | "target": "es2015", 15 | "preserveSymlinks": true, 16 | "typeRoots": [ 17 | "node_modules/@types" 18 | ], 19 | "lib": [ 20 | "es2018", 21 | "dom" 22 | ] 23 | }, 24 | "include": [ 25 | "src" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rules": { 4 | "array-type": false, 5 | "arrow-parens": false, 6 | "deprecation": { 7 | "severity": "warn" 8 | }, 9 | "component-class-suffix": true, 10 | "contextual-lifecycle": true, 11 | "directive-class-suffix": true, 12 | "directive-selector": [ 13 | true, 14 | "attribute", 15 | "app", 16 | "camelCase" 17 | ], 18 | "component-selector": [ 19 | true, 20 | "element", 21 | "app", 22 | "kebab-case" 23 | ], 24 | "import-blacklist": [ 25 | true, 26 | "rxjs/Rx" 27 | ], 28 | "interface-name": false, 29 | "max-classes-per-file": false, 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-consecutive-blank-lines": false, 47 | "no-console": [ 48 | true, 49 | "debug", 50 | "info", 51 | "time", 52 | "timeEnd", 53 | "trace" 54 | ], 55 | "no-empty": false, 56 | "no-inferrable-types": [ 57 | true, 58 | "ignore-params" 59 | ], 60 | "no-non-null-assertion": true, 61 | "no-redundant-jsdoc": true, 62 | "no-switch-case-fall-through": true, 63 | "no-use-before-declare": true, 64 | "no-var-requires": false, 65 | "object-literal-key-quotes": [ 66 | true, 67 | "as-needed" 68 | ], 69 | "object-literal-sort-keys": false, 70 | "ordered-imports": false, 71 | "quotemark": [ 72 | true, 73 | "single" 74 | ], 75 | "trailing-comma": false, 76 | "no-conflicting-lifecycle": true, 77 | "no-host-metadata-property": true, 78 | "no-input-rename": true, 79 | "no-inputs-metadata-property": true, 80 | "no-output-native": true, 81 | "no-output-on-prefix": true, 82 | "no-output-rename": true, 83 | "no-outputs-metadata-property": true, 84 | "template-banana-in-box": true, 85 | "template-no-negated-async": true, 86 | "use-lifecycle-interface": true, 87 | "use-pipe-transform-interface": true 88 | }, 89 | "rulesDirectory": [ 90 | "codelyzer" 91 | ] 92 | } --------------------------------------------------------------------------------