diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/svg/iot-svg-object-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/svg/iot-svg-object-settings.component.html index 3e0684fd75..6ddb440827 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/svg/iot-svg-object-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/svg/iot-svg-object-settings.component.html @@ -16,7 +16,7 @@ --> -
{{ metadata?.title | customTranslate }}
+
{{ metadata?.title | customTranslate }}
scada.behavior.behavior
@@ -52,6 +52,7 @@
+
widget-config.appearance
{{ propertyRow.label | customTranslate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/svg/iot-svg-object-settings.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/svg/iot-svg-object-settings.component.scss new file mode 100644 index 0000000000..8e51b991ff --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/svg/iot-svg-object-settings.component.scss @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2024 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:host { + .iot-svg-object-title { + font-size: 18px; + font-style: normal; + font-weight: 500; + line-height: 24px; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/svg/iot-svg-object-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/svg/iot-svg-object-settings.component.ts index 7073d034ff..29c1d83332 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/svg/iot-svg-object-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/svg/iot-svg-object-settings.component.ts @@ -51,7 +51,7 @@ import { ImageService } from '@core/http/image.service'; @Component({ selector: 'tb-iot-svg-object-settings', templateUrl: './iot-svg-object-settings.component.html', - styleUrls: ['./../../widget-settings.scss'], + styleUrls: ['./iot-svg-object-settings.component.scss', './../../widget-settings.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-components.module.ts b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-components.module.ts index 13fdcd848b..7507b4cdcb 100644 --- a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-components.module.ts +++ b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-components.module.ts @@ -45,6 +45,9 @@ import { ScadaSymbolPropertyPanelComponent } from '@home/pages/scada-symbol/metadata-components/scada-symbol-property-panel.component'; import { WidgetSettingsCommonModule } from '@home/components/widget/lib/settings/common/widget-settings-common.module'; +import { + ScadaSymbolMetadataTagFunctionPanelComponent +} from '@home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tag-function-panel.component'; @NgModule({ declarations: @@ -52,6 +55,7 @@ import { WidgetSettingsCommonModule } from '@home/components/widget/lib/settings ScadaSymbolMetadataComponent, ScadaSymbolMetadataTagComponent, ScadaSymbolMetadataTagsComponent, + ScadaSymbolMetadataTagFunctionPanelComponent, ScadaSymbolBehaviorsComponent, ScadaSymbolBehaviorRowComponent, ScadaSymbolBehaviorPanelComponent, diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tag-function-panel.component.html b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tag-function-panel.component.html new file mode 100644 index 0000000000..9ca5f0b998 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tag-function-panel.component.html @@ -0,0 +1,55 @@ + + diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tag-function-panel.component.scss b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tag-function-panel.component.scss new file mode 100644 index 0000000000..31b00469ca --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tag-function-panel.component.scss @@ -0,0 +1,55 @@ +/** + * Copyright © 2016-2024 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@import '../../../../../../scss/constants'; + +.tb-scada-symbol-metadata-tag-function-panel { + width: 800px; + display: flex; + flex-direction: column; + gap: 16px; + @media #{$mat-lt-md} { + width: 90vw; + } + .tb-scada-symbol-metadata-tag-function-panel-content { + display: flex; + flex-direction: column; + gap: 16px; + overflow: auto; + margin: -10px; + padding: 10px; + } + .tb-scada-symbol-metadata-tag-function-title-container { + display: flex; + flex-direction: row; + gap: 16px; + align-items: center; + } + .tb-scada-symbol-metadata-tag-function-title { + font-size: 16px; + font-weight: 500; + line-height: 24px; + letter-spacing: 0.25px; + color: rgba(0, 0, 0, 0.87); + } + .tb-scada-symbol-metadata-tag-function-panel-buttons { + height: 40px; + display: flex; + flex-direction: row; + gap: 16px; + justify-content: flex-end; + align-items: flex-end; + } +} diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tag-function-panel.component.ts b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tag-function-panel.component.ts new file mode 100644 index 0000000000..127724938d --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tag-function-panel.component.ts @@ -0,0 +1,121 @@ +/// +/// Copyright © 2016-2024 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + AfterViewInit, + Component, + EventEmitter, + Input, + OnInit, + Output, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import { TbPopoverComponent } from '@shared/components/popover.component'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { WidgetService } from '@core/http/widget.service'; +import { TranslateService } from '@ngx-translate/core'; +import { TbEditorCompleter } from '@shared/models/ace/completion.models'; +import { TbHighlightRule } from '@shared/models/ace/ace.models'; +import { + scadaSymbolClickActionHighlightRules, + scadaSymbolClickActionPropertiesHighlightRules, + scadaSymbolElementStateRenderHighlightRules, + scadaSymbolElementStateRenderPropertiesHighlightRules +} from '@home/pages/scada-symbol/scada-symbol.models'; +import { JsFuncComponent } from '@shared/components/js-func.component'; + +@Component({ + selector: 'tb-scada-symbol-metadata-tag-function-panel', + templateUrl: './scada-symbol-metadata-tag-function-panel.component.html', + styleUrls: ['./scada-symbol-metadata-tag-function-panel.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class ScadaSymbolMetadataTagFunctionPanelComponent implements OnInit, AfterViewInit { + + @ViewChild('tagFunctionComponent') + tagFunctionComponent: JsFuncComponent; + + @Input() + tagFunction: string; + + @Input() + tagFunctionType: 'renderFunction' | 'clickAction'; + + @Input() + tag: string; + + @Input() + completer: TbEditorCompleter; + + @Input() + popover: TbPopoverComponent; + + @Output() + tagFunctionApplied = new EventEmitter(); + + functionScopeVariables = this.widgetService.getWidgetScopeVariables(); + + tagFunctionFormGroup: UntypedFormGroup; + + panelTitle: string; + + tagFunctionArgs: string[]; + + tagFunctionHelpId: string; + + objectHighlightRules: TbHighlightRule[]; + + propertyHighlightRules: TbHighlightRule[]; + + constructor(private fb: UntypedFormBuilder, + private widgetService: WidgetService) { + } + + ngOnInit(): void { + this.tagFunctionFormGroup = this.fb.group( + { + tagFunction: [this.tagFunction, []] + } + ); + if (this.tagFunctionType === 'renderFunction') { + this.panelTitle = 'scada.state-render-function'; + this.tagFunctionArgs = ['ctx', 'element']; + this.objectHighlightRules = scadaSymbolElementStateRenderHighlightRules; + this.propertyHighlightRules = scadaSymbolElementStateRenderPropertiesHighlightRules; + this.tagFunctionHelpId = 'widget/lib/timeseries/cell_style_fn'; // TODO: + } else if (this.tagFunctionType === 'clickAction') { + this.panelTitle = 'scada.tag.on-click-action'; + this.tagFunctionArgs = ['ctx', 'element', 'event']; + this.objectHighlightRules = scadaSymbolClickActionHighlightRules; + this.propertyHighlightRules = scadaSymbolClickActionPropertiesHighlightRules; + this.tagFunctionHelpId = 'widget/lib/timeseries/cell_style_fn'; // TODO: + } + } + + ngAfterViewInit() { + this.tagFunctionComponent.focus(); + } + + cancel() { + this.popover?.hide(); + } + + applyTagFunction() { + const tagFunction: string = this.tagFunctionFormGroup.get('tagFunction').value; + this.tagFunctionApplied.emit(tagFunction); + } +} 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 20dfc9b203..3e57c1ef30 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 @@ -15,8 +15,35 @@ limitations under the License. --> -
- + diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tag.component.scss b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tag.component.scss index 1e0bacd858..08296a986e 100644 --- a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tag.component.scss +++ b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tag.component.scss @@ -13,10 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -.tb-scada-symbol-metadata-tag-header { - flex: 1; - display: flex; - flex-direction: row; - gap: 8px; - padding-right: 8px; +.tb-scada-symbol-metadata-tag-row { + .tb-tag-field { + flex: 1 1 70%; + } + .tb-state-render-function-field { + flex: 1 1 15%; + } + .tb-on-click-action-field { + flex: 1 1 15%; + } + .tb-state-render-function-field, .tb-on-click-action-field { + display: flex; + justify-content: flex-end; + .mdc-button { + .mat-mdc-button-touch-target { + height: 36px; + } + } + } } 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 91de73e76c..cb9b950f2c 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 @@ -21,12 +21,13 @@ import { Input, OnChanges, OnInit, - Output, + Output, Renderer2, SimpleChanges, - ViewChild, + ViewChild, ViewContainerRef, ViewEncapsulation } from '@angular/core'; import { + AbstractControl, ControlValueAccessor, NG_VALIDATORS, NG_VALUE_ACCESSOR, @@ -47,6 +48,15 @@ import { scadaSymbolElementStateRenderHighlightRules, scadaSymbolElementStateRenderPropertiesHighlightRules } from '@home/pages/scada-symbol/scada-symbol.models'; +import { MatButton } from '@angular/material/button'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { deepClone } from '@core/utils'; +import { + ScadaSymbolBehaviorPanelComponent +} from '@home/pages/scada-symbol/metadata-components/scada-symbol-behavior-panel.component'; +import { + ScadaSymbolMetadataTagFunctionPanelComponent +} from '@home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tag-function-panel.component'; @Component({ selector: 'tb-scada-symbol-metadata-tag', @@ -66,83 +76,41 @@ import { ], encapsulation: ViewEncapsulation.None }) -export class ScadaSymbolMetadataTagComponent implements ControlValueAccessor, OnInit, OnChanges, Validator { - - @ViewChild('tagSelect') - tagSelect: MatSelect; - - @ViewChild('expansionPanel') - expansionPanel: MatExpansionPanel; +export class ScadaSymbolMetadataTagComponent implements ControlValueAccessor, OnInit, Validator { - @ViewChild('renderFunctionExpansionPanel') - renderFunctionExpansionPanel: MatExpansionPanel; + @ViewChild('editStateRenderFunctionButton') + editStateRenderFunctionButton: MatButton; - @ViewChild('clickActionExpansionPanel') - clickActionExpansionPanel: MatExpansionPanel; - - @ViewChild('stateRenderFunction') - stateRenderFunction: JsFuncComponent; - - @ViewChild('clickAction') - clickAction: JsFuncComponent; + @ViewChild('editClickActionButton') + editClickActionButton: MatButton; @Input() disabled: boolean; - @Input() - tags: string[]; - @Input() elementStateRenderFunctionCompleter: TbEditorCompleter; @Input() clickActionFunctionCompleter: TbEditorCompleter; - @Output() - tagRemoved = new EventEmitter(); - - availableTags: string[]; - tagFormGroup: UntypedFormGroup; modelValue: IotSvgTag; - scadaSymbolElementStateRenderHighlightRules = scadaSymbolElementStateRenderHighlightRules; - scadaSymbolElementStateRenderPropertiesHighlightRules = scadaSymbolElementStateRenderPropertiesHighlightRules; - - scadaSymbolClickActionHighlightRules = scadaSymbolClickActionHighlightRules; - scadaSymbolClickActionPropertiesHighlightRules = scadaSymbolClickActionPropertiesHighlightRules; - private propagateChange = (_val: any) => {}; - constructor(private fb: UntypedFormBuilder) { + constructor(private fb: UntypedFormBuilder, + private popoverService: TbPopoverService, + private renderer: Renderer2, + private viewContainerRef: ViewContainerRef) { } ngOnInit() { - this.availableTags = (this.tags || []).concat(); this.tagFormGroup = this.fb.group({ - tag: [null, [Validators.required]], + tag: [null, []], stateRenderFunction: [null, []], clickAction: [null, []] }); - this.tagFormGroup.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.availableTags = (this.tags || []).concat(); - if (this.modelValue?.tag && !this.availableTags.includes(this.modelValue?.tag)) { - this.availableTags.push(this.modelValue?.tag); - this.availableTags.sort(); - } - } - } - } } registerOnChange(fn: any): void { @@ -163,9 +131,6 @@ export class ScadaSymbolMetadataTagComponent implements ControlValueAccessor, On writeValue(value: IotSvgTag): void { this.modelValue = value; - if (value?.tag && !(this.tags || []).includes(value?.tag)) { - this.availableTags = (this.tags || []).concat(value.tag).sort(); - } const clickAction = value?.actions && value?.actions.click ? value.actions.click.actionFunction : null; this.tagFormGroup.patchValue( { @@ -186,27 +151,61 @@ export class ScadaSymbolMetadataTagComponent implements ControlValueAccessor, On } editTagStateRenderFunction(): void { - this.openPanelWithCallback(this.expansionPanel, () => { + this.openTagFunction('renderFunction', this.editStateRenderFunctionButton); + /*this.openPanelWithCallback(this.expansionPanel, () => { this.openPanelWithCallback(this.renderFunctionExpansionPanel, () => { this.stateRenderFunction.focus(); }); - }); + });*/ } editClickAction(): void { - this.openPanelWithCallback(this.expansionPanel, () => { + this.openTagFunction('clickAction', this.editClickActionButton); + /*this.openPanelWithCallback(this.expansionPanel, () => { this.openPanelWithCallback(this.clickActionExpansionPanel, () => { this.clickAction.focus(); }); - }); + });*/ } - focus() { - this.tagSelect._elementRef.nativeElement.scrollIntoView(); - this.tagSelect.focus(); + private openTagFunction(tagFunctionType: 'renderFunction' | 'clickAction', + button: MatButton) { + const trigger = button._elementRef.nativeElement; + trigger.scrollIntoView(); + if (this.popoverService.hasPopover(trigger)) { + this.popoverService.hidePopover(trigger); + } else { + let tagFunctionControl: AbstractControl; + let completer: TbEditorCompleter; + if (tagFunctionType === 'renderFunction') { + tagFunctionControl = this.tagFormGroup.get('stateRenderFunction'); + completer = this.elementStateRenderFunctionCompleter; + } else if (tagFunctionType === 'clickAction') { + tagFunctionControl = this.tagFormGroup.get('clickAction'); + completer = this.clickActionFunctionCompleter; + } + const ctx: any = { + tagFunction: tagFunctionControl.value, + tagFunctionType, + tag: this.tagFormGroup.get('tag').value, + completer + }; + const scadaSymbolTagFunctionPanelPopover = this.popoverService.displayPopover(trigger, this.renderer, + this.viewContainerRef, ScadaSymbolMetadataTagFunctionPanelComponent, + ['leftOnly', 'leftTopOnly', 'leftBottomOnly'], true, null, + ctx, + {}, + {}, {}, true); + scadaSymbolTagFunctionPanelPopover.tbComponentRef.instance.popover = scadaSymbolTagFunctionPanelPopover; + scadaSymbolTagFunctionPanelPopover.tbComponentRef.instance.tagFunctionApplied.subscribe((tagFunction) => { + scadaSymbolTagFunctionPanelPopover.hide(); + tagFunctionControl.patchValue(tagFunction, {emitEvent: false}); + this.updateModel(); + }); + } } - private openPanelWithCallback(panel: MatExpansionPanel, callback: () => void) { +/* private openPanelWithCallback(panel: MatExpansionPanel, callback: () => void) { if (!panel.expanded) { const s = panel.afterExpand.subscribe(() => { s.unsubscribe(); @@ -218,13 +217,10 @@ export class ScadaSymbolMetadataTagComponent implements ControlValueAccessor, On } else { callback(); } - } + }*/ private updateModel() { const value = this.tagFormGroup.value; - if (value.tag && !this.modelValue?.tag) { - this.expansionPanel.open(); - } this.modelValue = { tag: value.tag, stateRenderFunction: value.stateRenderFunction 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 6ce57301af..ec0f69a7f0 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 @@ -16,23 +16,25 @@ --> `, styleUrls: ['./scada-symbol-tooltip.component.scss'], diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 1d49829f21..b70f4cee72 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -3414,13 +3414,18 @@ "title": "Title", "state-render-function": "State render function", "preview": "Preview", - "tag": "Tag", - "on-click-action": "On click action", - "remove-tag": "Remove tag", - "add-tag": "Add tag", - "no-tags": "No tags configured", - "select-tag": "Select tag", "preview-widget-action-text": "Widget action '{{type}}' successfully invoked!", + "tag": { + "tag": "Tag", + "on-click-action": "On click action", + "no-tags": "No tags configured", + "delete-tag-text": "Are you sure you want to delete tag
{{tag}} from {{elementType}} element?", + "update-tag": "Update tag", + "enter-tag": "Enter tag", + "tag-settings": "Tag settings", + "remove-tag": "Remove tag", + "add-tag": "Add tag" + }, "behavior": { "behavior": "Behavior", "id": "Id", diff --git a/ui-ngx/src/styles.scss b/ui-ngx/src/styles.scss index f741fa5a31..d6a5d0c8ea 100644 --- a/ui-ngx/src/styles.scss +++ b/ui-ngx/src/styles.scss @@ -392,6 +392,7 @@ mat-icon { } .tb-ace-doc-tooltip { + white-space: pre-wrap; code { color: #444; &.title {