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
minHeight="300px"
formControlName="tagFunction"
[objectHighlightRules]="objectHighlightRules"
[propertyHighlightRules]="propertyHighlightRules"
[highlightRules]="highlightRules"
[editorCompleter]="completer"
[functionArgs]="tagFunctionArgs"
[helpId]="tagFunctionHelpId">

View File

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

View File

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

View File

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

View File

@ -34,7 +34,7 @@ import {
} from '@home/components/widget/lib/scada/scada-symbol.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 { AceHighlightRule, AceHighlightRules } from '@shared/models/ace/ace.models';
import { ValueType } from '@shared/models/constants';
import ITooltipsterInstance = JQueryTooltipster.ITooltipsterInstance;
import TooltipPositioningSide = JQueryTooltipster.TooltipPositioningSide;
@ -916,91 +916,184 @@ export class ScadaSymbolElement {
}
const scadaSymbolCtxObjectHighlightRules: TbHighlightRule[] = [
{
class: 'scada-symbol-ctx',
regex: /(?<=\W|^)(ctx)(?=\W|$)\b/
}
];
const identifierRe = /[a-zA-Z$_\u00a1-\uffff][a-zA-Z\d$_\u00a1-\uffff]*/;
export const scadaSymbolGeneralStateRenderHighlightRules: TbHighlightRule[] =
scadaSymbolCtxObjectHighlightRules.concat({
class: 'scada-symbol-svg',
regex: /(?<=\W|^)(svg)(?=\W|$)\b/
});
const dotOperatorHighlightRule: AceHighlightRule = {
token: 'punctuation.operator',
regex: /[.](?![.])/,
};
export const scadaSymbolElementStateRenderHighlightRules: TbHighlightRule[] =
scadaSymbolCtxObjectHighlightRules.concat({
class: 'scada-symbol-element',
regex: /(?<=\W|^)(element)(?=\W|$)\b/
});
const endGroupHighlightRule: AceHighlightRule = {
regex: '',
token: 'empty',
next: 'no_regex'
};
export const scadaSymbolClickActionHighlightRules: TbHighlightRule[] =
scadaSymbolElementStateRenderHighlightRules.concat({
class: 'scada-symbol-event',
regex: /(?<=\W|^)(event)(?=\W|$)\b/
});
const scadaSymbolCtxObjectHighlightRule: AceHighlightRule = {
token: 'tb.scada-symbol-ctx',
regex: /\bctx\b/,
next: 'scadaSymbolCtxApi'
};
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-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/
}
];
const scadaSymbolSVGHighlightRule: AceHighlightRule = {
token: 'tb.scada-symbol-svg',
regex: /\bsvg\b/,
next: 'scadaSymbolSVGApi'
};
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/
});
const scadaSymbolElementHighlightRule: AceHighlightRule = {
token: 'tb.scada-symbol-element',
regex: /\belement\b/,
next: 'scadaSymbolElementApi'
};
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/
});
const scadaSymbolEventHighlightRule: AceHighlightRule = {
token: 'tb.scada-symbol-event',
regex: /\bevent\b/,
next: 'scadaSymbolEventApi'
};
export const scadaSymbolClickActionPropertiesHighlightRules: TbHighlightRule[] =
scadaSymbolElementStateRenderPropertiesHighlightRules.concat({
class: 'scada-symbol-event-properties',
regex: /(?<=event\.)([a-zA-Z$_\u00a1-\uffff][a-zA-Z\d$_\u00a1-\uffff]*)\b/
});
const scadaSymbolCtxApiHighlightRules: AceHighlightRules = {
scadaSymbolCtxApi: [
dotOperatorHighlightRule,
{
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 => ({
ctx: ctxCompletion,

View File

@ -25,15 +25,15 @@ import {
ViewChild,
ViewEncapsulation
} 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 { 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 { 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 { deepClone, guid, isUndefined } from '@app/core/utils';
import { 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,9 +90,7 @@ export class JsFuncComponent implements OnInit, OnDestroy, ControlValueAccessor,
@Input() editorCompleter: TbEditorCompleter;
@Input() propertyHighlightRules: TbHighlightRule[];
@Input() objectHighlightRules: TbHighlightRule[];
@Input() highlightRules: AceHighlightRules;
@Input() globalVariables: Array<string>;
@ -220,27 +218,16 @@ export class JsFuncComponent implements OnInit, OnDestroy, ControlValueAccessor,
});
}
// @ts-ignore
if ((this.propertyHighlightRules?.length || this.objectHighlightRules?.length) && !!this.jsEditor.session.$mode) {
if (!!this.highlightRules && !!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);
for(const group in this.highlightRules) {
if(!!newMode.$highlightRules.$rules[group]) {
newMode.$highlightRules.$rules[group].unshift(...this.highlightRules[group]);
} else {
newMode.$highlightRules.$rules[group] = this.highlightRules[group];
}
}
// @ts-ignore
this.jsEditor.session.$onChangeMode(newMode);

View File

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