Browse Source

Merge pull request #12850 from maxunbearable/adjustments/calculated-fields-highlight-rework

Changed Calculated fields highlights and autocompletes
pull/12857/head
Andrew Shvayka 2 years ago
committed by GitHub
parent
commit
3a23020411
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 14
      ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.scss
  2. 2
      ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.ts
  3. 8
      ui-ngx/src/app/modules/home/components/calculated-fields/components/panel/calculated-field-argument-panel.component.html
  4. 9
      ui-ngx/src/app/modules/home/components/calculated-fields/components/panel/calculated-field-argument-panel.component.ts
  5. 2
      ui-ngx/src/app/modules/home/components/calculated-fields/components/test-dialog/calculated-field-script-test-dialog.component.html
  6. 107
      ui-ngx/src/app/modules/home/components/calculated-fields/components/test-dialog/calculated-field-script-test-dialog.component.scss
  7. 4
      ui-ngx/src/app/modules/home/components/calculated-fields/components/test-dialog/calculated-field-script-test-dialog.component.ts
  8. 95
      ui-ngx/src/app/shared/models/calculated-field.models.ts
  9. 1
      ui-ngx/src/assets/locale/locale.constant-en_US.json

14
ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.scss

@ -36,14 +36,20 @@
.tb-js-func { .tb-js-func {
.ace_tb { .ace_tb {
&.ace_calculated-field { &.ace_calculated-field {
&-key { &-ctx {
color: #C52F00; color: #C52F00;
} }
&-ts, &-time-window, &-values, &-value, &-func { &-args {
color: #185F2A;
}
&-key {
color: #c24c1a;
}
&-time-window, &-values, &-func, &-value, &-ts {
color: #7214D0; color: #7214D0;
} }
&-start-ts, &-end-ts, &-limit { &-start-ts, &-end-ts {
color: #185F2A; color: #2CAA00;
} }
} }
} }

2
ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.ts

@ -70,7 +70,7 @@ export class CalculatedFieldDialogComponent extends DialogComponent<CalculatedFi
functionArgs$ = this.configFormGroup.get('arguments').valueChanges functionArgs$ = this.configFormGroup.get('arguments').valueChanges
.pipe( .pipe(
startWith(this.data.value?.configuration?.arguments ?? {}), startWith(this.data.value?.configuration?.arguments ?? {}),
map(argumentsObj => Object.keys(argumentsObj)) map(argumentsObj => ['ctx', ...Object.keys(argumentsObj)])
); );
argumentsEditorCompleter$ = this.configFormGroup.get('arguments').valueChanges argumentsEditorCompleter$ = this.configFormGroup.get('arguments').valueChanges

8
ui-ngx/src/app/modules/home/components/calculated-fields/components/panel/calculated-field-argument-panel.component.html

@ -55,6 +55,14 @@
class="tb-error"> class="tb-error">
warning warning
</mat-icon> </mat-icon>
} @else if (argumentFormGroup.get('argumentName').touched && argumentFormGroup.get('argumentName').hasError('equalCtx')) {
<mat-icon matSuffix
matTooltipPosition="above"
matTooltipClass="tb-error-tooltip"
[matTooltip]="'calculated-fields.hint.argument-name-ctx' | translate"
class="tb-error">
warning
</mat-icon>
} }
</mat-form-field> </mat-form-field>
</div> </div>

9
ui-ngx/src/app/modules/home/components/calculated-fields/components/panel/calculated-field-argument-panel.component.ts

@ -64,7 +64,7 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit {
readonly defaultLimit = Math.floor(this.maxDataPointsPerRollingArg / 10); readonly defaultLimit = Math.floor(this.maxDataPointsPerRollingArg / 10);
argumentFormGroup = this.fb.group({ argumentFormGroup = this.fb.group({
argumentName: ['', [Validators.required, this.uniqNameRequired(), Validators.pattern(charsWithNumRegex), Validators.maxLength(255)]], argumentName: ['', [Validators.required, this.uniqNameRequired(), this.notEqualCtxValidator(), Validators.pattern(charsWithNumRegex), Validators.maxLength(255)]],
refEntityId: this.fb.group({ refEntityId: this.fb.group({
entityType: [ArgumentEntityType.Current], entityType: [ArgumentEntityType.Current],
id: [''] id: ['']
@ -237,6 +237,13 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit {
} }
} }
private notEqualCtxValidator(): ValidatorFn {
return (control: FormControl) => {
const trimmedValue = control.value.trim().toLowerCase();
return trimmedValue === 'ctx' ? { equalCtx: true } : null;
};
}
private observeUpdatePosition(): void { private observeUpdatePosition(): void {
merge( merge(
this.refEntityIdFormGroup.get('entityType').valueChanges, this.refEntityIdFormGroup.get('entityType').valueChanges,

2
ui-ngx/src/app/modules/home/components/calculated-fields/components/test-dialog/calculated-field-script-test-dialog.component.html

@ -15,7 +15,7 @@
limitations under the License. limitations under the License.
--> -->
<form #testScriptContainer class="test-dialog-container size-full" [formGroup]="calculatedFieldScriptTestFormGroup"> <form #testScriptContainer class="cf-test-dialog-container size-full" [formGroup]="calculatedFieldScriptTestFormGroup">
<mat-toolbar class="flex justify-between" color="primary"> <mat-toolbar class="flex justify-between" color="primary">
<h2>{{ 'calculated-fields.test-script-function' | translate }} ({{ 'api-usage.tbel' | translate }})</h2> <h2>{{ 'calculated-fields.test-script-function' | translate }} ({{ 'api-usage.tbel' | translate }})</h2>
<button mat-icon-button <button mat-icon-button

107
ui-ngx/src/app/modules/home/components/calculated-fields/components/test-dialog/calculated-field-script-test-dialog.component.scss

@ -13,62 +13,79 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
:host {
.test-dialog-container {
.block-label {
padding: 4px;
color: #00acc1;
background: rgba(220, 220, 220, .35);
border-radius: 5px;
}
.test-block-content { .cf-test-dialog-container {
padding-top: 5px; .gutter {
padding-left: 5px; background-color: #eee;
border: 1px solid #c0c0c0; background-repeat: no-repeat;
overflow: scroll; background-position: 50%;
} }
.block-label-container { .gutter.gutter-horizontal {
position: absolute; cursor: col-resize;
z-index: 10; background-image: url("../../../../../../../assets/split.js/grips/horizontal.png");
font-size: 12px; }
font-weight: bold;
&.left { .gutter.gutter-vertical {
right: 112px; cursor: row-resize;
top: 9px; background-image: url("../../../../../../../assets/split.js/grips/vertical.png");
} }
&.right-bottom { .block-label {
right: 40px; padding: 4px;
top: 6px; color: #00acc1;
} background: rgba(220, 220, 220, .35);
border-radius: 5px;
}
&.right-top { .test-block-content {
right: 8px; padding-top: 5px;
top: 2px; padding-left: 5px;
} border: 1px solid #c0c0c0;
} overflow: scroll;
} }
}
:host::ng-deep { .block-label-container {
.test-dialog-container { position: absolute;
.gutter { z-index: 10;
background-color: #eee; font-size: 12px;
background-repeat: no-repeat; font-weight: bold;
background-position: 50%;
&.left {
right: 112px;
top: 9px;
}
&.right-bottom {
right: 40px;
top: 6px;
} }
.gutter.gutter-horizontal { &.right-top {
cursor: col-resize; right: 8px;
background-image: url("../../../../../../../assets/split.js/grips/horizontal.png"); top: 2px;
} }
}
}
.gutter.gutter-vertical { .tb-js-func {
cursor: row-resize; .ace_tb {
background-image: url("../../../../../../../assets/split.js/grips/vertical.png"); &.ace_calculated-field {
&-ctx {
color: #C52F00;
}
&-args {
color: #185F2A;
}
&-key {
color: #c24c1a;
}
&-time-window, &-values, &-func, &-value, &-ts {
color: #7214D0;
}
&-start-ts, &-end-ts {
color: #2CAA00;
}
} }
} }
} }

4
ui-ngx/src/app/modules/home/components/calculated-fields/components/test-dialog/calculated-field-script-test-dialog.component.ts

@ -22,6 +22,7 @@ import {
Inject, Inject,
OnDestroy, OnDestroy,
ViewChild, ViewChild,
ViewEncapsulation,
} from '@angular/core'; } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
@ -49,6 +50,7 @@ import {
selector: 'tb-calculated-field-script-test-dialog', selector: 'tb-calculated-field-script-test-dialog',
templateUrl: './calculated-field-script-test-dialog.component.html', templateUrl: './calculated-field-script-test-dialog.component.html',
styleUrls: ['./calculated-field-script-test-dialog.component.scss'], styleUrls: ['./calculated-field-script-test-dialog.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class CalculatedFieldScriptTestDialogComponent extends DialogComponent<CalculatedFieldScriptTestDialogComponent, export class CalculatedFieldScriptTestDialogComponent extends DialogComponent<CalculatedFieldScriptTestDialogComponent,
string> implements AfterViewInit, OnDestroy { string> implements AfterViewInit, OnDestroy {
@ -70,7 +72,7 @@ export class CalculatedFieldScriptTestDialogComponent extends DialogComponent<Ca
readonly ContentType = ContentType; readonly ContentType = ContentType;
readonly ScriptLanguage = ScriptLanguage; readonly ScriptLanguage = ScriptLanguage;
readonly functionArgs = Object.keys(this.data.arguments); readonly functionArgs = ['ctx', ...Object.keys(this.data.arguments)];
private testScriptResize: ResizeObserver; private testScriptResize: ResizeObserver;
private splitObjects: SplitObject[] = []; private splitObjects: SplitObject[] = [];

95
ui-ngx/src/app/shared/models/calculated-field.models.ts

@ -225,7 +225,7 @@ export interface CalculatedFieldLatestTelemetryArgumentValue<ValueType = unknown
} }
export interface CalculatedFieldRollingTelemetryArgumentValue<ValueType = unknown> extends CalculatedFieldArgumentValueBase { export interface CalculatedFieldRollingTelemetryArgumentValue<ValueType = unknown> extends CalculatedFieldArgumentValueBase {
timeWindow: { startTs: number; endTs: number; limit: number }; timeWindow: { startTs: number; endTs: number; };
values: CalculatedFieldSingleArgumentValue<ValueType>[]; values: CalculatedFieldSingleArgumentValue<ValueType>[];
} }
@ -235,10 +235,10 @@ export type CalculatedFieldArgumentEventValue<ValueType = unknown> = CalculatedF
export type CalculatedFieldEventArguments<ValueType = unknown> = Record<string, CalculatedFieldArgumentEventValue<ValueType>>; export type CalculatedFieldEventArguments<ValueType = unknown> = Record<string, CalculatedFieldArgumentEventValue<ValueType>>;
export const CalculatedFieldLatestTelemetryArgumentAutocomplete = { export const CalculatedFieldCtxLatestTelemetryArgumentAutocomplete = {
meta: 'object', meta: 'object',
type: '{ ts: number; value: any; }', type: '{ ts: number; value: any; }',
description: 'Calculated field latest telemetry value argument.', description: 'Calculated field context latest telemetry value argument.',
children: { children: {
ts: { ts: {
meta: 'number', meta: 'number',
@ -253,10 +253,10 @@ export const CalculatedFieldLatestTelemetryArgumentAutocomplete = {
}, },
}; };
export const CalculatedFieldAttributeValueArgumentAutocomplete = { export const CalculatedFieldCtxAttributeValueArgumentAutocomplete = {
meta: 'object', meta: 'object',
type: '{ ts: number; value: any; }', type: '{ ts: number; value: any; }',
description: 'Calculated field attribute value argument.', description: 'Calculated field context attribute value argument.',
children: { children: {
ts: { ts: {
meta: 'number', meta: 'number',
@ -270,6 +270,19 @@ export const CalculatedFieldAttributeValueArgumentAutocomplete = {
} }
}, },
}; };
export const CalculatedFieldLatestTelemetryArgumentAutocomplete = {
meta: 'any',
type: 'any',
description: 'Calculated field latest telemetry argument value.',
};
export const CalculatedFieldAttributeValueArgumentAutocomplete = {
meta: 'any',
type: 'any',
description: 'Calculated field attribute argument value.',
};
export const CalculatedFieldRollingValueArgumentFunctionsAutocomplete = { export const CalculatedFieldRollingValueArgumentFunctionsAutocomplete = {
max: { max: {
meta: 'function', meta: 'function',
@ -513,35 +526,84 @@ export const getCalculatedFieldArgumentsEditorCompleter = (argumentsObj: Record<
switch (argumentsObj[key].refEntityKey.type) { switch (argumentsObj[key].refEntityKey.type) {
case ArgumentType.Attribute: case ArgumentType.Attribute:
acc[key] = CalculatedFieldAttributeValueArgumentAutocomplete; acc[key] = CalculatedFieldAttributeValueArgumentAutocomplete;
acc.ctx.children.args.children[key] = CalculatedFieldCtxAttributeValueArgumentAutocomplete;
break; break;
case ArgumentType.LatestTelemetry: case ArgumentType.LatestTelemetry:
acc[key] = CalculatedFieldLatestTelemetryArgumentAutocomplete; acc[key] = CalculatedFieldLatestTelemetryArgumentAutocomplete;
acc.ctx.children.args.children[key] = CalculatedFieldCtxLatestTelemetryArgumentAutocomplete;
break; break;
case ArgumentType.Rolling: case ArgumentType.Rolling:
acc[key] = CalculatedFieldRollingValueArgumentAutocomplete; acc[key] = CalculatedFieldRollingValueArgumentAutocomplete;
acc.ctx.children.args.children[key] = CalculatedFieldRollingValueArgumentAutocomplete;
break; break;
} }
return acc; return acc;
}, {})); }, {
ctx: {
meta: 'object',
type: '{ args: { [key: string]: object } }',
description: 'Calculated field context.',
children: {
args: {
meta: 'object',
type: '{ [key: string]: object }',
description: 'Calculated field context arguments.',
children: {}
}
}
}
}));
} }
export const getCalculatedFieldArgumentsHighlights = ( export const getCalculatedFieldArgumentsHighlights = (
argumentsObj: Record<string, CalculatedFieldArgument> argumentsObj: Record<string, CalculatedFieldArgument>
): AceHighlightRules => { ): AceHighlightRules => {
const calculatedFieldArgumentsKeys = Object.keys(argumentsObj).map(key => ({
token: 'tb.calculated-field-key',
regex: `\\b${key}\\b`,
next: argumentsObj[key].refEntityKey.type === ArgumentType.Rolling
? 'calculatedFieldRollingArgumentValue'
: 'start'
}));
const calculatedFieldCtxArgumentsHighlightRules = {
calculatedFieldCtxArgs: [
dotOperatorHighlightRule,
...calculatedFieldArgumentsKeys.map(argumentRule => argumentRule.next === 'start' ? {...argumentRule, next: 'calculatedFieldSingleArgumentValue' } : argumentRule),
endGroupHighlightRule
]
};
return { return {
start: Object.keys(argumentsObj).map(key => ({ start: [
token: 'tb.calculated-field-key', calculatedFieldArgumentsContextHighlightRules,
regex: `\\b${key}\\b`, ...calculatedFieldArgumentsKeys,
next: argumentsObj[key].refEntityKey.type === ArgumentType.Rolling ],
? 'calculatedFieldRollingArgumentValue' ...calculatedFieldArgumentsContextValueHighlightRules,
: 'calculatedFieldSingleArgumentValue' ...calculatedFieldCtxArgumentsHighlightRules,
})),
...calculatedFieldSingleArgumentValueHighlightRules, ...calculatedFieldSingleArgumentValueHighlightRules,
...calculatedFieldRollingArgumentValueHighlightRules, ...calculatedFieldRollingArgumentValueHighlightRules,
...calculatedFieldTimeWindowArgumentValueHighlightRules ...calculatedFieldTimeWindowArgumentValueHighlightRules
}; };
}; };
const calculatedFieldArgumentsContextHighlightRules: AceHighlightRule = {
token: 'tb.calculated-field-ctx',
regex: /ctx/,
next: 'calculatedFieldCtxValue'
}
const calculatedFieldArgumentsContextValueHighlightRules: AceHighlightRules = {
calculatedFieldCtxValue: [
dotOperatorHighlightRule,
{
token: 'tb.calculated-field-args',
regex: /args/,
next: 'calculatedFieldCtxArgs'
},
endGroupHighlightRule
]
}
const calculatedFieldSingleArgumentValueHighlightRules: AceHighlightRules = { const calculatedFieldSingleArgumentValueHighlightRules: AceHighlightRules = {
calculatedFieldSingleArgumentValue: [ calculatedFieldSingleArgumentValue: [
dotOperatorHighlightRule, dotOperatorHighlightRule,
@ -597,16 +659,11 @@ const calculatedFieldTimeWindowArgumentValueHighlightRules: AceHighlightRules =
regex: /endTs/, regex: /endTs/,
next: 'no_regex' next: 'no_regex'
}, },
{
token: 'tb.calculated-field-limit',
regex: /limit/,
next: 'no_regex'
},
endGroupHighlightRule endGroupHighlightRule
] ]
} }
export const calculatedFieldDefaultScript = 'return {\n' + export const calculatedFieldDefaultScript = 'return {\n' +
' // Convert Fahrenheit to Celsius\n' + ' // Convert Fahrenheit to Celsius\n' +
' "temperatureCelsius": (temperature.value - 32) / 1.8\n' + ' "temperatureCelsius": (temperature - 32) / 1.8\n' +
'};' '};'

1
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -1070,6 +1070,7 @@
"argument-name-pattern": "Argument name is invalid.", "argument-name-pattern": "Argument name is invalid.",
"argument-name-duplicate": "Argument with such name already exists.", "argument-name-duplicate": "Argument with such name already exists.",
"argument-name-max-length": "Argument name should be less than 256 characters.", "argument-name-max-length": "Argument name should be less than 256 characters.",
"argument-name-ctx": "Argument name 'ctx' is reserved and cannot be used.",
"argument-type-required": "Argument type is required.", "argument-type-required": "Argument type is required.",
"max-args": "Maximum number of arguments reached.", "max-args": "Maximum number of arguments reached.",
"decimals-range": "Decimals by default should be a number between 0 and 15.", "decimals-range": "Decimals by default should be a number between 0 and 15.",

Loading…
Cancel
Save