UI: Refactor SCADA editor highlighting to support Safari on iOS versions 16.3 and below

This commit is contained in:
Vladyslav_Prykhodko 2024-09-09 17:28:41 +03:00
parent 174166d9a1
commit 00d6f7d5be
7 changed files with 202 additions and 126 deletions

View File

@ -30,8 +30,7 @@
<tb-js-func #tagFunctionComponent <tb-js-func #tagFunctionComponent
minHeight="300px" minHeight="300px"
formControlName="tagFunction" formControlName="tagFunction"
[objectHighlightRules]="objectHighlightRules" [highlightRules]="highlightRules"
[propertyHighlightRules]="propertyHighlightRules"
[editorCompleter]="completer" [editorCompleter]="completer"
[functionArgs]="tagFunctionArgs" [functionArgs]="tagFunctionArgs"
[helpId]="tagFunctionHelpId"> [helpId]="tagFunctionHelpId">

View File

@ -28,12 +28,10 @@ import { TbPopoverComponent } from '@shared/components/popover.component';
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { WidgetService } from '@core/http/widget.service'; import { WidgetService } from '@core/http/widget.service';
import { TbEditorCompleter } from '@shared/models/ace/completion.models'; import { TbEditorCompleter } from '@shared/models/ace/completion.models';
import { TbHighlightRule } from '@shared/models/ace/ace.models'; import { AceHighlightRules } from '@shared/models/ace/ace.models';
import { import {
scadaSymbolClickActionHighlightRules, scadaSymbolClickActionHighlightRules,
scadaSymbolClickActionPropertiesHighlightRules, scadaSymbolRenderFunctionHighlightRules
scadaSymbolElementStateRenderHighlightRules,
scadaSymbolElementStateRenderPropertiesHighlightRules
} from '@home/pages/scada-symbol/scada-symbol-editor.models'; } from '@home/pages/scada-symbol/scada-symbol-editor.models';
import { JsFuncComponent } from '@shared/components/js-func.component'; import { JsFuncComponent } from '@shared/components/js-func.component';
@ -79,9 +77,7 @@ export class ScadaSymbolMetadataTagFunctionPanelComponent implements OnInit, Aft
tagFunctionHelpId: string; tagFunctionHelpId: string;
objectHighlightRules: TbHighlightRule[]; highlightRules: AceHighlightRules;
propertyHighlightRules: TbHighlightRule[];
constructor(private fb: UntypedFormBuilder, constructor(private fb: UntypedFormBuilder,
private widgetService: WidgetService) { private widgetService: WidgetService) {
@ -99,14 +95,12 @@ export class ScadaSymbolMetadataTagFunctionPanelComponent implements OnInit, Aft
if (this.tagFunctionType === 'renderFunction') { if (this.tagFunctionType === 'renderFunction') {
this.panelTitle = 'scada.state-render-function'; this.panelTitle = 'scada.state-render-function';
this.tagFunctionArgs = ['ctx', 'element']; this.tagFunctionArgs = ['ctx', 'element'];
this.objectHighlightRules = scadaSymbolElementStateRenderHighlightRules; this.highlightRules = scadaSymbolRenderFunctionHighlightRules;
this.propertyHighlightRules = scadaSymbolElementStateRenderPropertiesHighlightRules;
this.tagFunctionHelpId = 'scada/tag_state_render_fn'; this.tagFunctionHelpId = 'scada/tag_state_render_fn';
} else if (this.tagFunctionType === 'clickAction') { } else if (this.tagFunctionType === 'clickAction') {
this.panelTitle = 'scada.tag.on-click-action'; this.panelTitle = 'scada.tag.on-click-action';
this.tagFunctionArgs = ['ctx', 'element', 'event']; this.tagFunctionArgs = ['ctx', 'element', 'event'];
this.objectHighlightRules = scadaSymbolClickActionHighlightRules; this.highlightRules = scadaSymbolClickActionHighlightRules;
this.propertyHighlightRules = scadaSymbolClickActionPropertiesHighlightRules;
this.tagFunctionHelpId = 'scada/tag_click_action_fn'; this.tagFunctionHelpId = 'scada/tag_click_action_fn';
} }
} }

View File

@ -80,8 +80,7 @@
<tb-js-func formControlName="stateRenderFunction" <tb-js-func formControlName="stateRenderFunction"
minHeight="300px" minHeight="300px"
[editorCompleter]="generalStateRenderFunctionCompleter" [editorCompleter]="generalStateRenderFunctionCompleter"
[objectHighlightRules]="scadaSymbolGeneralStateRenderHighlightRules" [highlightRules]="highlightRules"
[propertyHighlightRules]="scadaSymbolGeneralStateRenderPropertiesHighlightRules"
[functionArgs]="['ctx', 'svg']" [functionArgs]="['ctx', 'svg']"
helpId="scada/symbol_state_render_fn"> helpId="scada/symbol_state_render_fn">
</tb-js-func> </tb-js-func>

View File

@ -49,8 +49,7 @@ import {
elementStateRenderFunctionCompletions, elementStateRenderFunctionCompletions,
generalStateRenderFunctionCompletions, generalStateRenderFunctionCompletions,
scadaSymbolContextCompletion, scadaSymbolContextCompletion,
scadaSymbolGeneralStateRenderHighlightRules, scadaSymbolGeneralStateHighlightRules
scadaSymbolGeneralStateRenderPropertiesHighlightRules
} from '@home/pages/scada-symbol/scada-symbol-editor.models'; } from '@home/pages/scada-symbol/scada-symbol-editor.models';
import { CustomTranslatePipe } from '@shared/pipe/custom-translate.pipe'; import { CustomTranslatePipe } from '@shared/pipe/custom-translate.pipe';
import { IAliasController } from '@core/api/widget-api.models'; import { IAliasController } from '@core/api/widget-api.models';
@ -122,9 +121,7 @@ export class ScadaSymbolMetadataComponent extends PageComponent implements OnIni
elementStateRenderFunctionCompleter: TbEditorCompleter; elementStateRenderFunctionCompleter: TbEditorCompleter;
clickActionFunctionCompleter: TbEditorCompleter; clickActionFunctionCompleter: TbEditorCompleter;
scadaSymbolGeneralStateRenderHighlightRules = scadaSymbolGeneralStateRenderHighlightRules; highlightRules = scadaSymbolGeneralStateHighlightRules;
scadaSymbolGeneralStateRenderPropertiesHighlightRules = scadaSymbolGeneralStateRenderPropertiesHighlightRules;
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
private fb: UntypedFormBuilder, private fb: UntypedFormBuilder,

View File

@ -34,7 +34,7 @@ import {
} from '@home/components/widget/lib/scada/scada-symbol.models'; } from '@home/components/widget/lib/scada/scada-symbol.models';
import { TbEditorCompletion, TbEditorCompletions } from '@shared/models/ace/completion.models'; import { TbEditorCompletion, TbEditorCompletions } from '@shared/models/ace/completion.models';
import { CustomTranslatePipe } from '@shared/pipe/custom-translate.pipe'; import { CustomTranslatePipe } from '@shared/pipe/custom-translate.pipe';
import { TbHighlightRule } from '@shared/models/ace/ace.models'; import { AceHighlightRule, AceHighlightRules } from '@shared/models/ace/ace.models';
import { ValueType } from '@shared/models/constants'; import { ValueType } from '@shared/models/constants';
import ITooltipsterInstance = JQueryTooltipster.ITooltipsterInstance; import ITooltipsterInstance = JQueryTooltipster.ITooltipsterInstance;
import TooltipPositioningSide = JQueryTooltipster.TooltipPositioningSide; import TooltipPositioningSide = JQueryTooltipster.TooltipPositioningSide;
@ -916,91 +916,184 @@ export class ScadaSymbolElement {
} }
const scadaSymbolCtxObjectHighlightRules: TbHighlightRule[] = [ const identifierRe = /[a-zA-Z$_\u00a1-\uffff][a-zA-Z\d$_\u00a1-\uffff]*/;
{
class: 'scada-symbol-ctx',
regex: /(?<=\W|^)(ctx)(?=\W|$)\b/
}
];
export const scadaSymbolGeneralStateRenderHighlightRules: TbHighlightRule[] = const dotOperatorHighlightRule: AceHighlightRule = {
scadaSymbolCtxObjectHighlightRules.concat({ token: 'punctuation.operator',
class: 'scada-symbol-svg', regex: /[.](?![.])/,
regex: /(?<=\W|^)(svg)(?=\W|$)\b/ };
});
export const scadaSymbolElementStateRenderHighlightRules: TbHighlightRule[] = const endGroupHighlightRule: AceHighlightRule = {
scadaSymbolCtxObjectHighlightRules.concat({ regex: '',
class: 'scada-symbol-element', token: 'empty',
regex: /(?<=\W|^)(element)(?=\W|$)\b/ next: 'no_regex'
}); };
export const scadaSymbolClickActionHighlightRules: TbHighlightRule[] = const scadaSymbolCtxObjectHighlightRule: AceHighlightRule = {
scadaSymbolElementStateRenderHighlightRules.concat({ token: 'tb.scada-symbol-ctx',
class: 'scada-symbol-event', regex: /\bctx\b/,
regex: /(?<=\W|^)(event)(?=\W|$)\b/ next: 'scadaSymbolCtxApi'
}); };
const scadaSymbolCtxPropertyHighlightRules: TbHighlightRule[] = [ const scadaSymbolSVGHighlightRule: AceHighlightRule = {
{ token: 'tb.scada-symbol-svg',
class: 'scada-symbol-ctx-properties', regex: /\bsvg\b/,
regex: /(?<=ctx\.)(properties)\b/ next: 'scadaSymbolSVGApi'
}, };
{
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-svg',
regex: /(?<=ctx\.)(svg)\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/
},
{
class: 'scada-symbol-ctx-svg-method',
regex: /(?<=ctx\.svg\.)([a-zA-Z$_\u00a1-\uffff][a-zA-Z\d$_\u00a1-\uffff]*)\b/
}
];
export const scadaSymbolGeneralStateRenderPropertiesHighlightRules: TbHighlightRule[] = const scadaSymbolElementHighlightRule: AceHighlightRule = {
scadaSymbolCtxPropertyHighlightRules.concat({ token: 'tb.scada-symbol-element',
class: 'scada-symbol-svg-properties', regex: /\belement\b/,
regex: /(?<=svg\.)([a-zA-Z$_\u00a1-\uffff][a-zA-Z\d$_\u00a1-\uffff]*)\b/ next: 'scadaSymbolElementApi'
}); };
export const scadaSymbolElementStateRenderPropertiesHighlightRules: TbHighlightRule[] = const scadaSymbolEventHighlightRule: AceHighlightRule = {
scadaSymbolCtxPropertyHighlightRules.concat({ token: 'tb.scada-symbol-event',
class: 'scada-symbol-element-properties', regex: /\bevent\b/,
regex: /(?<=element\.)([a-zA-Z$_\u00a1-\uffff][a-zA-Z\d$_\u00a1-\uffff]*)\b/ next: 'scadaSymbolEventApi'
}); };
export const scadaSymbolClickActionPropertiesHighlightRules: TbHighlightRule[] = const scadaSymbolCtxApiHighlightRules: AceHighlightRules = {
scadaSymbolElementStateRenderPropertiesHighlightRules.concat({ scadaSymbolCtxApi: [
class: 'scada-symbol-event-properties', dotOperatorHighlightRule,
regex: /(?<=event\.)([a-zA-Z$_\u00a1-\uffff][a-zA-Z\d$_\u00a1-\uffff]*)\b/ {
}); token: 'tb.scada-symbol-ctx-properties',
regex: /properties/,
next: 'scadaSymbolCtxPropertiesApi'
},
{
token: 'tb.scada-symbol-ctx-tags',
regex: /tags/,
next: 'scadaSymbolCtxTagsApi'
},
{
token: 'tb.scada-symbol-ctx-values',
regex: /values/,
next: 'scadaSymbolCtxValuesApi'
},
{
token: 'tb.scada-symbol-ctx-api',
regex: /api/,
next: 'scadaSymbolCtxApiMethodApi'
},
{
token: 'tb.scada-symbol-ctx-svg',
regex: /svg/,
next: 'scadaSymbolCtxSVGMethodApi'
},
endGroupHighlightRule
],
scadaSymbolCtxPropertiesApi: [
dotOperatorHighlightRule,
{
token: 'tb.scada-symbol-ctx-property',
regex: identifierRe,
next: 'no_regex'
},
endGroupHighlightRule
],
scadaSymbolCtxTagsApi: [
dotOperatorHighlightRule,
{
token: 'tb.scada-symbol-ctx-tag',
regex: identifierRe,
next: 'no_regex'
},
endGroupHighlightRule
],
scadaSymbolCtxValuesApi: [
dotOperatorHighlightRule,
{
token: 'tb.scada-symbol-ctx-value',
regex: identifierRe,
next: 'no_regex'
},
endGroupHighlightRule
],
scadaSymbolCtxApiMethodApi: [
dotOperatorHighlightRule,
{
token: 'tb.scada-symbol-ctx-api-method',
regex: identifierRe,
next: 'no_regex'
},
endGroupHighlightRule
],
scadaSymbolCtxSVGMethodApi: [
dotOperatorHighlightRule,
{
token: 'tb.scada-symbol-ctx-svg-method',
regex: identifierRe,
next: 'no_regex'
},
endGroupHighlightRule
]
};
const scadaSymbolSVGPropertyHighlightRules: AceHighlightRules = {
scadaSymbolSVGApi: [
dotOperatorHighlightRule,
{
token: 'tb.scada-symbol-svg-properties',
regex: identifierRe,
next: 'no_regex'
},
endGroupHighlightRule
]
};
const scadaSymbolElementPropertyHighlightRules: AceHighlightRules = {
scadaSymbolElementApi: [
dotOperatorHighlightRule,
{
token: 'tb.scada-symbol-element-properties',
regex: identifierRe,
next: 'no_regex'
},
endGroupHighlightRule
]
};
const scadaSymbolEventPropertyHighlightRules: AceHighlightRules = {
scadaSymbolEventApi: [
dotOperatorHighlightRule,
{
token: 'tb.scada-symbol-event-properties',
regex: identifierRe,
next: 'no_regex'
},
endGroupHighlightRule
]
};
export const scadaSymbolGeneralStateHighlightRules: AceHighlightRules = {
start: [
scadaSymbolCtxObjectHighlightRule,
scadaSymbolSVGHighlightRule
],
...scadaSymbolCtxApiHighlightRules,
...scadaSymbolSVGPropertyHighlightRules
};
export const scadaSymbolRenderFunctionHighlightRules: AceHighlightRules = {
no_regex: [
scadaSymbolCtxObjectHighlightRule,
scadaSymbolElementHighlightRule
],
...scadaSymbolCtxApiHighlightRules,
...scadaSymbolElementPropertyHighlightRules
};
export const scadaSymbolClickActionHighlightRules: AceHighlightRules = {
start: [
scadaSymbolCtxObjectHighlightRule,
scadaSymbolElementHighlightRule,
scadaSymbolEventHighlightRule
],
...scadaSymbolCtxApiHighlightRules,
...scadaSymbolElementPropertyHighlightRules,
...scadaSymbolEventPropertyHighlightRules
};
export const generalStateRenderFunctionCompletions = (ctxCompletion: TbEditorCompletion): TbEditorCompletions => ({ export const generalStateRenderFunctionCompletions = (ctxCompletion: TbEditorCompletion): TbEditorCompletions => ({
ctx: ctxCompletion, ctx: ctxCompletion,

View File

@ -25,15 +25,15 @@ import {
ViewChild, ViewChild,
ViewEncapsulation ViewEncapsulation
} from '@angular/core'; } from '@angular/core';
import { ControlValueAccessor, UntypedFormControl, NG_VALIDATORS, NG_VALUE_ACCESSOR, Validator } from '@angular/forms'; import { ControlValueAccessor, NG_VALIDATORS, NG_VALUE_ACCESSOR, UntypedFormControl, Validator } from '@angular/forms';
import { Ace } from 'ace-builds'; import { Ace } from 'ace-builds';
import { getAce, Range, TbHighlightRule } from '@shared/models/ace/ace.models'; import { AceHighlightRules, getAce, Range } from '@shared/models/ace/ace.models';
import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { ActionNotificationHide, ActionNotificationShow } from '@core/notification/notification.actions'; import { ActionNotificationHide, ActionNotificationShow } from '@core/notification/notification.actions';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { UtilsService } from '@core/services/utils.service'; import { UtilsService } from '@core/services/utils.service';
import { deepClone, guid, isUndefined } from '@app/core/utils'; import { guid, isUndefined } from '@app/core/utils';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; import { CancelAnimationFrame, RafService } from '@core/services/raf.service';
import { ResizeObserver } from '@juggle/resize-observer'; import { ResizeObserver } from '@juggle/resize-observer';
@ -90,9 +90,7 @@ export class JsFuncComponent implements OnInit, OnDestroy, ControlValueAccessor,
@Input() editorCompleter: TbEditorCompleter; @Input() editorCompleter: TbEditorCompleter;
@Input() propertyHighlightRules: TbHighlightRule[]; @Input() highlightRules: AceHighlightRules;
@Input() objectHighlightRules: TbHighlightRule[];
@Input() globalVariables: Array<string>; @Input() globalVariables: Array<string>;
@ -220,27 +218,16 @@ export class JsFuncComponent implements OnInit, OnDestroy, ControlValueAccessor,
}); });
} }
// @ts-ignore // @ts-ignore
if ((this.propertyHighlightRules?.length || this.objectHighlightRules?.length) && !!this.jsEditor.session.$mode) { if (!!this.highlightRules && !!this.jsEditor.session.$mode) {
// @ts-ignore // @ts-ignore
const newMode = new this.jsEditor.session.$mode.constructor(); const newMode = new this.jsEditor.session.$mode.constructor();
newMode.$highlightRules = new newMode.HighlightRules(); newMode.$highlightRules = new newMode.HighlightRules();
if (this.propertyHighlightRules?.length) { for(const group in this.highlightRules) {
const propertiesRules: { token: string; regex: RegExp }[] = newMode.$highlightRules.$rules.property; if(!!newMode.$highlightRules.$rules[group]) {
const index = propertiesRules.findIndex(p => p.token === 'support.constant'); newMode.$highlightRules.$rules[group].unshift(...this.highlightRules[group]);
const additionalPropertyRules: { token: string; regex: RegExp }[] = this.propertyHighlightRules.map(r => ({ } else {
token: `tb.${r.class}`, newMode.$highlightRules.$rules[group] = this.highlightRules[group];
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 // @ts-ignore
this.jsEditor.session.$onChangeMode(newMode); this.jsEditor.session.$onChangeMode(newMode);

View File

@ -352,8 +352,15 @@ export class Range implements Ace.Range {
} }
export interface TbHighlightRule { export interface AceHighlightRules {
class: string; [group: string]: Array<AceHighlightRule>;
regex: RegExp;
} }
export interface AceHighlightRule {
regex: RegExp | string;
token: string;
next?: string;
}