SVG.Svg',
- description: 'A root svg node. Instance of
SVG.Svg.'
+ type: '
SVG.Svg',
+ description: 'A root svg node. Instance of
SVG.Svg.'
}
});
@@ -1114,8 +1115,9 @@ export const elementStateRenderFunctionCompletions = (ctxCompletion: TbEditorCom
meta: 'argument',
type: 'Element',
description: 'SVG element.
' +
- 'See
Manipulating section to manipulate the element.
' +
- 'See
Animating section to animate the element.'
+ 'See
Manipulating section to manipulate the element.
' +
+ 'See
Animating section to animate the element.'
}
});
@@ -1131,6 +1133,10 @@ export const clickActionFunctionCompletions = (ctxCompletion: TbEditorCompletion
export const scadaSymbolContextCompletion = (metadata: ScadaSymbolMetadata, tags: string[],
customTranslate: CustomTranslatePipe): TbEditorCompletion => {
+
+ const scadaSymbolAnimationLink = HelpLinks.linksMap.scadaSymbolDevAnimation;
+ const scadaSymbolAnimation = `
ScadaSymbolAnimation`;
+
const properties: TbEditorCompletion = {
meta: 'object',
type: 'object',
@@ -1191,9 +1197,8 @@ export const scadaSymbolContextCompletion = (metadata: ScadaSymbolMetadata, tags
}
],
return: {
- description: 'Instance of ScadaSymbolAnimation which has generally similar methods as ' +
- '
SVG.Runner to control the animation.',
- type: 'ScadaSymbolAnimation'
+ description: `Instance of ${scadaSymbolAnimation} class with API to setup and control the animation.`,
+ type: scadaSymbolAnimation
}
},
cssAnimation: {
@@ -1207,9 +1212,8 @@ export const scadaSymbolContextCompletion = (metadata: ScadaSymbolMetadata, tags
}
],
return: {
- description: 'Instance of ScadaSymbolAnimation which has generally similar methods as ' +
- '
SVG.Runner to control the animation.',
- type: 'ScadaSymbolAnimation'
+ description: `Instance of ${scadaSymbolAnimation} class with API to setup and control the animation.`,
+ type: scadaSymbolAnimation
}
},
resetCssAnimation: {
@@ -1280,8 +1284,8 @@ export const scadaSymbolContextCompletion = (metadata: ScadaSymbolMetadata, tags
text: {
meta: 'function',
description: 'Set text to element(s). Only applicable for elements of type ' +
- '
SVG.Text or ' +
- '
SVG.Tspan.',
+ '
SVG.Text or ' +
+ '
SVG.Tspan.',
args: [
{
name: 'element',
@@ -1298,8 +1302,8 @@ export const scadaSymbolContextCompletion = (metadata: ScadaSymbolMetadata, tags
font: {
meta: 'function',
description: 'Set element(s) text font and color. Only applicable for elements of type ' +
- '
SVG.Text or ' +
- '
SVG.Tspan.',
+ '
SVG.Text or ' +
+ '
SVG.Tspan.',
args: [
{
name: 'element',
@@ -1321,7 +1325,7 @@ export const scadaSymbolContextCompletion = (metadata: ScadaSymbolMetadata, tags
icon: {
meta: 'function',
description: 'Draws icon inside element(s). Only applicable for elements of type ' +
- '
SVG.G.',
+ '
SVG.G.',
args: [
{
name: 'element',
@@ -1429,8 +1433,9 @@ export const scadaSymbolContextCompletion = (metadata: ScadaSymbolMetadata, tags
tags: tagsCompletions,
svg: {
meta: 'argument',
- type: '
SVG.Svg',
- description: 'A root svg node. Instance of
SVG.Svg.'
+ type: '
SVG.Svg',
+ description: 'A root svg node. Instance of
SVG.Svg.'
}
}
};
diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-editor.models.ts b/ui-ngx/src/app/modules/home/pages/widget/widget-editor.models.ts
index e4d2af97af..cb4a3805f3 100644
--- a/ui-ngx/src/app/modules/home/pages/widget/widget-editor.models.ts
+++ b/ui-ngx/src/app/modules/home/pages/widget/widget-editor.models.ts
@@ -21,7 +21,7 @@ import { serviceCompletions } from '@shared/models/ace/service-completion.models
const widgetEditorCompletions: TbEditorCompletions = {
... {self: {
description: 'Built-in variable
self that is a reference to the widget instance',
- type: '
WidgetTypeInstance',
+ type: '
WidgetTypeInstance',
meta: 'object',
children: {
...{
@@ -31,19 +31,19 @@ const widgetEditorCompletions: TbEditorCompletions = {
},
onDataUpdated: {
description: 'Called when the new data is available from the widget subscription.
Latest data can be accessed from ' +
- 'the
defaultSubscription property of
widget context (ctx).',
+ 'the
defaultSubscription property of
widget context (ctx).',
meta: 'function'
},
onResize: {
- description: 'Called when widget container is resized. Latest
width and
height can be obtained from
widget context (ctx).',
+ description: 'Called when widget container is resized. Latest
width and
height can be obtained from
widget context (ctx).',
meta: 'function'
},
onEditModeChanged: {
- description: 'Called when dashboard editing mode is changed. Latest mode is handled by
isEdit property of
widget context (ctx).',
+ description: 'Called when dashboard editing mode is changed. Latest mode is handled by
isEdit property of
widget context (ctx).',
meta: 'function'
},
onMobileModeChanged: {
- description: 'Called when dashboard view width crosses mobile breakpoint. Latest state is handled by
isMobile property of
widget context (ctx).',
+ description: 'Called when dashboard view width crosses mobile breakpoint. Latest state is handled by
isMobile property of
widget context (ctx).',
meta: 'function'
},
onDestroy: {
@@ -51,7 +51,7 @@ const widgetEditorCompletions: TbEditorCompletions = {
meta: 'function'
},
getSettingsSchema: {
- description: 'Optional function returning widget settings schema json as alternative to
Settings tab of
Settings schema section.',
+ description: 'Optional function returning widget settings schema json as alternative to
Settings tab of
Settings schema section.',
meta: 'function',
return: {
description: 'An widget settings schema json',
@@ -59,7 +59,7 @@ const widgetEditorCompletions: TbEditorCompletions = {
}
},
getDataKeySettingsSchema: {
- description: 'Optional function returning particular data key settings schema json as alternative to
Data key settings schema of
Settings schema section.',
+ description: 'Optional function returning particular data key settings schema json as alternative to
Data key settings schema of
Settings schema section.',
meta: 'function',
return: {
description: 'A particular data key settings schema json',
@@ -71,7 +71,7 @@ const widgetEditorCompletions: TbEditorCompletions = {
meta: 'function',
return: {
description: 'An object describing widget datasource parameters.',
- type: '
WidgetTypeParameters'
+ type: '
WidgetTypeParameters'
}
},
actionSources: {
@@ -79,7 +79,7 @@ const widgetEditorCompletions: TbEditorCompletions = {
meta: 'function',
return: {
description: 'A map of action sources by action source id.',
- type: '{[actionSourceId: string]:
WidgetActionSource}'
+ type: '{[actionSourceId: string]:
WidgetActionSource}'
}
}
},
diff --git a/ui-ngx/src/app/shared/directives/context-menu.directive.ts b/ui-ngx/src/app/shared/directives/context-menu.directive.ts
new file mode 100644
index 0000000000..ada727cfe2
--- /dev/null
+++ b/ui-ngx/src/app/shared/directives/context-menu.directive.ts
@@ -0,0 +1,35 @@
+///
+/// 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 { Directive, ElementRef, EventEmitter, OnDestroy, Output } from '@angular/core';
+import { TbContextMenuEvent } from '@shared/models/jquery-event.models';
+
+@Directive({
+ selector: '[tbcontextmenu]'
+})
+export class ContextMenuDirective implements OnDestroy {
+
+ @Output()
+ tbcontextmenu = new EventEmitter
();
+
+ constructor(private el: ElementRef) {
+ $(this.el.nativeElement).on('tbcontextmenu', (e: TbContextMenuEvent) => this.tbcontextmenu.emit(e));
+ }
+
+ ngOnDestroy() {
+ $(this.el.nativeElement).off('tbcontextmenu');
+ }
+}
diff --git a/ui-ngx/src/app/shared/directives/public-api.ts b/ui-ngx/src/app/shared/directives/public-api.ts
index 6f25320d90..7e0902e223 100644
--- a/ui-ngx/src/app/shared/directives/public-api.ts
+++ b/ui-ngx/src/app/shared/directives/public-api.ts
@@ -16,3 +16,4 @@
export * from './truncate-with-tooltip.directive';
export * from './ellipsis-chip-list.directive';
+export * from './context-menu.directive';
diff --git a/ui-ngx/src/app/shared/models/ace/service-completion.models.ts b/ui-ngx/src/app/shared/models/ace/service-completion.models.ts
index dd7fb74dc8..5efc428194 100644
--- a/ui-ngx/src/app/shared/models/ace/service-completion.models.ts
+++ b/ui-ngx/src/app/shared/models/ace/service-completion.models.ts
@@ -16,101 +16,101 @@
import { FunctionArg, FunctionArgType, TbEditorCompletions } from '@shared/models/ace/completion.models';
-export const entityIdHref = 'EntityId';
+export const entityIdHref = 'EntityId';
-export const baseDataHref = 'Base data';
+export const baseDataHref = 'Base data';
-export const alarmDataHref = 'Alarm data';
+export const alarmDataHref = 'Alarm data';
-export const alarmDataQueryHref = 'Alarm data query';
+export const alarmDataQueryHref = 'Alarm data query';
-export const attributeScopeHref = 'Attribute scope';
+export const attributeScopeHref = 'Attribute scope';
-export const entityTypeHref = 'EntityType';
+export const entityTypeHref = 'EntityType';
-export const pageDataHref = 'PageData';
+export const pageDataHref = 'PageData';
-export const deviceInfoHref = 'DeviceInfo';
+export const deviceInfoHref = 'DeviceInfo';
-export const assetInfoHref = 'AssetInfo';
+export const assetInfoHref = 'AssetInfo';
-export const entityViewInfoHref = 'EntityViewInfo';
+export const entityViewInfoHref = 'EntityViewInfo';
-export const entityRelationsQueryHref = 'EntityRelationsQuery';
+export const entityRelationsQueryHref = 'EntityRelationsQuery';
-export const entityRelationInfoHref = 'EntityRelationInfo';
+export const entityRelationInfoHref = 'EntityRelationInfo';
-export const dashboardInfoHref = 'DashboardInfo';
+export const dashboardInfoHref = 'DashboardInfo';
-export const deviceHref = 'Device';
+export const deviceHref = 'Device';
-export const assetHref = 'Asset';
+export const assetHref = 'Asset';
-export const entityViewHref = 'entityView';
+export const entityViewHref = 'entityView';
-export const entityRelationHref = 'Entity relation';
+export const entityRelationHref = 'Entity relation';
-export const dashboardHref = 'Dashboard';
+export const dashboardHref = 'Dashboard';
-export const customerHref = 'Customer';
+export const customerHref = 'Customer';
-export const attributeDataHref = 'Attribute Data';
+export const attributeDataHref = 'Attribute Data';
-export const timeseriesDataHref = 'Timeseries Data';
+export const timeseriesDataHref = 'Timeseries Data';
-export const aggregationTypeHref = 'Aggregation Type';
+export const aggregationTypeHref = 'Aggregation Type';
-export const dataSortOrderHref = 'Data Sort Order';
+export const dataSortOrderHref = 'Data Sort Order';
-export const userHref = 'User';
+export const userHref = 'User';
-export const entityDataHref = 'Entity data';
+export const entityDataHref = 'Entity data';
-export const entityDataQueryHref = 'Entity Data Query';
+export const entityDataQueryHref = 'Entity Data Query';
-export const deviceCredentialsHref = 'DeviceCredentials';
+export const deviceCredentialsHref = 'DeviceCredentials';
-export const entityFilterHref = 'Entity filter';
+export const entityFilterHref = 'Entity filter';
-export const entityInfoHref = 'Entity info';
+export const entityInfoHref = 'Entity info';
-export const aliasEntityTypeHref = 'Alias Entity Type';
+export const aliasEntityTypeHref = 'Alias Entity Type';
-export const aliasFilterTypeHref = 'Alias filter type';
+export const aliasFilterTypeHref = 'Alias filter type';
-export const entityAliasHref = 'Entity alias';
+export const entityAliasHref = 'Entity alias';
-export const dataKeyTypeHref = 'Data key type';
+export const dataKeyTypeHref = 'Data key type';
-export const subscriptionInfoHref = 'Subscription info';
+export const subscriptionInfoHref = 'Subscription info';
-export const dataSourceHref = 'Datasource';
+export const dataSourceHref = 'Datasource';
-export const stateParamsHref = 'State params';
+export const stateParamsHref = 'State params';
-export const aliasInfoHref = 'Alias info';
+export const aliasInfoHref = 'Alias info';
-export const entityAliasFilterHref = 'Entity alias filter';
+export const entityAliasFilterHref = 'Entity alias filter';
-export const entityAliasFilterResultHref = 'Entity alias filter result';
+export const entityAliasFilterResultHref = 'Entity alias filter result';
-export const importEntityDataHref = 'Import entity data';
+export const importEntityDataHref = 'Import entity data';
-export const importEntitiesResultInfoHref = 'Import entities result info';
+export const importEntitiesResultInfoHref = 'Import entities result info';
-export const customDialogComponentHref = 'CustomDialogComponent';
+export const customDialogComponentHref = 'CustomDialogComponent';
-export const resourceInfoHref = 'Resource info';
+export const resourceInfoHref = 'Resource info';
export const pageLinkArg: FunctionArg = {
name: 'pageLink',
- type: 'PageLink',
+ type: 'PageLink',
description: 'Page link object used to perform paginated request.'
};
export const requestConfigArg: FunctionArg = {
name: 'config',
- type: 'RequestConfig',
+ type: 'RequestConfig',
description: 'HTTP request configuration.',
optional: true
};
@@ -167,9 +167,9 @@ export function observablePageDataReturnType(objectType: string): FunctionArgTyp
export const serviceCompletions: TbEditorCompletions = {
deviceService: {
description: 'Device Service API
' +
- 'See DeviceService for API reference.',
+ 'See DeviceService for API reference.',
meta: 'service',
- type: 'DeviceService',
+ type: 'DeviceService',
children: {
getTenantDeviceInfos: {
description: 'Get tenant devices',
@@ -243,7 +243,7 @@ export const serviceCompletions: TbEditorCompletions = {
args: [
requestConfigArg
],
- return: observableArrayReturnType('EntitySubtype')
+ return: observableArrayReturnType('EntitySubtype')
},
getDeviceCredentials: {
description: 'Get device credentials by device id',
@@ -322,7 +322,7 @@ export const serviceCompletions: TbEditorCompletions = {
description: 'Find devices by search query',
meta: 'function',
args: [
- { name: 'query', type: 'DeviceSearchQuery',
+ { name: 'query', type: 'DeviceSearchQuery',
description: 'Device search query object'},
requestConfigArg
],
@@ -346,7 +346,7 @@ export const serviceCompletions: TbEditorCompletions = {
description: 'Claiming device name'},
requestConfigArg
],
- return: observableReturnType('ClaimResult')
+ return: observableReturnType('ClaimResult')
},
unclaimDevice: {
description: 'Send un-claim device request',
@@ -362,9 +362,9 @@ export const serviceCompletions: TbEditorCompletions = {
},
assetService: {
description: 'Asset Service API
' +
- 'See AssetService for API reference.',
+ 'See AssetService for API reference.',
meta: 'service',
- type: 'AssetService',
+ type: 'AssetService',
children: {
getTenantAssetInfos: {
description: 'Get tenant assets',
@@ -438,7 +438,7 @@ export const serviceCompletions: TbEditorCompletions = {
args: [
requestConfigArg
],
- return: observableArrayReturnType('EntitySubtype')
+ return: observableArrayReturnType('EntitySubtype')
},
makeAssetPublic: {
description: 'Make asset public (available from public dashboard)',
@@ -474,7 +474,7 @@ export const serviceCompletions: TbEditorCompletions = {
args: [
{
name: 'query',
- type: 'AssetSearchQuery',
+ type: 'AssetSearchQuery',
description: 'Asset search query object'
},
requestConfigArg
@@ -497,9 +497,9 @@ export const serviceCompletions: TbEditorCompletions = {
},
entityViewService: {
description: 'EntityView Service API
' +
- 'See EntityViewService for API reference.',
+ 'See EntityViewService for API reference.',
meta: 'service',
- type: 'EntityViewService',
+ type: 'EntityViewService',
children: {
getTenantEntityViewInfos: {
description: 'Get tenant entity view infos',
@@ -564,7 +564,7 @@ export const serviceCompletions: TbEditorCompletions = {
args: [
requestConfigArg
],
- return: observableArrayReturnType('EntitySubtype')
+ return: observableArrayReturnType('EntitySubtype')
},
makeEntityViewPublic: {
description: 'Make entity view public (available from public dashboard)',
@@ -600,7 +600,7 @@ export const serviceCompletions: TbEditorCompletions = {
args: [
{
name: 'query',
- type: 'AssetSearchQuery',
+ type: 'AssetSearchQuery',
description: 'Entity view search query object'
},
requestConfigArg
@@ -611,9 +611,9 @@ export const serviceCompletions: TbEditorCompletions = {
},
customerService: {
description: 'Customer Service API
' +
- 'See CustomerService for API reference.',
+ 'See CustomerService for API reference.',
meta: 'service',
- type: 'CustomerService',
+ type: 'CustomerService',
children: {
getCustomer: {
description: 'Get customer by id',
@@ -655,9 +655,9 @@ export const serviceCompletions: TbEditorCompletions = {
},
dashboardService: {
description: 'Dashboard Service API
' +
- 'See DashboardService for API reference.',
+ 'See DashboardService for API reference.',
meta: 'service',
- type: 'DashboardService',
+ type: 'DashboardService',
children: {
getTenantDashboards: {
description: 'Get tenant dashboards',
@@ -814,9 +814,9 @@ export const serviceCompletions: TbEditorCompletions = {
},
userService: {
description: 'User Service API
' +
- 'See UserService for API reference.',
+ 'See UserService for API reference.',
meta: 'service',
- type: 'UserService',
+ type: 'UserService',
children: {
getUsers: {
description: 'Get users',
@@ -907,9 +907,9 @@ export const serviceCompletions: TbEditorCompletions = {
},
entityRelationService: {
description: 'Entity Relation Service API
' +
- 'See EntityRelationService for API reference.',
+ 'See EntityRelationService for API reference.',
meta: 'service',
- type: 'EntityRelationService',
+ type: 'EntityRelationService',
children: {
saveRelation: {
description: 'Save relation',
@@ -1029,9 +1029,9 @@ export const serviceCompletions: TbEditorCompletions = {
},
attributeService: {
description: 'Attribute Service API
' +
- 'See AttributeService for API reference.',
+ 'See AttributeService for API reference.',
meta: 'service',
- type: 'AttributeService',
+ type: 'AttributeService',
children: {
getEntityAttributes: {
description: 'Get entity attributes by id',
@@ -1109,9 +1109,9 @@ export const serviceCompletions: TbEditorCompletions = {
},
entityService: {
description: 'Entity Service API
' +
- 'See EntityService for API reference.',
+ 'See EntityService for API reference.',
meta: 'service',
- type: 'EntityService',
+ type: 'EntityService',
children: {
getEntity: {
description: 'Get entity by id',
@@ -1304,9 +1304,9 @@ export const serviceCompletions: TbEditorCompletions = {
},
resourceService: {
description: 'Resource Service API
' +
- 'See ResourceService for API reference.',
+ 'See ResourceService for API reference.',
meta: 'service',
- type: 'ResourceService',
+ type: 'ResourceService',
children: {
getResources: {
description: 'Find resources by search text',
@@ -1321,9 +1321,9 @@ export const serviceCompletions: TbEditorCompletions = {
},
dialogs: {
description: 'Dialogs Service API
' +
- 'See DialogService for API reference.',
+ 'See DialogService for API reference.',
meta: 'service',
- type: 'DialogService',
+ type: 'DialogService',
children: {
confirm: {
description: 'Confirm',
@@ -1382,9 +1382,9 @@ export const serviceCompletions: TbEditorCompletions = {
},
customDialog: {
description: 'Custom Dialog Service API
' +
- 'See CustomDialogService for API reference.',
+ 'See CustomDialogService for API reference.',
meta: 'service',
- type: 'CustomDialogService',
+ type: 'CustomDialogService',
children: {
customDialog: {
description: 'Custom Dialog',
@@ -1400,32 +1400,32 @@ export const serviceCompletions: TbEditorCompletions = {
},
date: {
description: 'Date Pipe
Formats a date value according to locale rules.
' +
- 'See DatePipe for API reference.',
+ 'See DatePipe for API reference.',
meta: 'service',
- type: 'DatePipe'
+ type: 'DatePipe'
},
translate: {
description: 'Translate Service API
' +
- 'See TranslateService for API reference.',
+ 'See TranslateService for API reference.',
meta: 'service',
- type: 'TranslateService'
+ type: 'TranslateService'
},
http: {
description: 'HTTP Client Service
' +
- 'See HttpClient for API reference.',
+ 'See HttpClient for API reference.',
meta: 'service',
- type: 'HttpClient'
+ type: 'HttpClient'
},
sanitizer: {
description: 'DomSanitizer Service
' +
- 'See DomSanitizer for API reference.',
+ 'See DomSanitizer for API reference.',
meta: 'service',
- type: 'DomSanitizer'
+ type: 'DomSanitizer'
},
router: {
description: 'Router Service
' +
- 'See Router for API reference.',
+ 'See Router for API reference.',
meta: 'service',
- type: 'Router'
+ type: 'Router'
}
};
diff --git a/ui-ngx/src/app/shared/models/ace/widget-completion.models.ts b/ui-ngx/src/app/shared/models/ace/widget-completion.models.ts
index e54e52bdbc..b2b0b6235d 100644
--- a/ui-ngx/src/app/shared/models/ace/widget-completion.models.ts
+++ b/ui-ngx/src/app/shared/models/ace/widget-completion.models.ts
@@ -20,7 +20,7 @@ import { entityIdHref, serviceCompletions } from '@shared/models/ace/service-com
export const timewindowCompletion: TbEditorCompletion = {
description: 'Timewindow configuration object',
meta: 'property',
- type: 'Timewindow',
+ type: 'Timewindow',
children: {
displayValue: {
description: 'Current timewindow display value.',
@@ -50,7 +50,7 @@ export const timewindowCompletion: TbEditorCompletion = {
realtime: {
description: 'Realtime timewindow configuration object.',
meta: 'property',
- type: 'IntervalWindow',
+ type: 'IntervalWindow',
children: {
interval: {
description: 'Timewindow aggregation interval in milliseconds',
@@ -67,7 +67,7 @@ export const timewindowCompletion: TbEditorCompletion = {
history: {
description: 'History timewindow configuration object.',
meta: 'property',
- type: 'HistoryWindow',
+ type: 'HistoryWindow',
children: {
historyType: {
description: 'History timewindow type (0 - last interval, 1 - fixed)',
@@ -87,7 +87,7 @@ export const timewindowCompletion: TbEditorCompletion = {
fixedTimewindow: {
description: 'Fixed history timewindow configuration object',
meta: 'property',
- type: 'FixedWindow',
+ type: 'FixedWindow',
children: {
startTimeMs: {
description: 'Timewindow start time in UTC milliseconds',
@@ -106,7 +106,7 @@ export const timewindowCompletion: TbEditorCompletion = {
aggregation: {
description: 'Timewindow aggregation configuration object.',
meta: 'property',
- type: 'Aggregation',
+ type: 'Aggregation',
children: {
interval: {
description: 'Aggregation interval in milliseconds',
@@ -116,7 +116,7 @@ export const timewindowCompletion: TbEditorCompletion = {
type: {
description: 'Aggregation type',
meta: 'property',
- type: 'AggregationType'
+ type: 'AggregationType'
},
limit: {
description: 'Maximum allowed datapoints when aggregation is disabled (AggregationType == \'NONE\')',
@@ -132,7 +132,7 @@ export const widgetContextCompletions: TbEditorCompletions = {
ctx: {
description: 'A reference to widget context that has all necessary API
and data used by widget instance.',
meta: 'object',
- type: 'WidgetContext',
+ type: 'WidgetContext',
children: {
...{
$container: {
@@ -143,7 +143,7 @@ export const widgetContextCompletions: TbEditorCompletions = {
$scope: {
description: 'Reference to the current widget component.
Can be used to access/modify component properties when widget is built using Angular approach.',
meta: 'property',
- type: 'IDynamicWidgetComponent'
+ type: 'IDynamicWidgetComponent'
},
width: {
description: 'Current width of widget container in pixels.',
@@ -168,7 +168,7 @@ export const widgetContextCompletions: TbEditorCompletions = {
widgetConfig: {
description: 'Common widget configuration containing properties such as color (text color), backgroundColor (widget background color), etc.',
meta: 'property',
- type: 'WidgetConfig',
+ type: 'WidgetConfig',
children: {
title: {
description: 'Widget title.',
@@ -233,17 +233,17 @@ export const widgetContextCompletions: TbEditorCompletions = {
legendConfig: {
description: 'Legend configuration.',
meta: 'property',
- type: 'LegendConfig',
+ type: 'LegendConfig',
children: {
position: {
description: 'Legend position. Possible values: \'top\', \'bottom\', \'left\', \'right\'',
meta: 'property',
- type: 'LegendPosition',
+ type: 'LegendPosition',
},
direction: {
description: 'Legend direction. Possible values: \'column\', \'row\'',
meta: 'property',
- type: 'LegendDirection',
+ type: 'LegendDirection',
},
showMin: {
description: 'Whether to display aggregated min values.',
@@ -331,12 +331,12 @@ export const widgetContextCompletions: TbEditorCompletions = {
alarmSource: {
description: 'Configured alarm source for alarm widget type.',
meta: 'property',
- type: 'Datasource'
+ type: 'Datasource'
},
alarmSearchStatus: {
description: 'Configured default alarm search status for alarm widget type.',
meta: 'property',
- type: 'AlarmSearchStatus'
+ type: 'AlarmSearchStatus'
},
alarmsPollingInterval: {
description: 'Configured alarms polling interval for alarm widget type.',
@@ -356,29 +356,29 @@ export const widgetContextCompletions: TbEditorCompletions = {
datasources: {
description: 'Array of configured widget datasources.',
meta: 'property',
- type: 'Array<Datasource>'
+ type: 'Array<Datasource>'
}
}
},
settings: {
- description: 'Widget settings containing widget specific properties according to the defined settings json schema',
+ description: 'Widget settings containing widget specific properties according to the defined settings json schema',
meta: 'property',
type: 'object'
},
datasources: {
description: 'Array of resolved widget datasources.',
meta: 'property',
- type: 'Array<Datasource>'
+ type: 'Array<Datasource>'
},
data: {
description: 'Array of latest datasources data.',
meta: 'property',
- type: 'Array<DatasourceData>'
+ type: 'Array<DatasourceData>'
},
timeWindow: {
description: 'Current widget timewindow (applicable for timeseries widgets).',
meta: 'property',
- type: 'WidgetTimewindow'
+ type: 'WidgetTimewindow'
},
units: {
description: 'Optional property defining units text of values displayed by widget. Useful for simple widgets like cards or gauges.',
@@ -393,7 +393,7 @@ export const widgetContextCompletions: TbEditorCompletions = {
currentUser: {
description: 'Current user object.',
meta: 'property',
- type: 'AuthUser',
+ type: 'AuthUser',
children: {
sub: {
description: 'User subject (email).',
@@ -443,7 +443,7 @@ export const widgetContextCompletions: TbEditorCompletions = {
authority: {
description: 'User authority. Possible values: SYS_ADMIN, TENANT_ADMIN, CUSTOMER_USER',
meta: 'property',
- type: 'Authority'
+ type: 'Authority'
}
}
},
@@ -468,12 +468,12 @@ export const widgetContextCompletions: TbEditorCompletions = {
defaultSubscription: {
description: 'Default widget subscription object contains all subscription information,
including current data, according to the widget type.',
meta: 'property',
- type: 'IWidgetSubscription'
+ type: 'IWidgetSubscription'
},
timewindowFunctions: {
description: 'Object with timewindow functions used to manage widget data time frame. Can by used by Time-series or Alarm widgets.',
meta: 'property',
- type: 'TimewindowFunctions',
+ type: 'TimewindowFunctions',
children: {
onUpdateTimewindow: {
description: 'This function can be used to update current subscription time frame
to historical one identified by startTimeMs and endTimeMs arguments.',
@@ -500,7 +500,7 @@ export const widgetContextCompletions: TbEditorCompletions = {
controlApi: {
description: 'Object that provides API functions for RPC (Control) widgets.',
meta: 'property',
- type: 'RpcApi',
+ type: 'RpcApi',
children: {
sendOneWayCommand: {
description: 'Sends one way (without response) RPC command to the device.',
@@ -585,7 +585,7 @@ export const widgetContextCompletions: TbEditorCompletions = {
actionsApi: {
description: 'Set of API functions to work with user defined actions.',
meta: 'property',
- type: 'WidgetActionsApi',
+ type: 'WidgetActionsApi',
children: {
getActionDescriptors: {
description: 'Get list of action descriptors for provided actionSourceId.',
@@ -599,7 +599,7 @@ export const widgetContextCompletions: TbEditorCompletions = {
],
return: {
description: 'The list of action descriptors',
- type: 'Array<WidgetActionDescriptor>'
+ type: 'Array<WidgetActionDescriptor>'
}
},
handleWidgetAction: {
@@ -614,7 +614,7 @@ export const widgetContextCompletions: TbEditorCompletions = {
{
name: 'descriptor',
description: 'An action descriptor.',
- type: 'WidgetActionDescriptor'
+ type: 'WidgetActionDescriptor'
},
{
name: 'entityId',
@@ -635,7 +635,7 @@ export const widgetContextCompletions: TbEditorCompletions = {
stateController: {
description: 'Reference to Dashboard state controller, providing API to manage current dashboard state.',
meta: 'property',
- type: 'IStateController',
+ type: 'IStateController',
children: {
openState: {
description: 'Navigate to new dashboard state.',
@@ -649,7 +649,7 @@ export const widgetContextCompletions: TbEditorCompletions = {
{
name: 'params',
description: 'An object with state parameters to use by the new state.',
- type: 'StateParams',
+ type: 'StateParams',
optional: true
},
{
@@ -667,7 +667,7 @@ export const widgetContextCompletions: TbEditorCompletions = {
{
name: 'id',
description: 'An array state object of the target dashboard state.',
- type: 'Array StateObject',
+ type: 'Array StateObject',
},
{
name: 'openRightLayout',
@@ -690,7 +690,7 @@ export const widgetContextCompletions: TbEditorCompletions = {
{
name: 'params',
description: 'An object with state parameters to update current state parameters.',
- type: 'StateParams',
+ type: 'StateParams',
optional: true
},
{
@@ -714,7 +714,7 @@ export const widgetContextCompletions: TbEditorCompletions = {
meta: 'function',
return: {
description: 'current dashboard state parameters.',
- type: 'StateParams'
+ type: 'StateParams'
}
},
getStateParamsByStateId: {
@@ -729,7 +729,7 @@ export const widgetContextCompletions: TbEditorCompletions = {
],
return: {
description: 'current dashboard state parameters.',
- type: 'StateParams'
+ type: 'StateParams'
}
}
}
diff --git a/ui-ngx/src/app/shared/models/constants.ts b/ui-ngx/src/app/shared/models/constants.ts
index 36600eec72..1a681972bf 100644
--- a/ui-ngx/src/app/shared/models/constants.ts
+++ b/ui-ngx/src/app/shared/models/constants.ts
@@ -187,6 +187,7 @@ export const HelpLinks = {
gatewayInstall: `${helpBaseUrl}/docs/iot-gateway/install/docker-installation`,
scada: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/scada`,
scadaSymbolDev: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/scada/symbols-dev-guide`,
+ scadaSymbolDevAnimation: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/scada/scada-symbols-dev-guide/#scadasymbolanimation`
}
};
/* eslint-enable max-len */
diff --git a/ui-ngx/src/app/shared/models/jquery-event.models.ts b/ui-ngx/src/app/shared/models/jquery-event.models.ts
new file mode 100644
index 0000000000..5657beb60b
--- /dev/null
+++ b/ui-ngx/src/app/shared/models/jquery-event.models.ts
@@ -0,0 +1,80 @@
+///
+/// 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 Timeout = NodeJS.Timeout;
+
+export interface TbContextMenuEvent extends Event {
+ clientX: number;
+ clientY: number;
+ ctrlKey: boolean;
+ metaKey: boolean;
+}
+
+const isIOSDevice = (): boolean =>
+ /iPhone|iPad|iPod/i.test(navigator.userAgent) || (navigator.userAgent.includes('Mac') && 'ontouchend' in document);
+
+export const initCustomJQueryEvents = () => {
+ $.event.special.tbcontextmenu = {
+ setup(this: HTMLElement) {
+ const el = $(this);
+ if (isIOSDevice()) {
+ let timeoutId: Timeout;
+
+ el.on('touchstart', (e) => {
+ e.stopPropagation();
+ timeoutId = setTimeout(() => {
+ timeoutId = null;
+ e.stopPropagation();
+ const touch = e.originalEvent.changedTouches[0];
+ const event = $.Event('tbcontextmenu', {
+ clientX: touch.clientX,
+ clientY: touch.clientY,
+ ctrlKey: false,
+ metaKey: false
+ });
+ el.trigger(event, e);
+ }, 500);
+ });
+
+ el.on('touchend touchmove', () => {
+ if (timeoutId) {
+ clearTimeout(timeoutId);
+ }
+ });
+ } else {
+ el.on('contextmenu', (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ const event = $.Event('tbcontextmenu', {
+ clientX: e.originalEvent.clientX,
+ clientY: e.originalEvent.clientY,
+ ctrlKey: e.originalEvent.ctrlKey,
+ metaKey: e.originalEvent.metaKey,
+ });
+ el.trigger(event, e);
+ });
+ }
+ },
+ teardown(this: HTMLElement) {
+ const el = $(this);
+ if (isIOSDevice()) {
+ el.off('touchstart touchend touchmove');
+ } else {
+ el.off('contextmenu');
+ }
+ }
+ };
+};
diff --git a/ui-ngx/src/app/shared/shared.module.ts b/ui-ngx/src/app/shared/shared.module.ts
index 130e272c21..309a3e2bf3 100644
--- a/ui-ngx/src/app/shared/shared.module.ts
+++ b/ui-ngx/src/app/shared/shared.module.ts
@@ -68,6 +68,7 @@ import { NgxHmCarouselModule } from 'ngx-hm-carousel';
import { EditorModule, TINYMCE_SCRIPT_SRC } from '@tinymce/tinymce-angular';
import { UserMenuComponent } from '@shared/components/user-menu.component';
import { TruncateWithTooltipDirective } from '@shared/directives/truncate-with-tooltip.directive';
+import { ContextMenuDirective } from '@shared/directives/context-menu.directive';
import { NospacePipe } from '@shared/pipe/nospace.pipe';
import { TranslateModule } from '@ngx-translate/core';
import { TbCheckboxComponent } from '@shared/components/tb-checkbox.component';
@@ -371,6 +372,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
LedLightComponent,
MarkdownEditorComponent,
TruncateWithTooltipDirective,
+ ContextMenuDirective,
NospacePipe,
MillisecondsToTimeStringPipe,
EnumToArrayPipe,
@@ -633,6 +635,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
LedLightComponent,
MarkdownEditorComponent,
TruncateWithTooltipDirective,
+ ContextMenuDirective,
NospacePipe,
MillisecondsToTimeStringPipe,
EnumToArrayPipe,
diff --git a/ui-ngx/src/assets/dashboard/tenant_admin_home_page.json b/ui-ngx/src/assets/dashboard/tenant_admin_home_page.json
index e3277ac88a..59eb61a935 100644
--- a/ui-ngx/src/assets/dashboard/tenant_admin_home_page.json
+++ b/ui-ngx/src/assets/dashboard/tenant_admin_home_page.json
@@ -1208,7 +1208,7 @@
"dropShadow": false,
"enableFullscreen": false,
"widgetStyle": {},
- "widgetCss": " .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n padding: 0;\n}\n\n .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n padding-bottom: 8px;\n font-weight: 600;\n font-size: 20px;\n line-height: 24px;\n letter-spacing: 0.1px;\n color: rgba(0, 0, 0, 0.76);\n}\n\n.tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel {\n padding: 0;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n padding-bottom: 0;\n font-weight: 500;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.25px;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1182px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel {\n gap: 0;\n }\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n min-width: 150px;\n }\n}\n\n@media screen and (max-width: 960px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n max-width: 120px;\n }\n \n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-badges {\n max-width: 160px;\n }\n}\n\n@media screen and (min-width: 1819px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n max-width: 125px;\n }\n \n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-badges {\n max-width: 160px;\n }\n}\n\n@media screen and (min-width: 960px) and (max-height: 960px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode-label {\n display: none;\n }\n}",
+ "widgetCss": " .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n padding: 0;\n}\n\n .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n padding-bottom: 8px;\n font-weight: 600;\n font-size: 20px;\n line-height: 24px;\n letter-spacing: 0.1px;\n color: rgba(0, 0, 0, 0.76);\n}\n\n.tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel {\n padding: 0;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n padding-bottom: 0;\n font-weight: 500;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.25px;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1182px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel {\n gap: 0;\n }\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n min-width: 150px;\n }\n}\n\n@media screen and (max-width: 960px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n max-width: 120px;\n }\n \n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-badges {\n max-width: 160px;\n }\n}\n\n@media screen and (min-width: 1819px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n max-width: 125px;\n }\n \n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-badges {\n max-width: 160px;\n }\n}\n\n@media screen and (min-width: 960px) and (max-height: 965px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode-label {\n display: none;\n }\n}",
"showTitleIcon": false,
"titleTooltip": "",
"titleStyle": null,
diff --git a/ui-ngx/src/assets/help/en_US/scada/symbol_state_render_fn.md b/ui-ngx/src/assets/help/en_US/scada/symbol_state_render_fn.md
index bdb0d45234..9a2c811817 100644
--- a/ui-ngx/src/assets/help/en_US/scada/symbol_state_render_fn.md
+++ b/ui-ngx/src/assets/help/en_US/scada/symbol_state_render_fn.md
@@ -10,9 +10,9 @@ A JavaScript function used to render SCADA symbol state.
**Parameters:**
- - ctx:
ScadaSymbolContext - Context of the SCADA symbol.
+ - ctx:
ScadaSymbolContext - Context of the SCADA symbol.
- - svg:
SVG.Svg - A root svg node. Instance of SVG.Svg.
+ - svg:
SVG.Svg - A root svg node. Instance of SVG.Svg.
@@ -20,8 +20,44 @@ A JavaScript function used to render SCADA symbol state.
##### Examples
-
-
-TODO
-
-
+This JavaScript snippet manages the enablement and appearance of SVG buttons used to control volume settings based on device connectivity and value thresholds.
+The 'Up' button is disabled when the volume reaches its maximum allowable value (`maxValue`), and the 'Down' button is disabled when the volume is at its minimum allowable value (`minValue`).
+Both buttons are disabled when the device is offline. The visual cues for enabled/disabled states are represented by color changes.
+
+```javascript
+
+var levelUpButton = ctx.tags.levelUpButton; // Button for increasing volume
+var levelDownButton = ctx.tags.levelDownButton; // Button for decreasing volume
+var levelArrowUp = ctx.tags.levelArrowUp; // Visual arrow for 'Up' button
+var levelArrowDown = ctx.tags.levelArrowDown; // Visual arrow for 'Down' button
+
+var active = ctx.values.active; // Device connectivity status
+var value = ctx.values.value; // Current volume level
+var minValue = ctx.properties.minValue; // Minimum volume level
+var maxValue = ctx.properties.maxValue; // Maximum volume level
+
+var enabledColor = '#4CAF50'; // Color for enabled state
+var disabledColor = '#A1ADB1'; // Color for disabled state
+
+// Determine if the 'Up' button should be enabled
+var levelUpEnabled = active && value < maxValue;
+if (levelUpEnabled) {
+ ctx.api.enable(levelUpButton); // Enable 'Up' button
+ levelArrowUp[0].attr({fill: enabledColor}); // Set arrow color to indicate enabled state
+} else {
+ ctx.api.disable(levelUpButton); // Disable 'Up' button
+ levelArrowUp[0].attr({fill: disabledColor}); // Set arrow color to indicate disabled state
+}
+
+// Determine if the 'Down' button should be enabled
+var levelDownEnabled = active && value > minValue;
+if (levelDownEnabled) {
+ ctx.api.enable(levelDownButton); // Enable 'Down' button
+ levelArrowDown[0].attr({fill: enabledColor}); // Set arrow color to indicate enabled state
+} else {
+ ctx.api.disable(levelDownButton); // Disable 'Down' button
+ levelArrowDown[0].attr({fill: disabledColor}); // Set arrow color to indicate disabled state
+}
+
+{:copy-code}
+```
diff --git a/ui-ngx/src/assets/help/en_US/scada/tag_click_action_fn.md b/ui-ngx/src/assets/help/en_US/scada/tag_click_action_fn.md
index 26ec4f9bb3..8675419803 100644
--- a/ui-ngx/src/assets/help/en_US/scada/tag_click_action_fn.md
+++ b/ui-ngx/src/assets/help/en_US/scada/tag_click_action_fn.md
@@ -10,11 +10,10 @@ A JavaScript function invoked when user clicks on SVG element with specific tag.
**Parameters:**
- - ctx:
ScadaSymbolContext - Context of the SCADA symbol.
+ - ctx:
ScadaSymbolContext - Context of the SCADA symbol.
- - element:
Element - SVG element.
- See Manipulating section to manipulate the element.
- See Animating section to animate the element.
+ - element:
Element - SVG.js element.
+ See the examples below to learn how to manipulate and animate elements.
- event:
Event - DOM event.
@@ -24,6 +23,28 @@ A JavaScript function invoked when user clicks on SVG element with specific tag.
##### Examples
-
+The example demonstrates how to dynamically call the 'turnOn' or 'turnOff' actions based on the 'active' status from the context. The actions are implemented using the following methods from the Scada Symbol API:
-TODO
+- **callAction**: *(event: Event, behaviorId: string, value?: any, observer?: Partial\\>): void* - Triggers a specific behavior action identified by its ID, allowing for the optional passing of values and observer callbacks.
+
+- **setValue**: *(valueId: string, value: any): void* - Updates a specific value within the `ctx.values` object and initiates all related rendering functions.
+
+For more detailed guidelines on device interaction, consider reviewing the best practices.
+
+```javascript
+var active = ctx.values.active; // Current active status from context
+var action = active ? 'turnOn' : 'turnOff'; // Determine action based on active status
+var parameter = "any object or primitive"; // Parameter to pass with the action
+
+// Call the action with observer callbacks for next and error handling
+ctx.api.callAction(event, action, parameter, {
+ next: () => {
+ // Action succeeded; toggle the 'activate' status for debugging
+ ctx.api.setValue('activate', !active);
+ },
+ error: () => {
+ // Action failed; reset the 'activate' status for debugging
+ ctx.api.setValue('activate', active);
+ }
+});
+```
diff --git a/ui-ngx/src/assets/help/en_US/scada/tag_state_render_fn.md b/ui-ngx/src/assets/help/en_US/scada/tag_state_render_fn.md
index caed8060d4..c16ad96c58 100644
--- a/ui-ngx/src/assets/help/en_US/scada/tag_state_render_fn.md
+++ b/ui-ngx/src/assets/help/en_US/scada/tag_state_render_fn.md
@@ -10,11 +10,10 @@ A JavaScript function used to render SCADA symbol element with specific tag.
**Parameters:**
- - ctx:
ScadaSymbolContext - Context of the SCADA symbol.
+ - ctx:
ScadaSymbolContext - Context of the SCADA symbol.
- - element:
Element - SVG element.
- See Manipulating section to manipulate the element.
- See Animating section to animate the element.
+ - element:
Element - SVG.js element.
+ See the examples below to learn how to manipulate and animate elements.
@@ -22,6 +21,46 @@ A JavaScript function used to render SCADA symbol element with specific tag.
##### Examples
-
+* Change the background of the element based on the value of the “active”
-TODO
+```javascript
+if(ctx.values.active){
+ element.attr({fill: ctx.properties.activeColor});
+} else {
+ element.attr({fill: ctx.properties.inactiveColor});
+}
+{:copy-code}
+```
+
+* Enable and disable the “On” button based on the state of the "active" (avoid or prevent click action)
+
+```javascript
+if (ctx.values.active) {
+ ctx.api.disable(element);
+} else {
+ ctx.api.enable(element);
+}
+{:copy-code}
+```
+
+* Smooth infinite rotation animation based on the value of the “active” with speed based on the value of the “speed”
+
+```javascript
+var on = ctx.values.active;
+var speed = ctx.values.speed ? ctx.values.speed / 60 : 1;
+var animation = ctx.api.cssAnimation(element);
+
+if (on) {
+ if (!animation) {
+ animation = ctx.api.cssAnimate(element, 2000)
+ .rotate(360).loop().speed(speed);
+ } else {
+ animation.speed(speed).play();
+ }
+} else {
+ if (animation) {
+ animation.pause();
+ }
+}
+{:copy-code}
+```
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 b66457cc3d..23a60fec14 100644
--- a/ui-ngx/src/assets/locale/locale.constant-en_US.json
+++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json
@@ -3143,10 +3143,11 @@
"title": "{{type}} Connector RPC parameters",
"templates-title": "Connector RPC Templates",
"methodFilter": "Method filter",
+ "method-name": "Method name",
"requestTopicExpression": "Request topic expression",
"responseTopicExpression": "Response topic expression",
- "responseTimeout": "Response Time",
- "valueExpression": "Value Expression",
+ "responseTimeout": "Response timeout",
+ "valueExpression": "Value expression",
"tag": "Tag",
"type": "Type",
"functionCode": "Function Code",
@@ -3185,6 +3186,11 @@
"tries": "Tries",
"httpHeaders": "HTTP Headers",
"header-name": "Header name",
+ "hint": {
+ "modbus-response-reading": "RPC response will return all subtracted values from all connected devices when the reading functions are selected.",
+ "modbus-writing-functions": "RPC will write a filled value to all connected devices when the writing functions are selected.",
+ "opc-method": "A filled method name is the OPC-UA method that will processed on the server side (make sure your node has the requested method)."
+ },
"security-name": "Security name",
"value": "Value",
"security": "Security",
diff --git a/ui-ngx/src/typings/jquery.typings.d.ts b/ui-ngx/src/typings/jquery.typings.d.ts
index d457729ace..a058373022 100644
--- a/ui-ngx/src/typings/jquery.typings.d.ts
+++ b/ui-ngx/src/typings/jquery.typings.d.ts
@@ -14,6 +14,9 @@
/// limitations under the License.
///
+import { TbContextMenuEvent } from '@shared/models/jquery-event.models';
+
interface JQuery {
terminal(options?: any): any;
+ on(events: 'tbcontextmenu', handler: (e: TbContextMenuEvent) => void): this;
}
diff --git a/ui-ngx/yarn.lock b/ui-ngx/yarn.lock
index 51bfe8f56e..cc6762eb57 100644
--- a/ui-ngx/yarn.lock
+++ b/ui-ngx/yarn.lock
@@ -3052,10 +3052,10 @@
dependencies:
"@types/jasmine" "*"
-"@types/jquery@*", "@types/jquery@^3.5.16", "@types/jquery@^3.5.29":
- version "3.5.29"
- resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.5.29.tgz#3c06a1f519cd5fc3a7a108971436c00685b5dcea"
- integrity sha512-oXQQC9X9MOPRrMhPHHOsXqeQDnWeCDT3PelUIg/Oy8FAbzSZtFHRjc7IpbfFVmpLtJ+UOoywpRsuO5Jxjybyeg==
+"@types/jquery@*", "@types/jquery@^3.5.29", "@types/jquery@^3.5.30":
+ version "3.5.30"
+ resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.5.30.tgz#888d584cbf844d3df56834b69925085038fd80f7"
+ integrity sha512-nbWKkkyb919DOUxjmRVk8vwtDb0/k8FKncmUKFi+NY+QXqWltooxTrswvz4LspQwxvLdvzBN1TImr6cw3aQx2A==
dependencies:
"@types/sizzle" "*"
@@ -7377,7 +7377,7 @@ jquery.terminal@^2.35.3:
optionalDependencies:
fsevents "^2.3.2"
-jquery@>=1.9.1, jquery@^3.5.0, jquery@^3.6.3, jquery@^3.7.1:
+jquery@>=1.9.1, jquery@^3.5.0, jquery@^3.7.1:
version "3.7.1"
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.7.1.tgz#083ef98927c9a6a74d05a6af02806566d16274de"
integrity sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==