diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/svg/iot-svg.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/svg/iot-svg.models.ts
index c77065fbf8..61fdcb9b58 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/svg/iot-svg.models.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/svg/iot-svg.models.ts
@@ -61,13 +61,13 @@ export interface IotSvgContext {
properties: {[id: string]: any};
}
-export type IotSvgStateRenderFunction = (svg: Svg, ctx: IotSvgContext) => void;
+export type IotSvgStateRenderFunction = (ctx: IotSvgContext, svg: Svg) => void;
-export type IotSvgTagStateRenderFunction = (element: Element, ctx: IotSvgContext) => void;
+export type IotSvgTagStateRenderFunction = (ctx: IotSvgContext, element: Element) => void;
export type IotSvgActionTrigger = 'click';
-export type IotSvgActionFunction = (event: Event, ctx: IotSvgContext) => void;
+export type IotSvgActionFunction = (ctx: IotSvgContext, event: Event) => void;
export interface IotSvgAction {
actionFunction?: string;
action?: IotSvgActionFunction;
@@ -421,13 +421,13 @@ export class IotSvgObject {
}
private prepareMetadata() {
- this.metadata.stateRender = parseFunction(this.metadata.stateRenderFunction, ['svg', 'ctx']) || (() => {});
+ this.metadata.stateRender = parseFunction(this.metadata.stateRenderFunction, ['ctx', 'svg']) || (() => {});
for (const tag of this.metadata.tags) {
- tag.stateRender = parseFunction(tag.stateRenderFunction, ['element', 'ctx']) || (() => {});
+ tag.stateRender = parseFunction(tag.stateRenderFunction, ['ctx', 'element']) || (() => {});
if (tag.actions) {
for (const trigger of Object.keys(tag.actions)) {
const action = tag.actions[trigger];
- action.action = parseFunction(action.actionFunction, ['event', 'ctx']) || (() => {});
+ action.action = parseFunction(action.actionFunction, ['ctx', 'event']) || (() => {});
}
}
}
@@ -483,7 +483,7 @@ export class IotSvgObject {
elements.forEach(element => {
element.attr('cursor', 'pointer');
element.on(trigger, (event) => {
- action.action(event, this.context);
+ action.action(this.context, event);
});
});
}
@@ -637,11 +637,11 @@ export class IotSvgObject {
}
private renderState(): void {
- this.metadata.stateRender(this.svgShape, this.context);
+ this.metadata.stateRender(this.context, this.svgShape);
for (const tag of this.metadata.tags) {
const elements = this.context.tags[tag.tag];// this.svgShape.find(`[tb\\:tag="${tag.tag}"]`);
elements.forEach(element => {
- tag.stateRender(element, this.context);
+ tag.stateRender(this.context, element);
});
}
}
diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tag.component.html b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tag.component.html
index 01914d1e32..88302152c5 100644
--- a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tag.component.html
+++ b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tag.component.html
@@ -49,7 +49,10 @@
@@ -65,7 +68,10 @@
diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tag.component.ts b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tag.component.ts
index 663a87d8fa..7b5e36266b 100644
--- a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tag.component.ts
+++ b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tag.component.ts
@@ -38,6 +38,13 @@ import { IotSvgTag } from '@home/components/widget/lib/svg/iot-svg.models';
import { MatExpansionPanel } from '@angular/material/expansion';
import { JsFuncComponent } from '@shared/components/js-func.component';
import { MatSelect } from '@angular/material/select';
+import { TbEditorCompleter } from '@shared/models/ace/completion.models';
+import {
+ scadaSymbolClickActionHighlightRules,
+ scadaSymbolClickActionPropertiesHighlightRules,
+ scadaSymbolElementStateRenderHighlightRules,
+ scadaSymbolElementStateRenderPropertiesHighlightRules
+} from '@home/pages/scada-symbol/scada-symbol.models';
@Component({
selector: 'tb-scada-symbol-metadata-tag',
@@ -78,6 +85,12 @@ export class ScadaSymbolMetadataTagComponent implements ControlValueAccessor, On
@Input()
tags: string[];
+ @Input()
+ elementStateRenderFunctionCompleter: TbEditorCompleter;
+
+ @Input()
+ clickActionFunctionCompleter: TbEditorCompleter;
+
@Output()
tagRemoved = new EventEmitter();
@@ -87,6 +100,12 @@ export class ScadaSymbolMetadataTagComponent implements ControlValueAccessor, On
modelValue: IotSvgTag;
+ scadaSymbolElementStateRenderHighlightRules = scadaSymbolElementStateRenderHighlightRules;
+ scadaSymbolElementStateRenderPropertiesHighlightRules = scadaSymbolElementStateRenderPropertiesHighlightRules;
+
+ scadaSymbolClickActionHighlightRules = scadaSymbolClickActionHighlightRules;
+ scadaSymbolClickActionPropertiesHighlightRules = scadaSymbolClickActionPropertiesHighlightRules;
+
private propagateChange = (_val: any) => {};
constructor(private fb: UntypedFormBuilder,
diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tags.component.html b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tags.component.html
index 5e3b49a4aa..6ce57301af 100644
--- a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tags.component.html
+++ b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tags.component.html
@@ -19,6 +19,8 @@
diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tags.component.ts b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tags.component.ts
index 5b6c8210db..3d77c90d0b 100644
--- a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tags.component.ts
+++ b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tags.component.ts
@@ -40,6 +40,7 @@ import { IotSvgTag } from '@home/components/widget/lib/svg/iot-svg.models';
import {
ScadaSymbolMetadataTagComponent
} from '@home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tag.component';
+import { TbEditorCompleter } from '@shared/models/ace/completion.models';
const tagValidator = (control: AbstractControl): ValidationErrors | null => {
const tag: IotSvgTag = control.value;
@@ -83,6 +84,12 @@ export class ScadaSymbolMetadataTagsComponent implements ControlValueAccessor, O
@Input()
tags: string[];
+ @Input()
+ elementStateRenderFunctionCompleter: TbEditorCompleter;
+
+ @Input()
+ clickActionFunctionCompleter: TbEditorCompleter;
+
availableTags: string[];
tagsFormGroup: UntypedFormGroup;
diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.html b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.html
index 134dc9bb06..e9fb61bc73 100644
--- a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.html
+++ b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.html
@@ -43,7 +43,10 @@
@@ -55,6 +58,8 @@
diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.scss b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.scss
index 7b923e1688..75ab006bc0 100644
--- a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.scss
+++ b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.scss
@@ -73,4 +73,40 @@
}
}
}
+ .ace_tb {
+ &.ace_scada-symbol {
+ &-ctx {
+ color: orangered;
+ font-weight: bold;
+ &-properties, &-property {
+ color: #0c959c;
+ }
+ &-values, &-value {
+ color: #669900;
+ }
+ &-tags, &-tag {
+ color: #cc8500;
+ }
+ &-api, &-api-method {
+ color: #ff00fe;
+ }
+ &-properties, &-values, &-tags, &-api {
+ font-style: italic;
+ // font-weight: bold;
+ }
+ &-property, &-value, &-tag, &-api-method {
+ font-weight: bold;
+ // font-weight: bold;
+ }
+ }
+ &-svg, &-element, &-event {
+ color: orangered;
+ font-weight: bold;
+ &-properties {
+ color: #0c959c;
+ font-weight: bold;
+ }
+ }
+ }
+ }
}
diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.ts b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.ts
index 75e6fcc8bb..fba374b848 100644
--- a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.ts
+++ b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.ts
@@ -14,7 +14,16 @@
/// limitations under the License.
///
-import { Component, forwardRef, Input, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
+import {
+ Component,
+ forwardRef,
+ Input,
+ OnChanges,
+ OnInit,
+ SimpleChanges,
+ ViewChild,
+ ViewEncapsulation
+} from '@angular/core';
import {
ControlValueAccessor,
NG_VALIDATORS,
@@ -26,7 +35,7 @@ import {
Validators
} from '@angular/forms';
import { PageComponent } from '@shared/components/page.component';
-import { IotSvgMetadata } from '@home/components/widget/lib/svg/iot-svg.models';
+import { emptyMetadata, IotSvgMetadata } from '@home/components/widget/lib/svg/iot-svg.models';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { ToggleHeaderOption } from '@shared/components/toggle-header.component';
@@ -34,6 +43,16 @@ import { TranslateService } from '@ngx-translate/core';
import {
ScadaSymbolMetadataTagsComponent
} from '@home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tags.component';
+import { TbEditorCompleter } from '@shared/models/ace/completion.models';
+import {
+ clickActionFunctionCompletions,
+ elementStateRenderFunctionCompletions,
+ generalStateRenderFunctionCompletions,
+ iotSvgContextCompletion,
+ scadaSymbolGeneralStateRenderHighlightRules,
+ scadaSymbolGeneralStateRenderPropertiesHighlightRules
+} from '@home/pages/scada-symbol/scada-symbol.models';
+import { CustomTranslatePipe } from '@shared/pipe/custom-translate.pipe';
@Component({
selector: 'tb-scada-symbol-metadata',
@@ -53,7 +72,7 @@ import {
],
encapsulation: ViewEncapsulation.None
})
-export class ScadaSymbolMetadataComponent extends PageComponent implements OnInit, ControlValueAccessor, Validator {
+export class ScadaSymbolMetadataComponent extends PageComponent implements OnInit, OnChanges, ControlValueAccessor, Validator {
@ViewChild('symbolMetadataTags')
symbolMetadataTags: ScadaSymbolMetadataTagsComponent;
@@ -91,9 +110,18 @@ export class ScadaSymbolMetadataComponent extends PageComponent implements OnIni
selectedOption = 'general';
+ generalStateRenderFunctionCompleter: TbEditorCompleter;
+ elementStateRenderFunctionCompleter: TbEditorCompleter;
+ clickActionFunctionCompleter: TbEditorCompleter;
+
+ scadaSymbolGeneralStateRenderHighlightRules = scadaSymbolGeneralStateRenderHighlightRules;
+
+ scadaSymbolGeneralStateRenderPropertiesHighlightRules = scadaSymbolGeneralStateRenderPropertiesHighlightRules;
+
constructor(protected store: Store
,
private fb: UntypedFormBuilder,
- private translate: TranslateService) {
+ private translate: TranslateService,
+ private customTranslate: CustomTranslatePipe) {
super(store);
}
@@ -105,12 +133,24 @@ export class ScadaSymbolMetadataComponent extends PageComponent implements OnIni
behavior: [null],
properties: [null]
});
+ this.updateFunctionCompleters(emptyMetadata());
this.metadataFormGroup.valueChanges.subscribe(() => {
this.updateModel();
});
}
+ ngOnChanges(changes: SimpleChanges): void {
+ for (const propName of Object.keys(changes)) {
+ const change = changes[propName];
+ if (!change.firstChange && change.currentValue !== change.previousValue) {
+ if (['tags'].includes(propName)) {
+ this.updateFunctionCompleters(this.modelValue);
+ }
+ }
+ }
+ }
+
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
@@ -132,6 +172,7 @@ export class ScadaSymbolMetadataComponent extends PageComponent implements OnIni
this.metadataFormGroup.patchValue(
value, {emitEvent: false}
);
+ this.updateFunctionCompleters(value);
}
editTagStateRenderFunction(tag: string): void {
@@ -157,5 +198,28 @@ export class ScadaSymbolMetadataComponent extends PageComponent implements OnIni
const metadata: IotSvgMetadata = this.metadataFormGroup.getRawValue();
this.modelValue = metadata;
this.propagateChange(this.modelValue);
+ this.updateFunctionCompleters(metadata);
+ }
+
+ private updateFunctionCompleters(metadata: IotSvgMetadata) {
+ const contextCompleter = iotSvgContextCompletion(metadata, this.tags, this.customTranslate);
+ const generalStateRender = generalStateRenderFunctionCompletions(contextCompleter);
+ if (!this.generalStateRenderFunctionCompleter) {
+ this.generalStateRenderFunctionCompleter = new TbEditorCompleter(generalStateRender);
+ } else {
+ this.generalStateRenderFunctionCompleter.updateCompletions(generalStateRender);
+ }
+ const elementStateRender = elementStateRenderFunctionCompletions(contextCompleter);
+ if (!this.elementStateRenderFunctionCompleter) {
+ this.elementStateRenderFunctionCompleter = new TbEditorCompleter(elementStateRender);
+ } else {
+ this.elementStateRenderFunctionCompleter.updateCompletions(elementStateRender);
+ }
+ const clickAction = clickActionFunctionCompletions(contextCompleter);
+ if (!this.clickActionFunctionCompleter) {
+ this.clickActionFunctionCompleter = new TbEditorCompleter(clickAction);
+ } else {
+ this.clickActionFunctionCompleter.updateCompletions(clickAction);
+ }
}
}
diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol.models.ts b/ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol.models.ts
index 3cb586218b..421ddce343 100644
--- a/ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol.models.ts
+++ b/ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol.models.ts
@@ -24,11 +24,22 @@ import {
setupAddTagPanelTooltip,
setupTagPanelTooltip
} from '@home/pages/scada-symbol/scada-symbol-tooltip.components';
-import { iotSvgContentData } from '@home/components/widget/lib/svg/iot-svg.models';
+import {
+ IotSvgBehavior,
+ IotSvgBehaviorType,
+ iotSvgContentData,
+ IotSvgMetadata,
+ IotSvgProperty,
+ IotSvgPropertyType
+} from '@home/components/widget/lib/svg/iot-svg.models';
+import { TbEditorCompletion, TbEditorCompletions } from '@shared/models/ace/completion.models';
+import { CustomTranslatePipe } from '@shared/pipe/custom-translate.pipe';
+import { TbHighlightRule } from '@shared/models/ace/ace.models';
import ITooltipsterInstance = JQueryTooltipster.ITooltipsterInstance;
import TooltipPositioningSide = JQueryTooltipster.TooltipPositioningSide;
import ITooltipsterHelper = JQueryTooltipster.ITooltipsterHelper;
import ITooltipPosition = JQueryTooltipster.ITooltipPosition;
+import { ValueType } from '@shared/models/constants';
export interface ScadaSymbolData {
imageResource: ImageResourceInfo;
@@ -747,3 +758,243 @@ export class ScadaSymbolElement {
}
}
+
+const scadaSymbolCtxObjectHighlightRules: TbHighlightRule[] = [
+ {
+ class: 'scada-symbol-ctx',
+ regex: /(?<=\W|^)(ctx)(?=\.)\b/
+ }
+];
+
+export const scadaSymbolGeneralStateRenderHighlightRules: TbHighlightRule[] =
+ scadaSymbolCtxObjectHighlightRules.concat({
+ class: 'scada-symbol-svg',
+ regex: /(?<=\W|^)(svg)(?=\.)\b/
+ });
+
+export const scadaSymbolElementStateRenderHighlightRules: TbHighlightRule[] =
+ scadaSymbolCtxObjectHighlightRules.concat({
+ class: 'scada-symbol-element',
+ regex: /(?<=\W|^)(element)(?=\.)\b/
+ });
+
+export const scadaSymbolClickActionHighlightRules: TbHighlightRule[] =
+ scadaSymbolCtxObjectHighlightRules.concat({
+ class: 'scada-symbol-event',
+ regex: /(?<=\W|^)(event)(?=\.)\b/
+ });
+
+const scadaSymbolCtxPropertyHighlightRules: TbHighlightRule[] = [
+ {
+ class: 'scada-symbol-ctx-properties',
+ regex: /(?<=ctx\.)(properties)\b/
+ },
+ {
+ class: 'scada-symbol-ctx-tags',
+ regex: /(?<=ctx\.)(tags)\b/
+ },
+ {
+ class: 'scada-symbol-ctx-values',
+ regex: /(?<=ctx\.)(values)\b/
+ },
+ {
+ class: 'scada-symbol-ctx-api',
+ regex: /(?<=ctx\.)(api)\b/
+ },
+ {
+ class: 'scada-symbol-ctx-property',
+ regex: /(?<=ctx\.properties\.)([a-zA-Z\$_\u00a1-\uffff][a-zA-Z\d\$_\u00a1-\uffff]*)\b/
+ },
+ {
+ class: 'scada-symbol-ctx-tag',
+ regex: /(?<=ctx\.tags\.)([a-zA-Z\$_\u00a1-\uffff][a-zA-Z\d\$_\u00a1-\uffff]*)\b/
+ },
+ {
+ class: 'scada-symbol-ctx-value',
+ regex: /(?<=ctx\.values\.)([a-zA-Z\$_\u00a1-\uffff][a-zA-Z\d\$_\u00a1-\uffff]*)\b/
+ },
+ {
+ class: 'scada-symbol-ctx-api-method',
+ regex: /(?<=ctx\.api\.)([a-zA-Z\$_\u00a1-\uffff][a-zA-Z\d\$_\u00a1-\uffff]*)\b/
+ }
+];
+
+export const scadaSymbolGeneralStateRenderPropertiesHighlightRules: TbHighlightRule[] =
+ scadaSymbolCtxPropertyHighlightRules.concat({
+ class: 'scada-symbol-svg-properties',
+ regex: /(?<=svg\.)([a-zA-Z\$_\u00a1-\uffff][a-zA-Z\d\$_\u00a1-\uffff]*)\b/
+ });
+
+export const scadaSymbolElementStateRenderPropertiesHighlightRules: TbHighlightRule[] =
+ scadaSymbolCtxPropertyHighlightRules.concat({
+ class: 'scada-symbol-element-properties',
+ regex: /(?<=element\.)([a-zA-Z\$_\u00a1-\uffff][a-zA-Z\d\$_\u00a1-\uffff]*)\b/
+ });
+
+export const scadaSymbolClickActionPropertiesHighlightRules: TbHighlightRule[] =
+ scadaSymbolCtxPropertyHighlightRules.concat({
+ class: 'scada-symbol-event-properties',
+ regex: /(?<=event\.)([a-zA-Z\$_\u00a1-\uffff][a-zA-Z\d\$_\u00a1-\uffff]*)\b/
+ });
+
+export const generalStateRenderFunctionCompletions = (ctxCompletion: TbEditorCompletion): TbEditorCompletions => {
+ return {
+ ctx: ctxCompletion,
+ svg: {
+ meta: 'argument',
+ type: 'Svg',
+ description: 'A root svg node. Instance of SVG.Svg.'
+ }
+ };
+};
+
+export const elementStateRenderFunctionCompletions = (ctxCompletion: TbEditorCompletion): TbEditorCompletions => {
+ return {
+ ctx: ctxCompletion,
+ element: {
+ meta: 'argument',
+ type: 'Element',
+ description: 'An SVG element.'
+ },
+ };
+};
+
+export const clickActionFunctionCompletions = (ctxCompletion: TbEditorCompletion): TbEditorCompletions => {
+ return {
+ ctx: ctxCompletion,
+ event: {
+ meta: 'argument',
+ type: 'Event',
+ description: 'DOM event.'
+ },
+ };
+};
+
+export const iotSvgContextCompletion = (metadata: IotSvgMetadata, tags: string[],
+ customTranslate: CustomTranslatePipe): TbEditorCompletion => {
+ const properties: TbEditorCompletion = {
+ meta: 'object',
+ type: 'object',
+ description: 'An object holding all defined SVG object properties.',
+ children: {}
+ };
+ for (const property of metadata.properties) {
+ properties.children[property.id] = iotSvgPropertyCompletion(property, customTranslate);
+ }
+ const values: TbEditorCompletion = {
+ meta: 'object',
+ type: 'object',
+ description: 'An object holding all values obtained using behaviors of type \'Value\'',
+ children: {}
+ };
+ const getValues = metadata.behavior.filter(b => b.type === IotSvgBehaviorType.value);
+ for (const value of getValues) {
+ values.children[value.id] = iotSvgValueCompletion(value, customTranslate);
+ }
+ const tagsCompletions: TbEditorCompletion = {
+ meta: 'object',
+ type: 'object',
+ description: 'An object holding all tagged SVG elements grouped by tags (object keys).',
+ children: {}
+ };
+ if (tags?.length) {
+ for (const tag of tags) {
+ tagsCompletions.children[tag] = {
+ meta: 'property',
+ description: 'An array of SVG elements.',
+ type: 'Array'
+ };
+ }
+ }
+ return {
+ meta: 'argument',
+ type: 'IotSvgContext',
+ description: 'Context of svg object.',
+ children: {
+ api: {
+ meta: 'object',
+ type: 'IotSvgApi',
+ description: 'Svg object API',
+ children: {
+ animate: {
+ meta: 'function',
+ description: 'Animate SVG element',
+ args: [
+ {
+ name: 'element',
+ description: 'SVG element',
+ type: 'Element'
+ },
+ {
+ name: 'duration',
+ description: 'Animation duretion is milliseconds',
+ type: 'number'
+ }
+ ],
+ return: {
+ description: 'Instance of SVG.Runner which has the same methods as any element and additional methods to control the runner.',
+ type: 'SVG.Runner'
+ }
+ }
+ }
+ },
+ properties,
+ values,
+ tags: tagsCompletions
+ }
+ };
+};
+
+const iotSvgPropertyCompletion = (property: IotSvgProperty, customTranslate: CustomTranslatePipe): TbEditorCompletion => {
+ let description = customTranslate.transform(property.name, property.name);
+ if (property.subLabel) {
+ description += ` ${customTranslate.transform(property.subLabel, property.subLabel)}`;
+ }
+ return {
+ meta: 'property',
+ description,
+ type: iotSvgPropertyCompletionType(property.type)
+ };
+};
+
+const iotSvgValueCompletion = (value: IotSvgBehavior, customTranslate: CustomTranslatePipe): TbEditorCompletion => {
+ const description = customTranslate.transform(value.name, value.name);
+ return {
+ meta: 'property',
+ description,
+ type: iotSvgValueCompletionType(value.valueType)
+ };
+};
+
+const iotSvgPropertyCompletionType = (type: IotSvgPropertyType): string => {
+ switch (type) {
+ case IotSvgPropertyType.text:
+ return 'string';
+ case IotSvgPropertyType.number:
+ return 'number';
+ case IotSvgPropertyType.switch:
+ return 'boolean';
+ case IotSvgPropertyType.color:
+ return 'color string';
+ case IotSvgPropertyType.color_settings:
+ return 'ColorProcessor';
+ case IotSvgPropertyType.font:
+ return 'Font';
+ case IotSvgPropertyType.units:
+ return 'units string';
+ }
+};
+
+const iotSvgValueCompletionType = (type: ValueType): string => {
+ switch (type) {
+ case ValueType.STRING:
+ return 'string';
+ case ValueType.INTEGER:
+ case ValueType.DOUBLE:
+ return 'number';
+ case ValueType.BOOLEAN:
+ return 'boolean';
+ case ValueType.JSON:
+ return 'object';
+ }
+};
diff --git a/ui-ngx/src/app/shared/components/js-func.component.ts b/ui-ngx/src/app/shared/components/js-func.component.ts
index 857982c718..8c3fa0a423 100644
--- a/ui-ngx/src/app/shared/components/js-func.component.ts
+++ b/ui-ngx/src/app/shared/components/js-func.component.ts
@@ -27,13 +27,13 @@ import {
} from '@angular/core';
import { ControlValueAccessor, UntypedFormControl, NG_VALIDATORS, NG_VALUE_ACCESSOR, Validator } from '@angular/forms';
import { Ace } from 'ace-builds';
-import { getAce, Range } from '@shared/models/ace/ace.models';
+import { getAce, Range, TbHighlightRule } from '@shared/models/ace/ace.models';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { ActionNotificationHide, ActionNotificationShow } from '@core/notification/notification.actions';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { UtilsService } from '@core/services/utils.service';
-import { guid, isUndefined } from '@app/core/utils';
+import { deepClone, guid, isUndefined } from '@app/core/utils';
import { TranslateService } from '@ngx-translate/core';
import { CancelAnimationFrame, RafService } from '@core/services/raf.service';
import { ResizeObserver } from '@juggle/resize-observer';
@@ -90,6 +90,10 @@ export class JsFuncComponent implements OnInit, OnDestroy, ControlValueAccessor,
@Input() editorCompleter: TbEditorCompleter;
+ @Input() propertyHighlightRules: TbHighlightRule[];
+
+ @Input() objectHighlightRules: TbHighlightRule[];
+
@Input() globalVariables: Array;
@Input() disableUndefinedCheck = false;
@@ -216,6 +220,32 @@ export class JsFuncComponent implements OnInit, OnDestroy, ControlValueAccessor,
});
}
// @ts-ignore
+ if ((this.propertyHighlightRules?.length || this.objectHighlightRules?.length) && !!this.jsEditor.session.$mode) {
+ // @ts-ignore
+ const newMode = new this.jsEditor.session.$mode.constructor();
+ newMode.$highlightRules = new newMode.HighlightRules();
+ if (this.propertyHighlightRules?.length) {
+ const propertiesRules: { token: string; regex: RegExp }[] = newMode.$highlightRules.$rules.property;
+ const index = propertiesRules.findIndex(p => p.token === 'support.constant');
+ const additionalPropertyRules: { token: string; regex: RegExp }[] = this.propertyHighlightRules.map(r => ({
+ token: `tb.${r.class}`,
+ regex: r.regex
+ }));
+ propertiesRules.splice(index, 0, ...additionalPropertyRules);
+ }
+ if (this.objectHighlightRules?.length) {
+ const noRegexRules: { token: string; regex: RegExp }[] = newMode.$highlightRules.$rules.no_regex;
+ const index = noRegexRules.findIndex(p => Array.isArray(p.token) && p.token[0] === 'support.constant');
+ const additionalNoRegexRules: { token: string; regex: RegExp }[] = this.objectHighlightRules.map(r => ({
+ token: `tb.${r.class}`,
+ regex: r.regex
+ }));
+ noRegexRules.splice(index, 0, ...additionalNoRegexRules);
+ }
+ // @ts-ignore
+ this.jsEditor.session.$onChangeMode(newMode);
+ }
+ // @ts-ignore
if (!!this.jsEditor.session.$worker) {
const jsWorkerOptions = {
undef: !this.disableUndefinedCheck,
diff --git a/ui-ngx/src/app/shared/models/ace/ace.models.ts b/ui-ngx/src/app/shared/models/ace/ace.models.ts
index a63064da43..d21859e531 100644
--- a/ui-ngx/src/app/shared/models/ace/ace.models.ts
+++ b/ui-ngx/src/app/shared/models/ace/ace.models.ts
@@ -347,3 +347,9 @@ export class Range implements Ace.Range {
}
}
+
+export interface TbHighlightRule {
+ class: string;
+ regex: RegExp;
+}
+
diff --git a/ui-ngx/src/app/shared/models/ace/completion.models.ts b/ui-ngx/src/app/shared/models/ace/completion.models.ts
index 9a8613d6f1..bfba8e5616 100644
--- a/ui-ngx/src/app/shared/models/ace/completion.models.ts
+++ b/ui-ngx/src/app/shared/models/ace/completion.models.ts
@@ -59,6 +59,10 @@ export class TbEditorCompleter implements Ace.Completer {
constructor(private editorCompletions: TbEditorCompletions) {
}
+ updateCompletions(completions: TbEditorCompletions): void {
+ this.editorCompletions = completions;
+ }
+
getCompletions(editor: Ace.Editor, session: Ace.EditSession,
position: Ace.Point, prefix: string, callback: Ace.CompleterCallback): void {
const result = this.prepareCompletions(prefix);