├── .browserslistrc ├── .editorconfig ├── .eslintrc.json ├── .gitignore ├── .yarnrc ├── README.md ├── angular.json ├── builders ├── .gitignore ├── builders.json ├── package.json ├── static-serve │ ├── index.js │ ├── index.ts │ └── schema.json └── tsconfig.builders.json ├── install.js ├── load-tb-classes.js ├── package.json ├── patches ├── @angular+compiler-cli+18.2.13.patch └── ng-packagr+18.2.1.patch ├── postcss.config.js ├── projects └── custom-nodes-config │ ├── .eslintrc.json │ ├── ng-package.json │ ├── package.json │ ├── src │ ├── lib │ │ ├── components │ │ │ ├── enrichment │ │ │ │ ├── custom-nodes-config-enrichment.module.ts │ │ │ │ ├── get-sum-into-metadata-config.component.html │ │ │ │ ├── get-sum-into-metadata-config.component.ts │ │ │ │ └── public-api.ts │ │ │ ├── filter │ │ │ │ ├── check-key-config.component.html │ │ │ │ ├── check-key-config.component.ts │ │ │ │ ├── custom-nodes-config-filter.module.ts │ │ │ │ └── public-api.ts │ │ │ ├── public-api.ts │ │ │ └── transform │ │ │ │ ├── custom-nodes-config-transform.module.ts │ │ │ │ ├── get-sum-config.component.html │ │ │ │ ├── get-sum-config.component.ts │ │ │ │ └── public-api.ts │ │ ├── custom-nodes-config.module.ts │ │ └── locale │ │ │ └── custom-nodes-locale.constant.ts │ └── public-api.ts │ ├── style.scss │ ├── tsconfig.lib.json │ └── tsconfig.lib.prod.json ├── static.serve.conf.js ├── tailwind.config.js ├── tsconfig.json └── yarn.lock /.browserslistrc: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # For the full list of supported browsers by the Angular framework, please see: 6 | # https://angular.io/guide/browser-support 7 | 8 | # You can see what browsers were selected by your queries by running: 9 | # npx browserslist 10 | 11 | last 1 Chrome version 12 | last 1 Firefox version 13 | last 2 Edge major versions 14 | last 2 Safari major versions 15 | last 2 iOS major versions 16 | Firefox ESR 17 | not ios_saf 15.2-15.3 # Fixed invalid dependency setting 18 | not safari 15.2-15.3 # Fixed invalid dependency setting 19 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "ignorePatterns": [ 4 | "projects/**/*" 5 | ], 6 | "overrides": [ 7 | { 8 | "files": [ 9 | "*.ts" 10 | ], 11 | "parserOptions": { 12 | "project": [ 13 | "tsconfig.json" 14 | ], 15 | "createDefaultProgram": true 16 | }, 17 | "extends": [ 18 | "plugin:@angular-eslint/recommended", 19 | "plugin:@angular-eslint/template/process-inline-templates" 20 | ], 21 | "rules": { 22 | "@typescript-eslint/explicit-member-accessibility": [ 23 | "off", 24 | { 25 | "accessibility": "explicit" 26 | } 27 | ], 28 | "arrow-parens": [ 29 | "off", 30 | "always" 31 | ], 32 | "@angular-eslint/component-selector": [ 33 | "error", 34 | { 35 | "prefix": [ "tb" ] 36 | } 37 | ], 38 | "id-blacklist": [ 39 | "error", 40 | "any", 41 | "Number", 42 | "String", 43 | "string", 44 | "Boolean", 45 | "boolean", 46 | "Undefined", 47 | "undefined" 48 | ], 49 | "import/order": "off", 50 | "@typescript-eslint/member-ordering": "off", 51 | "no-underscore-dangle": "off", 52 | "@typescript-eslint/naming-convention": "off" 53 | } 54 | }, 55 | { 56 | "files": [ 57 | "*.html" 58 | ], 59 | "extends": [ 60 | "plugin:@angular-eslint/template/recommended", 61 | "plugin:tailwindcss/recommended" 62 | ], 63 | "rules": { 64 | "tailwindcss/no-custom-classname": "off", 65 | "tailwindcss/migration-from-tailwind-2": "off", 66 | "tailwindcss/enforces-negative-arbitrary-values": "off" 67 | } 68 | } 69 | ] 70 | } 71 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /target 6 | /tmp 7 | /out-tsc 8 | # Only exists if Bazel was run 9 | /bazel-out 10 | 11 | # dependencies 12 | /node_modules 13 | 14 | # profiling files 15 | chrome-profiler-events*.json 16 | speed-measure-plugin*.json 17 | 18 | # IDEs and editors 19 | /.idea 20 | .project 21 | .classpath 22 | .c9/ 23 | *.launch 24 | .settings/ 25 | *.sublime-workspace 26 | 27 | # IDE - VSCode 28 | .vscode/* 29 | !.vscode/settings.json 30 | !.vscode/tasks.json 31 | !.vscode/launch.json 32 | !.vscode/extensions.json 33 | .history/* 34 | 35 | # misc 36 | /.angular/cache 37 | /.sass-cache 38 | /connect.lock 39 | /coverage 40 | /libpeerconnection.log 41 | npm-debug.log 42 | yarn-error.log 43 | testem.log 44 | /typings 45 | 46 | # System Files 47 | .DS_Store 48 | Thumbs.db 49 | 50 | # Compiled Scss 51 | style.comp.scss 52 | -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | network-timeout 1000000 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rule-node-examples-ui-ngx 2 | 3 | Configuration UI for Custom Rule Nodes from rule-node-examples ThingsBoard repository 4 | 5 | ## Build steps 6 | 7 | 1) Install dependencies 8 | ``` 9 | yarn install 10 | ``` 11 | 2) Production build 12 | ``` 13 | yarn run build 14 | ``` 15 | Resulting JavaScript should be here: 16 | ``` 17 | ./target/generated-resources/public/static/custom-nodes-config.js 18 | ``` 19 | 3) Deploy Rule Nodes UI JavaScript code to to rule-node-examples 20 | 21 | Resulting **custom-nodes-config.js** 22 | should be copied to ```rule-node-examples/src/main/resources/public/static/rulenode/``` 23 | directory of rule-node-examples repository. 24 | 25 | 4) Run Rule Nodes UI in hot redeploy mode 26 | 27 | ``` 28 | yarn start 29 | ``` 30 | 31 | By default, Rule Nodes UI will be available on port 5000 (http://localhost:5000) 32 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "custom-nodes-config": { 7 | "projectType": "library", 8 | "root": "projects/custom-nodes-config", 9 | "sourceRoot": "projects/custom-nodes-config/src", 10 | "prefix": "tb", 11 | "schematics": { 12 | "@schematics/angular:component": { 13 | "style": "scss" 14 | } 15 | }, 16 | "architect": { 17 | "build": { 18 | "builder": "@angular-devkit/build-angular:ng-packagr", 19 | "options": { 20 | "tsConfig": "projects/custom-nodes-config/tsconfig.lib.json", 21 | "project": "projects/custom-nodes-config/ng-package.json" 22 | }, 23 | "configurations": { 24 | "production": { 25 | "tsConfig": "projects/custom-nodes-config/tsconfig.lib.prod.json" 26 | }, 27 | "development": { 28 | "tsConfig": "projects/custom-nodes-config/tsconfig.lib.json" 29 | } 30 | }, 31 | "defaultConfiguration": "production" 32 | }, 33 | "serve": { 34 | "builder": "@tb/custom-builder:static-serve", 35 | "options": { 36 | "port": 5000, 37 | "tsConfig": "projects/custom-nodes-config/tsconfig.lib.json", 38 | "project": "projects/custom-nodes-config/ng-package.json", 39 | "staticServeConfig": "static.serve.conf.js" 40 | } 41 | }, 42 | "lint": { 43 | "builder": "@angular-eslint/builder:lint", 44 | "options": { 45 | "lintFilePatterns": [ 46 | "projects/custom-nodes-config/**/*.ts", 47 | "projects/custom-nodes-config/**/*.html" 48 | ] 49 | } 50 | } 51 | } 52 | } 53 | }, 54 | "defaultProject": "custom-nodes-config", 55 | "cli": { 56 | "packageManager": "yarn", 57 | "analytics": false, 58 | "schematicCollections": [ 59 | "@angular-eslint/schematics" 60 | ] 61 | }, 62 | "schematics": { 63 | "@angular-eslint/schematics:application": { 64 | "setParserOptionsProject": true 65 | }, 66 | "@angular-eslint/schematics:library": { 67 | "setParserOptionsProject": true 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /builders/.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | -------------------------------------------------------------------------------- /builders/builders.json: -------------------------------------------------------------------------------- 1 | { 2 | "builders": { 3 | "static-serve": { 4 | "implementation": "./static-serve", 5 | "class": "./static-serve", 6 | "schema": "./static-serve/schema.json", 7 | "description": "Serve static files" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /builders/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tb/custom-builder", 3 | "version": "1.0.0", 4 | "description": "Builder for Architect", 5 | "builders": "builders.json", 6 | "scripts": { 7 | "postinstall": "tsc -p tsconfig.builders.json --skipLibCheck" 8 | }, 9 | "dependencies": { 10 | "@angular-devkit/architect": "0.1802.12", 11 | "rxjs": "7.8.1", 12 | "typescript": "~5.5.4" 13 | }, 14 | "peerDependencies": { 15 | "ng-packagr": "18.2.1" 16 | }, 17 | "devDependencies": { 18 | "@angular-devkit/core": "18.2.12", 19 | "@angular-devkit/architect": "0.1802.12", 20 | "@angular-devkit/build-angular": "18.2.12", 21 | "@angular/compiler": "18.2.13", 22 | "@angular/compiler-cli": "18.2.13", 23 | "ng-packagr": "18.2.1", 24 | "tslib": "^2.7.0", 25 | "tsickle": "^0.46.3", 26 | "express": "^4.21.0", 27 | "http": "^0.0.0", 28 | "typescript": "~5.5.4" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /builders/static-serve/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | const architect_1 = require("@angular-devkit/architect"); 13 | const express = require("express"); 14 | const http = require("http"); 15 | const path_1 = require("path"); 16 | const rxjs_1 = require("rxjs"); 17 | const operators_1 = require("rxjs/operators"); 18 | const ng_packagr_1 = require("ng-packagr"); 19 | let server = null; 20 | function initialize(options, root) { 21 | return __awaiter(this, void 0, void 0, function* () { 22 | const packager = ng_packagr_1.ngPackagr(); 23 | packager.forProject(path_1.resolve(root, options.project)); 24 | if (options.tsConfig) { 25 | packager.withTsConfig(path_1.resolve(root, options.tsConfig)); 26 | } 27 | return packager; 28 | }); 29 | } 30 | function execute(options, context) { 31 | return rxjs_1.from(initialize(options, context.workspaceRoot)).pipe(operators_1.switchMap(packager => { 32 | return packager.watch().pipe(operators_1.tap(() => { 33 | createServer(options, context); 34 | })); 35 | }), operators_1.mapTo({ success: true })); 36 | } 37 | exports.execute = execute; 38 | function createServer(options, context) { 39 | if (server) { 40 | server.close(); 41 | server = null; 42 | } 43 | const app = express(); 44 | const staticServeConfig = require(path_1.resolve(context.workspaceRoot, options.staticServeConfig)); 45 | for (const path of Object.keys(staticServeConfig)) { 46 | const route = staticServeConfig[path]; 47 | app.get(path, (req, res) => { 48 | res.sendFile(path_1.resolve(context.workspaceRoot, route.target)); 49 | }); 50 | } 51 | /* app.get(options.path, (req, res) => { 52 | res.sendFile(resolve(context.workspaceRoot, options.source)); 53 | });*/ 54 | /* app.get('/static/rulenode/rulenode-core-config.umd.js.map', (req, res) => { 55 | res.sendFile(resolve(context.workspaceRoot, 'dist/rulenode-core-config/bundles/rulenode-core-config.umd.js.map')); 56 | }); */ 57 | server = http.createServer(app); 58 | const host = 'localhost'; 59 | server.on('error', (error) => { 60 | context.logger.error(error.message); 61 | }); 62 | server.listen(options.port, host, 511, () => { 63 | context.logger.info(`==> 🌎 Listening on port ${options.port}. Open up http://localhost:${options.port}/ in your browser.`); 64 | }); 65 | } 66 | exports.createServer = createServer; 67 | exports.default = architect_1.createBuilder(execute); 68 | -------------------------------------------------------------------------------- /builders/static-serve/index.ts: -------------------------------------------------------------------------------- 1 | import { BuilderContext, BuilderOutput, createBuilder } from '@angular-devkit/architect'; 2 | import * as express from 'express'; 3 | import * as http from 'http'; 4 | import { NgPackagrBuilderOptions } from '@angular-devkit/build-angular'; 5 | import { resolve } from 'path'; 6 | import { from, Observable } from 'rxjs'; 7 | import { mapTo, switchMap, tap } from 'rxjs/operators'; 8 | import { NgPackagr, ngPackagr } from 'ng-packagr'; 9 | import { existsSync } from 'fs'; 10 | import { watch } from 'chokidar'; 11 | import { execSync } from 'child_process'; 12 | 13 | interface StaticServeOptions extends NgPackagrBuilderOptions { 14 | staticServeConfig: string; 15 | port: number; 16 | } 17 | 18 | interface StaticServeConfig { 19 | [path: string]: { 20 | target: string; 21 | }; 22 | } 23 | 24 | let server: http.Server = null; 25 | 26 | async function initialize( 27 | options: StaticServeOptions, 28 | root: string, 29 | ): Promise { 30 | const packager = ngPackagr(); 31 | 32 | packager.forProject(resolve(root, options.project)); 33 | 34 | if (options.tsConfig) { 35 | packager.withTsConfig(resolve(root, options.tsConfig)); 36 | } 37 | 38 | return packager; 39 | } 40 | 41 | function watchStyles(options: StaticServeOptions, 42 | context: BuilderContext) { 43 | const styleScss = resolve(context.workspaceRoot, 'projects', context.target.project, 'style.scss'); 44 | if (existsSync(styleScss)) { 45 | const styleCompScss = resolve(context.workspaceRoot, 'projects', context.target.project, 'style.comp.scss'); 46 | context.logger.info(`==> Watching library styles: ${styleScss}`); 47 | const postcss = resolve(context.workspaceRoot, 'node_modules', '.bin', 'postcss'); 48 | watch(styleScss).on('change', () => { 49 | const compileStyleScssCommand = `${postcss} ${styleScss} -o ${styleCompScss}`; 50 | executeCliCommand(context, compileStyleScssCommand, 'Compile style.scss') 51 | }); 52 | } 53 | } 54 | 55 | function executeCliCommand(context: BuilderContext, 56 | cliCommand: string, description: string) { 57 | try { 58 | execSync(cliCommand, { 59 | stdio: 'inherit' 60 | }); 61 | } catch (err) { 62 | context.logger.error(`==> ${description} failed`, err); 63 | process.exit(1); 64 | } 65 | } 66 | 67 | export function execute( 68 | options: StaticServeOptions, 69 | context: BuilderContext, 70 | ): Observable { 71 | watchStyles(options, context); 72 | return from(initialize(options, context.workspaceRoot)).pipe( 73 | switchMap(packager => { 74 | return packager.watch().pipe( 75 | tap(() => { 76 | createServer(options, context); 77 | }) 78 | ); 79 | }), 80 | mapTo({ success: true }), 81 | ); 82 | } 83 | 84 | export function createServer(options: StaticServeOptions, context: BuilderContext) { 85 | if (server) { 86 | server.close(); 87 | server = null; 88 | } 89 | const app = express(); 90 | 91 | const staticServeConfig: StaticServeConfig = require(resolve(context.workspaceRoot, options.staticServeConfig)); 92 | for (const path of Object.keys(staticServeConfig)) { 93 | const route = staticServeConfig[path]; 94 | app.get(path, (req, res) => { 95 | if (path.endsWith('*')) { 96 | const target = req.params[0]; 97 | res.sendFile(resolve(context.workspaceRoot, route.target + target)); 98 | } else { 99 | res.sendFile(resolve(context.workspaceRoot, route.target)); 100 | } 101 | }); 102 | } 103 | 104 | /* app.get(options.path, (req, res) => { 105 | res.sendFile(resolve(context.workspaceRoot, options.source)); 106 | });*/ 107 | /* app.get('/static/rulenode/rulenode-core-config.umd.js.map', (req, res) => { 108 | res.sendFile(resolve(context.workspaceRoot, 'dist/rulenode-core-config/bundles/rulenode-core-config.umd.js.map')); 109 | }); */ 110 | 111 | server = http.createServer(app); 112 | const host = 'localhost'; 113 | server.on('error', (error) => { 114 | context.logger.error(error.message); 115 | }); 116 | server.listen(options.port, host, 511, () => { 117 | context.logger.info(`==> 🌎 Listening on port ${options.port}. Open up http://localhost:${options.port}/ in your browser.`); 118 | }); 119 | } 120 | 121 | export default createBuilder & StaticServeOptions>(execute); 122 | 123 | -------------------------------------------------------------------------------- /builders/static-serve/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/schema", 3 | "type": "object", 4 | "properties": { 5 | "tsConfig": { 6 | "type": "string" 7 | }, 8 | "project": { 9 | "type": "string" 10 | }, 11 | "staticServeConfig": { 12 | "type": "string" 13 | }, 14 | "port": { 15 | "type": "number" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /builders/tsconfig.builders.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "outDir": "./static-serve", 5 | "importHelpers": false, 6 | "sourceMap": false, 7 | "target": "es6" 8 | }, 9 | "files": [ 10 | "./static-serve/index.ts" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /install.js: -------------------------------------------------------------------------------- 1 | const fse = require('fs-extra'); 2 | const path = require('path'); 3 | 4 | let _projectRoot = null; 5 | 6 | (async() => { 7 | await fse.copy(sourcePackage(), 8 | targetPackage(), 9 | {overwrite: true}); 10 | })(); 11 | 12 | function projectRoot() { 13 | if (!_projectRoot) { 14 | _projectRoot = __dirname; 15 | } 16 | return _projectRoot; 17 | } 18 | 19 | function sourcePackage() { 20 | return path.join(projectRoot(), 'dist', 'custom-nodes-config', 'system', 'custom-nodes-config.js'); 21 | } 22 | 23 | function targetPackage() { 24 | return path.join(projectRoot(), 'target', 'generated-resources', 'public', 'static', 'custom-nodes-config.js'); 25 | } 26 | -------------------------------------------------------------------------------- /load-tb-classes.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const postcss = require('postcss'); 3 | const selectorParser = require('postcss-selector-parser'); 4 | const path = require("path"); 5 | 6 | const tbStylesCss = path.resolve(path.join('.', 'node_modules', 'thingsboard', 'src', 'styles.css')); 7 | const distDir = path.resolve(path.join('.', 'dist')); 8 | const tbClassesJson = path.resolve(path.join(distDir, 'tbClasses.json')); 9 | const classes = new Set(); 10 | 11 | const collectClassesPlugin = (opts = {}) => { 12 | return { 13 | postcssPlugin: 'collect-classes', 14 | Once (root, { result }) { 15 | root.walkRules((rule) => { 16 | selectorParser((selectors) => { 17 | selectors.walkClasses((classNode) => { 18 | classes.add(classNode.value); 19 | }); 20 | }).processSync(rule.selector); 21 | }); 22 | } 23 | } 24 | } 25 | 26 | collectClassesPlugin.postcss = true; 27 | 28 | const plugin = (opts = {}) => { 29 | return { 30 | postcssPlugin: 'collect-tb-classes', 31 | async Once (root, { result }) { 32 | const css = fs.readFileSync(tbStylesCss, 'utf8'); 33 | await postcss([collectClassesPlugin]) 34 | .process(css, {from: tbStylesCss}).then(() => { 35 | const classesArray = Array.from(classes); 36 | if (!fs.existsSync(distDir)) { 37 | fs.mkdirSync(distDir); 38 | } 39 | fs.writeFileSync(tbClassesJson, JSON.stringify(classesArray, null, 2), 'utf8'); 40 | }).catch((error) => { 41 | console.error('Error in CSS:', error); 42 | }); 43 | } 44 | } 45 | } 46 | 47 | plugin.postcss = true; 48 | 49 | module.exports = plugin; 50 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "thingsboard-rule-config-ui-ngx", 3 | "version": "4.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "yarn run build:scss && node --max_old_space_size=8048 ./node_modules/@angular/cli/bin/ng serve", 7 | "build:scss": "npx postcss ./projects/custom-nodes-config/style.scss -o ./projects/custom-nodes-config/style.comp.scss", 8 | "build": "yarn run build:scss && ng build && node install.js", 9 | "e2e": "ng e2e", 10 | "prepare": "patch-package", 11 | "lint": "ng lint" 12 | }, 13 | "private": true, 14 | "dependencies": {}, 15 | "devDependencies": { 16 | "@angular-devkit/build-angular": "18.2.12", 17 | "@angular-devkit/core": "18.2.12", 18 | "@angular-devkit/schematics": "18.2.12", 19 | "@angular-eslint/builder": "18.4.2", 20 | "@angular-eslint/eslint-plugin": "18.4.2", 21 | "@angular-eslint/eslint-plugin-template": "18.4.2", 22 | "@angular-eslint/schematics": "18.4.2", 23 | "@angular-eslint/template-parser": "18.4.2", 24 | "@angular/animations": "18.2.13", 25 | "@angular/cdk": "18.2.14", 26 | "@angular/cli": "18.2.12", 27 | "@angular/common": "18.2.13", 28 | "@angular/compiler": "18.2.13", 29 | "@angular/compiler-cli": "18.2.13", 30 | "@angular/core": "18.2.13", 31 | "@angular/forms": "18.2.13", 32 | "@angular/language-service": "18.2.13", 33 | "@angular/material": "18.2.14", 34 | "@angular/platform-browser": "18.2.13", 35 | "@angular/platform-browser-dynamic": "18.2.13", 36 | "@angular/router": "18.2.13", 37 | "@ngrx/store": "^18.1.1", 38 | "@ngx-translate/core": "^15.0.0", 39 | "@rollup/plugin-commonjs": "^28.0.2", 40 | "@rollup/plugin-replace": "6.0.2", 41 | "@rollup/plugin-terser": "^0.4.4", 42 | "@tb/custom-builder": "file:./builders", 43 | "@typescript-eslint/eslint-plugin": "^8.18.2", 44 | "@typescript-eslint/parser": "^8.18.2", 45 | "@typescript-eslint/utils": "^8.18.2", 46 | "autoprefixer": "^10.4.20", 47 | "eslint": "~9.15.0", 48 | "eslint-plugin-import": "latest", 49 | "eslint-plugin-jsdoc": "latest", 50 | "eslint-plugin-prefer-arrow": "latest", 51 | "eslint-plugin-tailwindcss": "^3.17.5", 52 | "ng-packagr": "18.2.1", 53 | "ngrx-store-freeze": "^0.2.4", 54 | "patch-package": "^8.0.0", 55 | "postcss": "^8.4.47", 56 | "postcss-cli": "^11.0.0", 57 | "postcss-scss": "^4.0.9", 58 | "postcss-selector-parser": "^7.0.0", 59 | "postinstall-prepare": "^2.0.0", 60 | "prettier": "^2.8.3", 61 | "rxjs": "7.8.1", 62 | "tailwindcss": "^3.4.15", 63 | "thingsboard": "https://github.com/thingsboard/thingsboard-ui-types.git#release/4.0", 64 | "ts-node": "^10.9.2", 65 | "tslib": "^2.7.0", 66 | "typescript": "~5.5.4", 67 | "zone.js": "~0.14.10" 68 | }, 69 | "resolutions": { 70 | "tinymce": "6.8.5", 71 | "sass": "1.77.6" 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /patches/@angular+compiler-cli+18.2.13.patch: -------------------------------------------------------------------------------- 1 | diff --git a/node_modules/@angular/compiler-cli/bundles/chunk-APDLWLLW.js b/node_modules/@angular/compiler-cli/bundles/chunk-APDLWLLW.js 2 | index 4ad0857..70a3035 100755 3 | --- a/node_modules/@angular/compiler-cli/bundles/chunk-APDLWLLW.js 4 | +++ b/node_modules/@angular/compiler-cli/bundles/chunk-APDLWLLW.js 5 | @@ -14026,7 +14026,7 @@ var ComponentDecoratorHandler = class { 6 | const def = compileComponentFromMetadata(meta, pool, makeBindingParser2()); 7 | const inputTransformFields = compileInputTransformFields(analysis.inputs); 8 | const classMetadata = analysis.classMetadata !== null ? compileComponentClassMetadata(analysis.classMetadata, perComponentDeferredDeps).toStmt() : null; 9 | - const debugInfo = analysis.classDebugInfo !== null ? compileClassDebugInfo(analysis.classDebugInfo).toStmt() : null; 10 | + const debugInfo = null;//analysis.classDebugInfo !== null ? compileClassDebugInfo(analysis.classDebugInfo).toStmt() : null; 11 | const deferrableImports = this.deferredSymbolTracker.getDeferrableImportDecls(); 12 | return compileResults(fac, def, classMetadata, "\u0275cmp", inputTransformFields, deferrableImports, debugInfo); 13 | } 14 | @@ -14067,7 +14067,7 @@ var ComponentDecoratorHandler = class { 15 | const def = compileComponentFromMetadata(meta, pool, makeBindingParser2()); 16 | const inputTransformFields = compileInputTransformFields(analysis.inputs); 17 | const classMetadata = analysis.classMetadata !== null ? compileComponentClassMetadata(analysis.classMetadata, deferrableTypes).toStmt() : null; 18 | - const debugInfo = analysis.classDebugInfo !== null ? compileClassDebugInfo(analysis.classDebugInfo).toStmt() : null; 19 | + const debugInfo = null;//analysis.classDebugInfo !== null ? compileClassDebugInfo(analysis.classDebugInfo).toStmt() : null; 20 | const deferrableImports = this.deferredSymbolTracker.getDeferrableImportDecls(); 21 | return compileResults(fac, def, classMetadata, "\u0275cmp", inputTransformFields, deferrableImports, debugInfo); 22 | } 23 | -------------------------------------------------------------------------------- /patches/ng-packagr+18.2.1.patch: -------------------------------------------------------------------------------- 1 | diff --git a/node_modules/ng-packagr/lib/flatten/rollup.d.ts b/node_modules/ng-packagr/lib/flatten/rollup.d.ts 2 | index 15f81f1..501b08d 100644 3 | --- a/node_modules/ng-packagr/lib/flatten/rollup.d.ts 4 | +++ b/node_modules/ng-packagr/lib/flatten/rollup.d.ts 5 | @@ -11,6 +11,7 @@ export interface RollupOptions { 6 | entryName: string; 7 | dir: string; 8 | sourceRoot: string; 9 | + format?: string; 10 | cache?: RollupCache; 11 | cacheDirectory?: string | false; 12 | fileCache: OutputFileCache; 13 | diff --git a/node_modules/ng-packagr/lib/flatten/rollup.js b/node_modules/ng-packagr/lib/flatten/rollup.js 14 | index 3c1f14d..d198bf6 100644 15 | --- a/node_modules/ng-packagr/lib/flatten/rollup.js 16 | +++ b/node_modules/ng-packagr/lib/flatten/rollup.js 17 | @@ -30,9 +30,12 @@ exports.rollupBundleFile = rollupBundleFile; 18 | const plugin_json_1 = __importDefault(require("@rollup/plugin-json")); 19 | const plugin_node_resolve_1 = __importDefault(require("@rollup/plugin-node-resolve")); 20 | const path = __importStar(require("path")); 21 | +const terser = require("@rollup/plugin-terser"); 22 | const cache_1 = require("../utils/cache"); 23 | const log = __importStar(require("../utils/log")); 24 | const file_loader_plugin_1 = require("./file-loader-plugin"); 25 | +const fs = require('fs'); 26 | +const os = require('os'); 27 | let rollup; 28 | /** Runs rollup over the given entry file, writes a bundle file. */ 29 | async function rollupBundleFile(opts) { 30 | @@ -41,12 +44,25 @@ async function rollupBundleFile(opts) { 31 | log.debug(`rollup (v${rollup.VERSION}) ${opts.entry} to ${opts.dir}`); 32 | const cacheDirectory = opts.cacheDirectory; 33 | // Create the bundle 34 | + const data = fs.readFileSync(path.join(__dirname, '../../../thingsboard/src/app/modules/common/modules-map.ts'), 'utf8'); 35 | + const reg = /private modulesMap: {\[key: string\]: any} = {([^;]*)};/gsm; 36 | + const regMatch = reg.exec(data); 37 | + const modulesStr = regMatch[1]; 38 | + const externalModuleIds = []; 39 | + modulesStr.split(',' + os.EOL).forEach( 40 | + (line) => { 41 | + const moduleId = line.trim().split(":")[0].replaceAll("'", "").trim(); 42 | + if (moduleId.length) { 43 | + externalModuleIds.push(moduleId); 44 | + } 45 | + } 46 | + ); 47 | const bundle = await rollup.rollup({ 48 | context: 'this', 49 | - external: moduleId => isExternalDependency(moduleId), 50 | + external: moduleId => isExternalDependency(moduleId, externalModuleIds), 51 | cache: (_a = opts.cache) !== null && _a !== void 0 ? _a : (cacheDirectory ? await (0, cache_1.readCacheEntry)(cacheDirectory, opts.cacheKey) : undefined), 52 | input: opts.entry, 53 | - plugins: [(0, plugin_node_resolve_1.default)(), (0, plugin_json_1.default)(), (0, file_loader_plugin_1.fileLoaderPlugin)(opts.fileCache)], 54 | + plugins: [(0, plugin_node_resolve_1.default)(), (0, plugin_json_1.default)(), terser()], 55 | onwarn: warning => { 56 | switch (warning.code) { 57 | case 'CIRCULAR_DEPENDENCY': 58 | @@ -66,12 +82,13 @@ async function rollupBundleFile(opts) { 59 | // Output the bundle to disk 60 | const output = await bundle.write({ 61 | name: opts.moduleName, 62 | - format: 'es', 63 | + format: opts.format || 'es', 64 | dir: opts.dir, 65 | inlineDynamicImports: false, 66 | hoistTransitiveImports: false, 67 | - chunkFileNames: opts.entryName + '-[name]-[hash].mjs', 68 | - entryFileNames: opts.entryName + '.mjs', 69 | + chunkFileNames: opts.entryName + '-[name]-[hash]' + (opts.entryExt || '.mjs'), 70 | + entryFileNames: opts.entryName + (opts.entryExt || '.mjs'), 71 | + compact: true, 72 | banner: '', 73 | sourcemap: true, 74 | }); 75 | @@ -98,13 +115,16 @@ async function ensureRollup() { 76 | log.debug(`rollup using wasm version.`); 77 | } 78 | } 79 | -function isExternalDependency(moduleId) { 80 | +function isExternalDependency(moduleId, externalModuleIds) { 81 | // more information about why we don't check for 'node_modules' path 82 | // https://github.com/rollup/rollup-plugin-node-resolve/issues/110#issuecomment-350353632 83 | if (moduleId.startsWith('.') || moduleId.startsWith('/') || path.isAbsolute(moduleId)) { 84 | // if it's either 'absolute', marked to embed, starts with a '.' or '/' or is the umd bundle and is tslib 85 | return false; 86 | } 87 | + if (externalModuleIds.indexOf(moduleId) === -1) { 88 | + return false; 89 | + } 90 | return true; 91 | } 92 | //# sourceMappingURL=rollup.js.map 93 | diff --git a/node_modules/ng-packagr/lib/ng-package/entry-point/entry-point.d.ts b/node_modules/ng-packagr/lib/ng-package/entry-point/entry-point.d.ts 94 | index 35d78ad..270e8ad 100644 95 | --- a/node_modules/ng-packagr/lib/ng-package/entry-point/entry-point.d.ts 96 | +++ b/node_modules/ng-packagr/lib/ng-package/entry-point/entry-point.d.ts 97 | @@ -8,10 +8,14 @@ export interface DestinationFiles { 98 | fesm2022: string; 99 | /** Absolute path of this entry point `ESM2022` module */ 100 | esm2022: string; 101 | + /** Absolute path of this entry point `SystemJS` module */ 102 | + system: string; 103 | /** Sub path of entrypoint distributable. */ 104 | directory: string; 105 | /** Absolute path of this entry point `FESM2022` directory */ 106 | fesm2022Dir: string; 107 | + /** Absolute path of this entry point `SystemJS` directory */ 108 | + systemDir: string; 109 | } 110 | /** 111 | * An entry point - quoting Angular Package Format - is: 112 | diff --git a/node_modules/ng-packagr/lib/ng-package/entry-point/entry-point.js b/node_modules/ng-packagr/lib/ng-package/entry-point/entry-point.js 113 | index b2eedc6..3f827c7 100644 114 | --- a/node_modules/ng-packagr/lib/ng-package/entry-point/entry-point.js 115 | +++ b/node_modules/ng-packagr/lib/ng-package/entry-point/entry-point.js 116 | @@ -103,6 +103,8 @@ class NgEntryPoint { 117 | esm2022: pathJoinWithDest('esm2022', secondaryDir, `${flatModuleFile}.mjs`), 118 | fesm2022: pathJoinWithDest('fesm2022', `${flatModuleFile}.mjs`), 119 | fesm2022Dir: pathJoinWithDest('fesm2022'), 120 | + systemDir: pathJoinWithDest('system'), 121 | + system: pathJoinWithDest('system', `${flatModuleFile}.js`) 122 | }; 123 | } 124 | $get(key) { 125 | diff --git a/node_modules/ng-packagr/lib/ng-package/entry-point/write-bundles.transform.js b/node_modules/ng-packagr/lib/ng-package/entry-point/write-bundles.transform.js 126 | index 198aaea..9de4180 100644 127 | --- a/node_modules/ng-packagr/lib/ng-package/entry-point/write-bundles.transform.js 128 | +++ b/node_modules/ng-packagr/lib/ng-package/entry-point/write-bundles.transform.js 129 | @@ -15,12 +15,12 @@ const writeBundlesTransform = (options) => (0, transform_1.transformFromPromise) 130 | const entryPoint = graph.find((0, nodes_1.isEntryPointInProgress)()); 131 | const { destinationFiles, entryPoint: ngEntryPoint, tsConfig } = entryPoint.data; 132 | const cache = entryPoint.cache; 133 | - const { fesm2022Dir, esm2022 } = destinationFiles; 134 | + const { fesm2022Dir, esm2022, systemDir, system } = destinationFiles; 135 | const spinner = (0, ora_1.default)({ 136 | hideCursor: false, 137 | discardStdin: false, 138 | }); 139 | - const key = await (0, cache_1.generateKey)(ngEntryPoint.moduleId, esm2022, fesm2022Dir, tsConfig.options.compilationMode); 140 | + const key = await (0, cache_1.generateKey)(ngEntryPoint.moduleId, esm2022, systemDir, tsConfig.options.compilationMode); 141 | const hash = await (0, cache_1.generateKey)([...cache.outputCache.values()].map(({ version }) => version).join(':')); 142 | const cacheDirectory = options.cacheEnabled && options.cacheDirectory; 143 | if (cacheDirectory) { 144 | @@ -40,13 +40,25 @@ const writeBundlesTransform = (options) => (0, transform_1.transformFromPromise) 145 | } 146 | await (0, fs_1.writeFile)(filePath, file.type === 'asset' ? file.source : file.code); 147 | } 148 | + for (const file of cacheResult.system) { 149 | + const filePath = (0, path_1.join)(systemDir, file.fileName); 150 | + if (options.watch && await (0, fs_1.exists)(filePath)) { 151 | + continue; 152 | + } 153 | + if (!writing) { 154 | + writing = true; 155 | + spinner.start('Writing SystemJS bundles'); 156 | + await (0, fs_1.mkdir)(systemDir, { recursive: true }); 157 | + } 158 | + await (0, fs_1.writeFile)(filePath, file.type === 'asset' ? file.source : file.code); 159 | + } 160 | if (writing) { 161 | - spinner.succeed('Writing FESM bundles'); 162 | + spinner.succeed('Writing SystemJS bundles'); 163 | } 164 | } 165 | catch (error) { 166 | if (!writing) { 167 | - spinner.start('Writing FESM bundles'); 168 | + spinner.start('Writing SystemJS bundles'); 169 | } 170 | spinner.fail(); 171 | throw error; 172 | @@ -54,20 +66,22 @@ const writeBundlesTransform = (options) => (0, transform_1.transformFromPromise) 173 | return; 174 | } 175 | } 176 | - async function generateFESM(rollupCache, dir) { 177 | +/* 178 | + async function generateFESM(rollupCache, dir, format) { 179 | const { cache: rollupFESMCache, files } = await (0, rollup_1.rollupBundleFile)({ 180 | sourceRoot: tsConfig.options.sourceRoot, 181 | entry: esm2022, 182 | entryName: ngEntryPoint.flatModuleFile, 183 | moduleName: ngEntryPoint.moduleId, 184 | dir, 185 | + format, 186 | cache: rollupCache, 187 | cacheDirectory, 188 | fileCache: cache.outputCache, 189 | cacheKey: await (0, cache_1.generateKey)(esm2022, dir, ngEntryPoint.moduleId, tsConfig.options.compilationMode), 190 | }); 191 | return { 192 | - /** The map contents are in an asset file type, which makes storing the map in the cache as redudant. */ 193 | + // The map contents are in an asset file type, which makes storing the map in the cache as redudant. 194 | files: files.map(f => { 195 | if (f.type === 'chunk') { 196 | f.map = null; 197 | @@ -81,7 +95,7 @@ const writeBundlesTransform = (options) => (0, transform_1.transformFromPromise) 198 | hash, 199 | }; 200 | try { 201 | - const { rollupCache, files } = await generateFESM(cache.rollupFESM2022Cache, fesm2022Dir); 202 | + const { rollupCache, files } = await generateFESM(cache.rollupFESM2022Cache, fesm2022Dir, 'es'); 203 | cache.rollupFESM2022Cache = rollupCache; 204 | fesmCache.fesm2022 = files; 205 | spinner.succeed(`Generating FESM bundles`); 206 | @@ -92,7 +106,32 @@ const writeBundlesTransform = (options) => (0, transform_1.transformFromPromise) 207 | } 208 | if (cacheDirectory) { 209 | await (0, cache_1.saveCacheEntry)(cacheDirectory, key, JSON.stringify(fesmCache)); 210 | - } 211 | + }*/ 212 | + try { 213 | + spinner.start('Generating SystemJS'); 214 | + const { cache: rollupSystemJsCache, files } = await (0, rollup_1.rollupBundleFile)({ 215 | + sourceRoot: tsConfig.options.sourceRoot, 216 | + entry: esm2022, 217 | + entryName: ngEntryPoint.flatModuleFile, 218 | + entryExt: '.js', 219 | + moduleName: null, // ngEntryPoint.moduleId, 220 | + format: 'system', 221 | + dir: systemDir, 222 | + downlevel: false, 223 | + cache: cache.rollupSystemJsCache, 224 | + cacheDirectory, 225 | + fileCache: cache.outputCache, 226 | + cacheKey: await (0, cache_1.generateKey)(esm2022, systemDir, ngEntryPoint.moduleId, tsConfig.options.compilationMode), 227 | + }); 228 | + spinner.succeed(); 229 | + if (options.watch) { 230 | + cache.rollupSystemJsCache = rollupSystemJsCache; 231 | + } 232 | + } 233 | + catch (error) { 234 | + spinner.fail(); 235 | + throw error; 236 | + } 237 | }); 238 | exports.writeBundlesTransform = writeBundlesTransform; 239 | //# sourceMappingURL=write-bundles.transform.js.map 240 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | const loadTbClassesPlugin = path.resolve('./load-tb-classes.js'); 4 | 5 | module.exports = { 6 | syntax: 'postcss-scss', 7 | plugins: { 8 | [loadTbClassesPlugin]: {}, 9 | tailwindcss: {}, 10 | autoprefixer: {} 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /projects/custom-nodes-config/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../.eslintrc.json", 3 | "ignorePatterns": [ 4 | "!**/*" 5 | ], 6 | "overrides": [ 7 | { 8 | "files": [ 9 | "*.ts" 10 | ], 11 | "parserOptions": { 12 | "project": [ 13 | "./tsconfig.lib.json" 14 | ], 15 | "createDefaultProgram": true 16 | }, 17 | "rules": { 18 | "@angular-eslint/directive-selector": [ 19 | "error", 20 | { 21 | "type": "attribute", 22 | "prefix": "tb", 23 | "style": "camelCase" 24 | } 25 | ], 26 | "@angular-eslint/component-selector": [ 27 | "error", 28 | { 29 | "type": "element", 30 | "prefix": "tb", 31 | "style": "kebab-case" 32 | } 33 | ] 34 | } 35 | }, 36 | { 37 | "files": [ 38 | "*.html" 39 | ], 40 | "rules": {} 41 | } 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /projects/custom-nodes-config/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/custom-nodes-config", 4 | "lib": { 5 | "entryFile": "src/public-api.ts" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /projects/custom-nodes-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "custom-nodes-config", 3 | "version": "4.0.0", 4 | "peerDependencies": { 5 | "@angular/common": "18.2.13", 6 | "@angular/core": "18.2.13", 7 | "@angular/cdk": "18.2.14", 8 | "@angular/forms": "18.2.13", 9 | "@angular/material": "18.2.14", 10 | "@angular/router": "18.2.13", 11 | "@ngrx/store": "^18.1.1", 12 | "@ngx-translate/core": "^15.0.0", 13 | "rxjs": "~7.8.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /projects/custom-nodes-config/src/lib/components/enrichment/custom-nodes-config-enrichment.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { SharedModule } from '@shared/public-api'; 4 | import { GetSumIntoMetadataConfigComponent } from './get-sum-into-metadata-config.component'; 5 | 6 | @NgModule({ 7 | declarations: [ 8 | GetSumIntoMetadataConfigComponent 9 | ], 10 | imports: [ 11 | CommonModule, 12 | SharedModule 13 | ], 14 | exports: [ 15 | GetSumIntoMetadataConfigComponent 16 | ] 17 | }) 18 | export class CustomNodesConfigEnrichmentModule { 19 | } 20 | -------------------------------------------------------------------------------- /projects/custom-nodes-config/src/lib/components/enrichment/get-sum-into-metadata-config.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | {{'tb.rulenode.input-key' | translate}} 4 | 5 | 6 | 7 | {{'tb.rulenode.output-key' | translate}} 8 | 9 | 10 |
11 | -------------------------------------------------------------------------------- /projects/custom-nodes-config/src/lib/components/enrichment/get-sum-into-metadata-config.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { AppState, isDefinedAndNotNull } from '@core/public-api'; 3 | import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@shared/public-api'; 4 | import { Store } from '@ngrx/store'; 5 | import { FormBuilder, FormGroup, Validators } from '@angular/forms'; 6 | 7 | @Component({ 8 | selector: 'tb-enrichment-node-sum-into-metadata-config', 9 | templateUrl: './get-sum-into-metadata-config.component.html', 10 | styleUrls: [] 11 | }) 12 | export class GetSumIntoMetadataConfigComponent extends RuleNodeConfigurationComponent { 13 | 14 | getSumIntoMetadataConfigForm: FormGroup; 15 | 16 | constructor(protected store: Store, 17 | private fb: FormBuilder) { 18 | super(store); 19 | } 20 | 21 | protected configForm(): FormGroup { 22 | return this.getSumIntoMetadataConfigForm; 23 | } 24 | 25 | protected onConfigurationSet(configuration: RuleNodeConfiguration) { 26 | this.getSumIntoMetadataConfigForm = this.fb.group({ 27 | inputKey: [configuration.inputKey, [Validators.required]], 28 | outputKey: [configuration.outputKey, [Validators.required]] 29 | }); 30 | } 31 | 32 | protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { 33 | return { 34 | inputKey: isDefinedAndNotNull(configuration?.inputKey) ? configuration.inputKey : null, 35 | outputKey: isDefinedAndNotNull(configuration?.outputKey) ? configuration.outputKey : null 36 | }; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /projects/custom-nodes-config/src/lib/components/enrichment/public-api.ts: -------------------------------------------------------------------------------- 1 | export * from './get-sum-into-metadata-config.component'; 2 | 3 | export * from './custom-nodes-config-enrichment.module'; 4 | -------------------------------------------------------------------------------- /projects/custom-nodes-config/src/lib/components/filter/check-key-config.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | {{'tb.rulenode.msg-key' | translate}} 4 | 5 | 6 |
7 | -------------------------------------------------------------------------------- /projects/custom-nodes-config/src/lib/components/filter/check-key-config.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { AppState, isDefinedAndNotNull } from '@core/public-api'; 3 | import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@shared/public-api'; 4 | import { Store } from '@ngrx/store'; 5 | import { FormBuilder, FormGroup, Validators } from '@angular/forms'; 6 | 7 | @Component({ 8 | selector: 'tb-filter-node-check-key-config', 9 | templateUrl: './check-key-config.component.html', 10 | styleUrls: [] 11 | }) 12 | export class CheckKeyConfigComponent extends RuleNodeConfigurationComponent { 13 | 14 | checkKeyConfigForm: FormGroup; 15 | 16 | constructor(protected store: Store, 17 | private fb: FormBuilder) { 18 | super(store); 19 | } 20 | 21 | protected configForm(): FormGroup { 22 | return this.checkKeyConfigForm; 23 | } 24 | 25 | protected onConfigurationSet(configuration: RuleNodeConfiguration) { 26 | this.checkKeyConfigForm = this.fb.group({ 27 | key: [configuration.key, [Validators.required]] 28 | }); 29 | } 30 | 31 | protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { 32 | return { 33 | key: isDefinedAndNotNull(configuration?.key) ? configuration.key : null, 34 | }; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /projects/custom-nodes-config/src/lib/components/filter/custom-nodes-config-filter.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { SharedModule } from '@shared/public-api'; 4 | import { CheckKeyConfigComponent } from './check-key-config.component'; 5 | 6 | @NgModule({ 7 | declarations: [ 8 | CheckKeyConfigComponent 9 | ], 10 | imports: [ 11 | CommonModule, 12 | SharedModule 13 | ], 14 | exports: [ 15 | CheckKeyConfigComponent 16 | ] 17 | }) 18 | export class CustomNodesConfigFilterModule { 19 | } 20 | -------------------------------------------------------------------------------- /projects/custom-nodes-config/src/lib/components/filter/public-api.ts: -------------------------------------------------------------------------------- 1 | export * from './check-key-config.component'; 2 | 3 | export * from './custom-nodes-config-filter.module'; 4 | -------------------------------------------------------------------------------- /projects/custom-nodes-config/src/lib/components/public-api.ts: -------------------------------------------------------------------------------- 1 | export * from './enrichment/public-api'; 2 | export * from './filter/public-api'; 3 | export * from './transform/public-api'; 4 | -------------------------------------------------------------------------------- /projects/custom-nodes-config/src/lib/components/transform/custom-nodes-config-transform.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { SharedModule } from '@shared/public-api'; 4 | import { GetSumConfigComponent } from './get-sum-config.component'; 5 | 6 | @NgModule({ 7 | declarations: [ 8 | GetSumConfigComponent 9 | ], 10 | imports: [ 11 | CommonModule, 12 | SharedModule 13 | ], 14 | exports: [ 15 | GetSumConfigComponent 16 | ] 17 | }) 18 | export class CustomNodesConfigTransformModule { 19 | } 20 | -------------------------------------------------------------------------------- /projects/custom-nodes-config/src/lib/components/transform/get-sum-config.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | {{'tb.rulenode.input-key' | translate}} 4 | 5 | 6 | 7 | {{'tb.rulenode.output-key' | translate}} 8 | 9 | 10 |
11 | -------------------------------------------------------------------------------- /projects/custom-nodes-config/src/lib/components/transform/get-sum-config.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { AppState, isDefinedAndNotNull } from '@core/public-api'; 3 | import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@shared/public-api'; 4 | import { Store } from '@ngrx/store'; 5 | import { FormBuilder, FormGroup, Validators } from '@angular/forms'; 6 | 7 | @Component({ 8 | selector: 'tb-transformation-node-sum-config', 9 | templateUrl: './get-sum-config.component.html', 10 | styleUrls: [] 11 | }) 12 | export class GetSumConfigComponent extends RuleNodeConfigurationComponent { 13 | 14 | getSumConfigForm: FormGroup; 15 | 16 | constructor(protected store: Store, 17 | private fb: FormBuilder) { 18 | super(store); 19 | } 20 | 21 | protected configForm(): FormGroup { 22 | return this.getSumConfigForm; 23 | } 24 | 25 | protected onConfigurationSet(configuration: RuleNodeConfiguration) { 26 | this.getSumConfigForm = this.fb.group({ 27 | inputKey: [configuration.inputKey, [Validators.required]], 28 | outputKey: [configuration.outputKey, [Validators.required]] 29 | }); 30 | } 31 | 32 | protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { 33 | return { 34 | inputKey: isDefinedAndNotNull(configuration?.inputKey) ? configuration.inputKey : null, 35 | outputKey: isDefinedAndNotNull(configuration?.outputKey) ? configuration.outputKey : null 36 | }; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /projects/custom-nodes-config/src/lib/components/transform/public-api.ts: -------------------------------------------------------------------------------- 1 | export * from './get-sum-config.component'; 2 | 3 | export * from './custom-nodes-config-transform.module'; 4 | -------------------------------------------------------------------------------- /projects/custom-nodes-config/src/lib/custom-nodes-config.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { HomeComponentsModule } from '@home/components/public-api'; 4 | import { 5 | TranslateService 6 | } from '@ngx-translate/core'; 7 | import addCustomNodesLocaleEnglish from './locale/custom-nodes-locale.constant'; 8 | import { SharedModule } from '@shared/public-api'; 9 | import { CustomNodesConfigFilterModule } from './components/filter/custom-nodes-config-filter.module'; 10 | import { CustomNodesConfigEnrichmentModule } from './components/enrichment/custom-nodes-config-enrichment.module'; 11 | import { CustomNodesConfigTransformModule } from './components/transform/custom-nodes-config-transform.module'; 12 | 13 | @NgModule({ 14 | imports: [ 15 | CommonModule, 16 | SharedModule, 17 | HomeComponentsModule 18 | ], 19 | exports: [ 20 | CustomNodesConfigFilterModule, 21 | CustomNodesConfigEnrichmentModule, 22 | CustomNodesConfigTransformModule 23 | ] 24 | }) 25 | export class CustomNodesConfigModule { 26 | 27 | constructor(translate: TranslateService) { 28 | addCustomNodesLocaleEnglish(translate); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /projects/custom-nodes-config/src/lib/locale/custom-nodes-locale.constant.ts: -------------------------------------------------------------------------------- 1 | import { TranslateService } from '@ngx-translate/core'; 2 | 3 | export default function addCustomNodesLocaleEnglish(translate: TranslateService) { 4 | 5 | const enUS = { 6 | tb: { 7 | rulenode: { 8 | 'msg-key': 'Message key', 9 | 'input-key': 'Input key', 10 | 'output-key': 'Output key' 11 | } 12 | } 13 | }; 14 | translate.setTranslation('en_US', enUS, true); 15 | } 16 | -------------------------------------------------------------------------------- /projects/custom-nodes-config/src/public-api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of rule-core-config 3 | */ 4 | 5 | export * from './lib/components/public-api'; 6 | export * from './lib/custom-nodes-config.module'; 7 | -------------------------------------------------------------------------------- /projects/custom-nodes-config/style.scss: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /projects/custom-nodes-config/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src", 5 | "outDir": "../../out-tsc/lib", 6 | "declaration": true, 7 | "declarationMap": true, 8 | "inlineSources": true 9 | }, 10 | "exclude": [ 11 | "src/test.ts", 12 | "**/*.spec.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /projects/custom-nodes-config/tsconfig.lib.prod.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.lib.json", 4 | "compilerOptions": { 5 | "declarationMap": false 6 | }, 7 | "angularCompilerOptions": { 8 | "compilationMode": "experimental-local", 9 | "supportTestBed": false, 10 | "supportJitMode": false 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /static.serve.conf.js: -------------------------------------------------------------------------------- 1 | const STATIC_SERVE_CONFIG = { 2 | '/static/rulenode/custom-nodes-config.js': { 3 | 'target': 'dist/custom-nodes-config/system/custom-nodes-config.js' 4 | }, 5 | '/static/rulenode/custom-nodes-config.js.map': { 6 | 'target': 'dist/custom-nodes-config/system/custom-nodes-config.js.map' 7 | } 8 | } 9 | 10 | module.exports = STATIC_SERVE_CONFIG; 11 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require("path"); 3 | 4 | const loadTbClasses = () => { 5 | const tbClassesJson = path.resolve(path.join('.', 'dist', 'tbClasses.json')); 6 | const data = fs.readFileSync(tbClassesJson, 'utf8'); 7 | return JSON.parse(data); 8 | } 9 | 10 | /** @type {import('tailwindcss').Config} */ 11 | module.exports = { 12 | important: ".tb-default", 13 | content: [ 14 | "./src/**/*.{html,ts}", 15 | "./projects/rulenode-core-config/src/**/*.{html,ts}", 16 | ], 17 | theme: { 18 | screens: { 19 | 'xs': { 20 | max: '599px' 21 | }, 22 | 'sm': { 23 | min: '600px', 24 | max: '959px' 25 | }, 26 | 'md': { 27 | min: '960px', 28 | max: '1279px' 29 | }, 30 | 'md-lg': { 31 | min: '960px', 32 | max: '1819px' 33 | }, 34 | 'lg': { 35 | min: '1280px', 36 | max: '1919px' 37 | }, 38 | 'xl': { 39 | min: '1920px', 40 | max: '5000px' 41 | }, 42 | 'lt-sm': { 43 | max: '599px' 44 | }, 45 | 'lt-md': { 46 | max: '959px' 47 | }, 48 | 'lt-lg': { 49 | max: '1279px' 50 | }, 51 | 'lt-xl': { 52 | max: '1919px' 53 | }, 54 | 'gt-xs': { 55 | min: '600px' 56 | }, 57 | 'gt-sm': { 58 | min: '960px' 59 | }, 60 | 'gt-md': { 61 | min: '1280px' 62 | }, 63 | 'gt-xmd': { 64 | min: '1600px' 65 | }, 66 | 'gt-md-lg': { 67 | min: '1820px' 68 | }, 69 | 'gt-lg': { 70 | min: '1920px' 71 | }, 72 | 'gt-xl': { 73 | min: '1920px' 74 | } 75 | }, 76 | extend: { 77 | flexBasis: { 78 | '7.5': '1.875rem', 79 | '25': '6.25rem', 80 | '37.5': '9.375rem', 81 | '62.5': '15.625rem', 82 | '72.5': '18.125rem' 83 | }, 84 | flex: { 85 | full: '1 1 100%' 86 | }, 87 | gap: { 88 | '0.75': '0.1875rem', 89 | '1.25': '0.3125rem', 90 | '3.75': '0.9375rem', 91 | '5.5': '1.375rem', 92 | '6.25': '1.5625rem', 93 | }, 94 | minHeight: { 95 | '19': '4.75rem' 96 | }, 97 | minWidth: { 98 | '7.5': '1.875rem', 99 | '25': '6.25rem', 100 | '37.5': '9.375rem', 101 | '62.5': '15.625rem', 102 | '72.5': '18.125rem' 103 | }, 104 | maxWidth: { 105 | '5%': '5%', 106 | '8%': '8%', 107 | '10%': '10%', 108 | '15%': '15%', 109 | '17%': '17%', 110 | '20%': '20%', 111 | '23%': '23%', 112 | '25%': '25%', 113 | '26%': '26%', 114 | '30%': '30%', 115 | '33%': '33%', 116 | '35%': '35%', 117 | '37%': '37%', 118 | '40%': '40%', 119 | '45%': '45%', 120 | '50%': '50%', 121 | '55%': '55%', 122 | '60%': '60%', 123 | '65%': '65%', 124 | '70%': '70%', 125 | '75%': '75%', 126 | '80%': '80%', 127 | '85%': '85%', 128 | '90%': '90%', 129 | '92%': '92%', 130 | '95%': '95%', 131 | '100%': '100%', 132 | '1/2': '50%', 133 | '1/3': '33.333333%', 134 | '2/3': '66.666667%', 135 | '1/6': '16.666667%', 136 | '2/6': '33.333333%', 137 | '4/6': '66.666667%', 138 | '5/6': '83.333333%', 139 | '1/12': '8.333333%', 140 | '2/12': '16.666667%', 141 | '4/12': '33.333333%', 142 | '5/12': '41.666667%', 143 | '7/12': '58.333333%', 144 | '8/12': '66.666667%', 145 | '10/12': '83.333333%', 146 | '11/12': '91.666667%', 147 | '7.5': '1.875rem', 148 | '25': '6.25rem', 149 | '37.5': '9.375rem', 150 | '50': '12.5rem', 151 | '62.5': '15.625rem', 152 | '72.5': '18.125rem' 153 | }, 154 | maxHeight: { 155 | '20%': '20%', 156 | '30%': '30%', 157 | '50%': '50%', 158 | '60%': '60%', 159 | '70%': '70%', 160 | '80%': '80%', 161 | '100%': '100%' 162 | } 163 | }, 164 | }, 165 | safelist: [], 166 | blocklist: loadTbClasses(), 167 | corePlugins: { 168 | preflight: false, 169 | filter: false, 170 | transform: false, 171 | blur: false, 172 | borderRadius: false 173 | }, 174 | experimental: { 175 | optimizeUniversalDefaults: true, 176 | }, 177 | plugins: [], 178 | } 179 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "forceConsistentCasingInFileNames": true, 7 | "noFallthroughCasesInSwitch": true, 8 | "sourceMap": true, 9 | "declaration": false, 10 | "downlevelIteration": true, 11 | "experimentalDecorators": true, 12 | "moduleResolution": "node", 13 | "importHelpers": true, 14 | "skipLibCheck": true, 15 | "target": "ES2022", 16 | "module": "es2020", 17 | "jsx": "react", 18 | "allowSyntheticDefaultImports": true, 19 | "lib": [ 20 | "es2020", 21 | "dom" 22 | ], 23 | "typeRoots": [ 24 | "node_modules/@types" 25 | ], 26 | "paths": { 27 | "@angular/*": [ 28 | "./node_modules/@angular/*" 29 | ], 30 | "custom-nodes-config": [ 31 | "dist/custom-nodes-config" 32 | ], 33 | "custom-nodes-config/*": [ 34 | "dist/custom-nodes-config/*" 35 | ], 36 | "@env/*": ["node_modules/thingsboard/src/environments/*"], 37 | "@app/*": ["node_modules/thingsboard/src/app/*"], 38 | "@core/*": ["node_modules/thingsboard/src/app/core/*"], 39 | "@shared/*": ["node_modules/thingsboard/src/app/shared/*"], 40 | "@modules/*": ["node_modules/thingsboard/src/app/modules/*"], 41 | "@home/*": ["node_modules/thingsboard/src/app/modules/home/*"] 42 | }, 43 | "useDefineForClassFields": false 44 | }, 45 | "angularCompilerOptions": { 46 | "enableI18nLegacyMessageIdFormat": false, 47 | "strictInjectionParameters": true, 48 | "strictInputAccessModifiers": true, 49 | "fullTemplateTypeCheck": false, 50 | "strictTemplates": false 51 | } 52 | } 53 | --------------------------------------------------------------------------------