diff --git a/.gitignore b/.gitignore index 3a4af54b75..e9a4e11b85 100644 --- a/.gitignore +++ b/.gitignore @@ -30,9 +30,10 @@ pom.xml.versionsBackup **/build **/target **/Californium.properties +**/Californium3.properties **/.env .instance_id rebuild-docker.sh */.run/** .run/** -.run \ No newline at end of file +.run diff --git a/README.md b/README.md index 5520645541..581e71977d 100644 --- a/README.md +++ b/README.md @@ -12,20 +12,20 @@ ThingsBoard documentation is hosted on [thingsboard.io](https://thingsboard.io/d ## IoT use cases -[**Smart metering**](https://thingsboard.io/smart-metering/) -[![Smart metering](https://user-images.githubusercontent.com/8308069/31455788-6888a948-aec1-11e7-9819-410e0ba785e0.gif "Smart metering")](https://thingsboard.io/smart-metering/) - -[**IoT Rule Engine**](https://thingsboard.io/docs/user-guide/rule-engine-2-0/re-getting-started/) -[![IoT Rule Engine](https://thingsboard.io/images/demo/send-email-rule-chain.gif "IoT Rule Engine")](https://thingsboard.io/docs/user-guide/rule-engine-2-0/re-getting-started/) - [**Smart energy**](https://thingsboard.io/smart-energy/) -[![Smart energy](https://cloud.githubusercontent.com/assets/8308069/24495682/aebd45d0-153e-11e7-8de4-7360ed5b41ae.gif "Smart energy")](https://thingsboard.io/smart-energy/) +[![Smart energy](https://user-images.githubusercontent.com/8308069/152984256-eb48564a-645c-468d-912b-f554b63104a5.gif "Smart energy")](https://thingsboard.io/smart-energy/) + +[**Fleet tracking**](https://thingsboard.io/fleet-tracking/) +[![Fleet tracking](https://user-images.githubusercontent.com/8308069/152984528-0054ed55-8b8b-4cda-ba45-02fe95a81222.gif "Fleet tracking")](https://thingsboard.io/fleet-tracking/) [**Smart farming**](https://thingsboard.io/smart-farming/) -[![Smart farming](https://cloud.githubusercontent.com/assets/8308069/24496824/10dc1144-1542-11e7-8aa1-5d3a281d5a1a.gif "Smart farming")](https://thingsboard.io/smart-farming/) +[![Smart farming](https://user-images.githubusercontent.com/8308069/152984443-a98b7d3d-ff7a-4037-9011-e71e1e6f755f.gif "Smart farming")](https://thingsboard.io/smart-farming/) -[**Fleet tracking**](https://thingsboard.io/fleet-tracking/) -[![Fleet tracking](https://cloud.githubusercontent.com/assets/8308069/24497169/3a1a61e0-1543-11e7-8d55-3c8a13f35634.gif "Fleet tracking")](https://thingsboard.io/fleet-tracking/) +[**IoT Rule Engine**](https://thingsboard.io/docs/user-guide/rule-engine-2-0/re-getting-started/) +[![IoT Rule Engine](https://thingsboard.io/images/demo/send-email-rule-chain.gif "IoT Rule Engine")](https://thingsboard.io/docs/user-guide/rule-engine-2-0/re-getting-started/) + +[**Smart metering**](https://thingsboard.io/smart-metering/) +[![Smart metering](https://user-images.githubusercontent.com/8308069/31455788-6888a948-aec1-11e7-9819-410e0ba785e0.gif "Smart metering")](https://thingsboard.io/smart-metering/) ## Getting Started diff --git a/application/pom.xml b/application/pom.xml index 321a1fc638..8e0e023d89 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -1,6 +1,6 @@ - + [parentDashboard]="parentDashboard" + [parentAliasController]="parentAliasController"> +
+ {{ 'dashboard.non-existent-dashboard-state-error' | translate:{stateId} }} +
diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-state.component.scss b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-state.component.scss new file mode 100644 index 0000000000..01322e7215 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-state.component.scss @@ -0,0 +1,21 @@ +/** + * Copyright © 2016-2022 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 { + position: relative; + display: block; + width: 100%; + height: 100%; +} diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-state.component.ts b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-state.component.ts index fb22d9f3c2..50863b0595 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-state.component.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-state.component.ts @@ -1,5 +1,5 @@ /// -/// Copyright © 2016-2021 The Thingsboard Authors +/// Copyright © 2016-2022 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. @@ -14,14 +14,14 @@ /// limitations under the License. /// -import { ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core'; +import { ChangeDetectorRef, Component, HostBinding, Input, OnDestroy, OnInit } from '@angular/core'; import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { Dashboard } from '@shared/models/dashboard.models'; -import { StateObject } from '@core/api/widget-api.models'; +import { Dashboard, DashboardLayoutId } from '@shared/models/dashboard.models'; +import { IAliasController, StateObject } from '@core/api/widget-api.models'; import { updateEntityParams, WidgetContext } from '@home/models/widget-component.models'; -import { deepClone, objToBase64 } from '@core/utils'; +import { deepClone, isDefinedAndNotNull, isNotEmptyStr, objToBase64 } from '@core/utils'; import { IDashboardComponent } from '@home/models/dashboard-component.models'; import { EntityId } from '@shared/models/id/entity-id'; import { Subscription } from 'rxjs'; @@ -29,7 +29,7 @@ import { Subscription } from 'rxjs'; @Component({ selector: 'tb-dashboard-state', templateUrl: './dashboard-state.component.html', - styleUrls: [] + styleUrls: ['./dashboard-state.component.scss'] }) export class DashboardStateComponent extends PageComponent implements OnInit, OnDestroy { @@ -42,6 +42,15 @@ export class DashboardStateComponent extends PageComponent implements OnInit, On @Input() syncParentStateParams = false; + @Input() + defaultAutofillLayout = true; + + @Input() + defaultMargin; + + @Input() + defaultBackgroundColor; + @Input() entityParamName: string; @@ -54,6 +63,10 @@ export class DashboardStateComponent extends PageComponent implements OnInit, On parentDashboard: IDashboardComponent; + parentAliasController: IAliasController; + + stateExists = true; + private stateSubscription: Subscription; constructor(protected store: Store, @@ -63,14 +76,32 @@ export class DashboardStateComponent extends PageComponent implements OnInit, On ngOnInit(): void { this.dashboard = deepClone(this.ctx.stateController.dashboardCtrl.dashboardCtx.getDashboard()); - this.updateCurrentState(); - this.parentDashboard = this.ctx.parentDashboard ? - this.ctx.parentDashboard : this.ctx.dashboard; - if (this.syncParentStateParams) { - this.stateSubscription = this.ctx.stateController.dashboardCtrl.dashboardCtx.stateChanged.subscribe(() => { - this.updateCurrentState(); - this.cd.markForCheck(); - }); + const state = this.dashboard.configuration.states[this.stateId]; + if (state) { + for (const layoutId of Object.keys(state.layouts)) { + if (this.defaultAutofillLayout) { + state.layouts[layoutId as DashboardLayoutId].gridSettings.autoFillHeight = true; + state.layouts[layoutId as DashboardLayoutId].gridSettings.mobileAutoFillHeight = true; + } + if (isDefinedAndNotNull(this.defaultMargin)) { + state.layouts[layoutId as DashboardLayoutId].gridSettings.margin = this.defaultMargin; + } + if (isNotEmptyStr(this.defaultBackgroundColor)) { + state.layouts[layoutId as DashboardLayoutId].gridSettings.backgroundColor = this.defaultBackgroundColor; + } + } + this.updateCurrentState(); + this.parentDashboard = this.ctx.parentDashboard ? + this.ctx.parentDashboard : this.ctx.dashboard; + if (this.syncParentStateParams) { + this.parentAliasController = this.parentDashboard.aliasController; + this.stateSubscription = this.ctx.stateController.dashboardCtrl.dashboardCtx.stateChanged.subscribe(() => { + this.updateCurrentState(); + this.cd.markForCheck(); + }); + } + } else { + this.stateExists = false; } } diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-toolbar.component.html b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-toolbar.component.html index de15c409ec..d7fb4c3f88 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-toolbar.component.html +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-toolbar.component.html @@ -1,6 +1,6 @@ -
-
+
+ {{('device-profile.condition' | translate) + ': '}} +
+ + {{ 'device-profile.propagate-alarm-to-owner' | translate }} + + + {{ 'device-profile.propagate-alarm-to-tenant' | translate }} +
diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.scss b/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.scss index 3546e48ca4..8596462c3e 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.scss +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.scss @@ -1,5 +1,5 @@ /** - * Copyright © 2016-2021 The Thingsboard Authors + * Copyright © 2016-2022 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. diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.ts b/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.ts index 20ea89e53c..81ec55261e 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.ts @@ -1,5 +1,5 @@ /// -/// Copyright © 2016-2021 The Thingsboard Authors +/// Copyright © 2016-2022 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. @@ -97,7 +97,9 @@ export class DeviceProfileAlarmComponent implements ControlValueAccessor, OnInit createRules: [null], clearRule: [null], propagate: [null], - propagateRelationTypes: [null] + propagateRelationTypes: [null], + propagateToOwner: [null], + propagateToTenant: [null] }, { validators: deviceProfileAlarmValidator }); this.alarmFormGroup.valueChanges.subscribe(() => { this.updateModel(); diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.html b/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.html index df0d5af5a3..dde3df31c1 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.html @@ -1,6 +1,6 @@ -
Observable; saveMarkerLocation: (e: FormattedData, lat?: number, lng?: number) => Observable; savePolygonLocation: (e: FormattedData, coordinates?: Array) => Observable; + translateService: TranslateService; protected constructor(public ctx: WidgetContext, public $container: HTMLElement, @@ -96,6 +97,7 @@ export default abstract class LeafletMap { this.options = options; this.editPolygons = options.showPolygon && options.editablePolygon; L.Icon.Default.imagePath = '/'; + this.translateService = this.ctx.$injector.get(TranslateService); } public initSettings(options: MapSettings) { @@ -171,6 +173,38 @@ export default abstract class LeafletMap { if (data !== null) { this.selectedEntity = data; this.toggleDrawMode(type); + let tooltipText; + let customTranslation; + switch (type) { + case 'tbMarker': + tooltipText = this.translateService.instant('widgets.maps.tooltips.placeMarker', {entityName: data.entityName}); + // @ts-ignore + this.map.pm.Draw.tbMarker._hintMarker.setTooltipContent(tooltipText); + break; + case 'tbRectangle': + tooltipText = this.translateService.instant('widgets.maps.tooltips.firstVertex', {entityName: data.entityName}); + // @ts-ignore + this.map.pm.Draw.tbRectangle._hintMarker.setTooltipContent(tooltipText); + customTranslation = { + tooltips: { + finishRect: this.translateService.instant('widgets.maps.tooltips.finishRect', {entityName: data.entityName}) + } + }; + this.map.pm.setLang('en', customTranslation, 'en'); + break; + case 'tbPolygon': + tooltipText = this.translateService.instant('widgets.maps.tooltips.firstVertex', {entityName: data.entityName}); + // @ts-ignore + this.map.pm.Draw.tbPolygon._hintMarker.setTooltipContent(tooltipText); + customTranslation = { + tooltips: { + continueLine: this.translateService.instant('widgets.maps.tooltips.continueLine', {entityName: data.entityName}), + finishPoly: this.translateService.instant('widgets.maps.tooltips.finishPoly', {entityName: data.entityName}) + } + }; + this.map.pm.setLang('en', customTranslation); + break; + } } else { // @ts-ignore this.map.pm.Toolbar.toggleButton(type, false); @@ -231,8 +265,7 @@ export default abstract class LeafletMap { }); } - const translateService = this.ctx.$injector.get(TranslateService); - this.map.pm.setLang('en', translateService.instant('widgets.maps'), 'en'); + this.map.pm.setLang('en', this.translateService.instant('widgets.maps'), 'en'); if (!this.options.hideAllControlButton) { this.map.pm.addControls({ position: 'topleft', @@ -367,6 +400,40 @@ export default abstract class LeafletMap { this.updatePending = false; this.updateData(this.drawRoutes, this.showPolygon); } + this.createdControlButtonTooltip(); + } + + private createdControlButtonTooltip() { + import('tooltipster').then(() => { + $(this.ctx.$container) + .find('a[role="button"]:not(.leaflet-pm-action)') + .each((index, element) => { + let title; + if (element.children.length) { + title = (element.children[0] as HTMLElement).title; + $(element).children().removeAttr('title'); + } else { + title = element.title; + $(element).removeAttr('title'); + } + $(element).tooltipster( + { + content: title, + theme: 'tooltipster-shadow', + delay: 10, + triggerClose: { + click: true, + tap: true, + scroll: true, + mouseleave: true + }, + side: 'right', + distance: 2, + trackOrigin: true + } + ); + }); + }); } createLatLng(lat: number, lng: number): L.LatLng { @@ -494,16 +561,70 @@ export default abstract class LeafletMap { } this.updateMarkers(formattedData, false); this.updateBoundsInternal(); - if (this.options.draggableMarker) { - const foundEntityWithoutLocation = formattedData.some(mData => !this.convertPosition(mData)); - this.map.pm.Toolbar.setButtonDisabled('tbMarker', !foundEntityWithoutLocation); - this.datasources = formattedData; - } - if (this.editPolygons) { - const foundEntityWithoutPolygon = formattedData.some(pData => !this.isValidPolygonPosition(pData)); - this.map.pm.Toolbar.setButtonDisabled('tbPolygon', !foundEntityWithoutPolygon); - this.map.pm.Toolbar.setButtonDisabled('tbRectangle', !foundEntityWithoutPolygon); - this.datasources = formattedData; + if (this.options.draggableMarker || this.editPolygons) { + let foundEntityWithoutLocation = false; + let foundEntityWithLocation = false; + let foundEntityWithoutPolygon = false; + let foundEntityWithPolygon = false; + + if (this.options.draggableMarker && !this.options.hideDrawControlButton && !this.options.hideAllControlButton) { + for (const mData of formattedData) { + const position = this.convertPosition(mData); + if (!position) { + foundEntityWithoutLocation = true; + } else if (!!position) { + foundEntityWithLocation = true; + } + if (foundEntityWithoutLocation && foundEntityWithLocation) { + break; + } + } + // @ts-ignore + if (this.map.pm.Toolbar.getButtons().tbMarker.disable !== foundEntityWithoutLocation) { + this.map.pm.Toolbar.setButtonDisabled('tbMarker', !foundEntityWithoutLocation); + } + this.datasources = formattedData; + } + + if (this.editPolygons && !this.options.hideDrawControlButton && !this.options.hideAllControlButton) { + for (const pData of formattedData) { + const isValidPolygon = this.isValidPolygonPosition(pData); + if (!isValidPolygon) { + foundEntityWithoutPolygon = true; + } else if (isValidPolygon) { + foundEntityWithPolygon = true; + } + if (foundEntityWithoutPolygon && foundEntityWithPolygon) { + break; + } + } + // @ts-ignore + if (this.map.pm.Toolbar.getButtons().tbPolygon.disable !== foundEntityWithoutPolygon) { + this.map.pm.Toolbar.setButtonDisabled('tbPolygon', !foundEntityWithoutPolygon); + this.map.pm.Toolbar.setButtonDisabled('tbRectangle', !foundEntityWithoutPolygon); + } + this.datasources = formattedData; + } + if (!this.options.hideRemoveControlButton && !this.options.hideAllControlButton) { + const disabledButton = !foundEntityWithLocation && !foundEntityWithPolygon; + if (disabledButton && this.map.pm.globalRemovalModeEnabled()) { + this.map.pm.toggleGlobalRemovalMode(); + } + this.map.pm.Toolbar.setButtonDisabled('removalMode', disabledButton); + } + if (!this.options.hideEditControlButton && !this.options.hideAllControlButton) { + const disabledButton = !foundEntityWithLocation && !foundEntityWithPolygon; + // @ts-ignore + if (this.map.pm.Toolbar.getButtons().dragMode.disable !== disabledButton) { + this.map.pm.Toolbar.setButtonDisabled('dragMode', disabledButton); + // @ts-ignore + if (this.editPolygons && this.map.pm.Toolbar.getButtons().editMode.disable !== foundEntityWithPolygon) { + this.map.pm.Toolbar.setButtonDisabled('editMode', !foundEntityWithPolygon); + this.map.pm.Toolbar.setButtonDisabled('cutPolygon', !foundEntityWithPolygon); + this.map.pm.Toolbar.setButtonDisabled('rotateMode', !foundEntityWithPolygon); + } + } + } } } else { this.updatePending = true; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts index de94bdc156..bbf3194b71 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts @@ -1,5 +1,5 @@ /// -/// Copyright © 2016-2021 The Thingsboard Authors +/// Copyright © 2016-2022 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. @@ -33,6 +33,7 @@ export type MapSettings = { posFunction: PosFuncton; defaultZoomLevel?: number; disableScrollZooming?: boolean; + disableZoomControl?: boolean; minZoomLevel?: number; useClusterMarkers: boolean; latKeyName?: string; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget.interface.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget.interface.ts index 5a74247f2f..252f0f6590 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget.interface.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget.interface.ts @@ -1,5 +1,5 @@ /// -/// Copyright © 2016-2021 The Thingsboard Authors +/// Copyright © 2016-2022 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. @@ -16,9 +16,10 @@ import { JsonSettingsSchema } from '@shared/models/widget.models'; import { MapProviders } from '@home/components/widget/lib/maps/map-models'; +import LeafletMap from '@home/components/widget/lib/maps/leaflet-map'; export interface MapWidgetInterface { - map?: any; + map?: LeafletMap; resize(); update(); destroy(); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts index bc042e4fc0..2950471d08 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts @@ -1,5 +1,5 @@ /// -/// Copyright © 2016-2021 The Thingsboard Authors +/// Copyright © 2016-2022 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. @@ -300,8 +300,8 @@ export class MapWidgetController implements MapWidgetInterface { } resize() { - this.map?.invalidateSize(); - this.map.onResize(); + this.map.onResize(); + this.map?.invalidateSize(); } destroy() { @@ -309,6 +309,12 @@ export class MapWidgetController implements MapWidgetInterface { if (this.map) { this.map.remove(); } + if ($.tooltipster) { + const instances = $.tooltipster.instances(); + instances.forEach((instance) => { + instance.destroy(); + }); + } } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts index 9c2fbcdcd5..0482f4cec5 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts @@ -1,5 +1,5 @@ /// -/// Copyright © 2016-2021 The Thingsboard Authors +/// Copyright © 2016-2022 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. diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.scss b/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.scss index f45d08f9e9..3d93f87ee0 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.scss @@ -1,5 +1,5 @@ /** - * Copyright © 2016-2021 The Thingsboard Authors + * Copyright © 2016-2022 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. @@ -41,7 +41,3 @@ .leaflet-container { background-color: white; } - -.leaflet-buttons-control-button.pm-disabled { - pointer-events: none; -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts index 741cf22719..1d5234f55c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts @@ -1,5 +1,5 @@ /// -/// Copyright © 2016-2021 The Thingsboard Authors +/// Copyright © 2016-2022 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. @@ -23,13 +23,16 @@ import { MarkerSettings, UnitedMapSettings } from './map-models'; -import { bindPopupActions, createTooltip, } from './maps-utils'; +import { bindPopupActions, createTooltip } from './maps-utils'; import { aspectCache, fillPattern, parseWithTranslation, processPattern, safeExecute } from './common-maps-utils'; import tinycolor from 'tinycolor2'; import { isDefined, isDefinedAndNotNull } from '@core/utils'; import LeafletMap from './leaflet-map'; export class Marker { + + private editing = false; + leafletMarker: L.Marker; labelOffset: L.LatLngTuple; tooltipOffset: L.LatLngTuple; @@ -65,16 +68,26 @@ export class Marker { if (this.settings.markerClick) { this.leafletMarker.on('click', (event: LeafletMouseEvent) => { - for (const action in this.settings.markerClick) { - if (typeof (this.settings.markerClick[action]) === 'function') { - this.settings.markerClick[action](event.originalEvent, this.data.$datasource); - } + for (const action in this.settings.markerClick) { + if (typeof (this.settings.markerClick[action]) === 'function') { + this.settings.markerClick[action](event.originalEvent, this.data.$datasource); } + } }); } if (settings.draggableMarker && onDragendListener) { - this.leafletMarker.on('pm:dragend', (e) => onDragendListener(e, this.data)); + this.leafletMarker.on('pm:dragstart', (e) => { + (this.leafletMarker.dragging as any)._draggable = { _moved: true }; + (this.leafletMarker.dragging as any)._enabled = true; + this.editing = true; + }); + this.leafletMarker.on('pm:dragend', (e) => { + onDragendListener(e, this.data); + delete (this.leafletMarker.dragging as any)._draggable; + delete (this.leafletMarker.dragging as any)._enabled; + this.editing = false; + }); } } @@ -97,7 +110,7 @@ export class Marker { } updateMarkerPosition(position: L.LatLng) { - if (!this.leafletMarker.getLatLng().equals(position)) { + if (!this.leafletMarker.getLatLng().equals(position) && !this.editing) { this.location = position; this.leafletMarker.setLatLng(position); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts index de55d9b1b0..e3e876a72c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts @@ -1,5 +1,5 @@ /// -/// Copyright © 2016-2021 The Thingsboard Authors +/// Copyright © 2016-2022 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. @@ -27,6 +27,8 @@ import { FormattedData, PolygonSettings, UnitedMapSettings } from './map-models' export class Polygon { + private editing = false; + leafletPoly: L.Polygon; tooltip: L.Popup; data: FormattedData; @@ -62,6 +64,8 @@ export class Polygon { private createEventListeners() { if (this.settings.editablePolygon && this.onDragendListener) { + this.leafletPoly.on('pm:markerdragstart', () => this.editing = true); + this.leafletPoly.on('pm:markerdragend', () => this.editing = false); this.leafletPoly.on('pm:edit', (e) => this.onDragendListener(e, this.data)); } @@ -100,6 +104,9 @@ export class Polygon { } updatePolygon(data: FormattedData, dataSources: FormattedData[], settings: PolygonSettings) { + if (this.editing) { + return; + } this.data = data; this.dataSources = dataSources; const polyData = data[this.settings.polygonKeyName]; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/polyline.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/polyline.ts index 895dc15763..0c3b48d68e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/polyline.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/polyline.ts @@ -1,5 +1,5 @@ /// -/// Copyright © 2016-2021 The Thingsboard Authors +/// Copyright © 2016-2022 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. diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/google-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/google-map.ts index 00d9353218..9ac73d2749 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/google-map.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/google-map.ts @@ -1,5 +1,5 @@ /// -/// Copyright © 2016-2021 The Thingsboard Authors +/// Copyright © 2016-2022 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. @@ -38,6 +38,7 @@ export class GoogleMap extends LeafletMap { this.loadGoogle(() => { const map = L.map($container, { attributionControl: false, + zoomControl: !this.options.disableZoomControl, tap: L.Browser.safari && L.Browser.mobile }).setView(options?.defaultCenterPosition, options?.defaultZoomLevel || DEFAULT_ZOOM_LEVEL); (L.gridLayer as any).googleMutant({ diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/here-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/here-map.ts index caac9a78b4..bc261b5c63 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/here-map.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/here-map.ts @@ -1,5 +1,5 @@ /// -/// Copyright © 2016-2021 The Thingsboard Authors +/// Copyright © 2016-2022 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. @@ -23,7 +23,8 @@ export class HEREMap extends LeafletMap { constructor(ctx: WidgetContext, $container, options: UnitedMapSettings) { super(ctx, $container, options); const map = L.map($container, { - tap: L.Browser.safari && L.Browser.mobile + tap: L.Browser.safari && L.Browser.mobile, + zoomControl: !this.options.disableZoomControl }).setView(options?.defaultCenterPosition, options?.defaultZoomLevel || DEFAULT_ZOOM_LEVEL); const tileLayer = (L.tileLayer as any).provider(options.mapProviderHere || 'HERE.normalDay', options.credentials); tileLayer.addTo(map); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts index be427f55cf..087b9febc8 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts @@ -1,5 +1,5 @@ /// -/// Copyright © 2016-2021 The Thingsboard Authors +/// Copyright © 2016-2022 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. @@ -29,6 +29,7 @@ import { DataSet, DatasourceType, widgetType } from '@shared/models/widget.model import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { WidgetSubscriptionOptions } from '@core/api/widget-api.models'; import { isDefinedAndNotNull, isEmptyStr } from '@core/utils'; +import { EntityDataPageLink } from '@shared/models/query/query.models'; const maxZoom = 4; // ? @@ -88,6 +89,7 @@ export class ImageMap extends LeafletMap { let isUpdate = false; const imageUrlSubscriptionOptions: WidgetSubscriptionOptions = { datasources, + hasDataPageLink: true, useDashboardTimewindow: false, type: widgetType.latest, callbacks: { @@ -97,7 +99,15 @@ export class ImageMap extends LeafletMap { } } }; - this.ctx.subscriptionApi.createSubscription(imageUrlSubscriptionOptions, true).subscribe(() => { }); + this.ctx.subscriptionApi.createSubscription(imageUrlSubscriptionOptions, true).subscribe((subscription) => { + const pageLink: EntityDataPageLink = { + page: 0, + pageSize: 1, + textSearch: null, + dynamic: true + }; + subscription.subscribeAllForPaginatedData(pageLink, null); + }); return this.imageFromAlias(result); } @@ -154,32 +164,32 @@ export class ImageMap extends LeafletMap { const southWest = this.pointToLatLng(-padding, h + padding); const northEast = this.pointToLatLng(w + padding, -padding); const maxBounds = new L.LatLngBounds(southWest, northEast); + (this.map as any)._enforcingBounds = true; this.map.setMaxBounds(maxBounds); if (lastCenterPos) { lastCenterPos.x *= w; lastCenterPos.y *= h; const center = this.pointToLatLng(lastCenterPos.x, lastCenterPos.y); - setTimeout(() => { - this.map.panTo(center, { animate: false }); - }, 0); + this.map.panTo(center, { animate: false }); } + (this.map as any)._enforcingBounds = false; } onResize(updateImage?: boolean) { let width = this.$container.clientWidth; if (width > 0 && this.aspect) { - let height = width / this.aspect; + let height = Math.round(width / this.aspect); const imageMapHeight = this.$container.clientHeight; if (imageMapHeight > 0 && height > imageMapHeight) { height = imageMapHeight; - width = height * this.aspect; + width = Math.round(height * this.aspect); } width *= maxZoom; const prevWidth = this.width; const prevHeight = this.height; if (this.width !== width || updateImage) { this.width = width; - this.height = width / this.aspect; + this.height = Math.round(width / this.aspect); if (!this.map) { this.initMap(updateImage); } else { @@ -187,7 +197,9 @@ export class ImageMap extends LeafletMap { lastCenterPos.x /= prevWidth; lastCenterPos.y /= prevHeight; this.updateBounds(updateImage, lastCenterPos); - this.map.invalidateSize(true); + (this.map as any)._enforcingBounds = true; + this.map.invalidateSize(false); + (this.map as any)._enforcingBounds = false; this.updateMarkers(this.markersData); if (this.options.draggableMarker && this.addMarkers.length) { this.addMarkers.forEach((marker) => { @@ -217,6 +229,7 @@ export class ImageMap extends LeafletMap { maxZoom, scrollWheelZoom: !this.options.disableScrollZooming, center, + zoomControl: !this.options.disableZoomControl, zoom: 1, crs: L.CRS.Simple, attributionControl: false, @@ -257,7 +270,7 @@ export class ImageMap extends LeafletMap { return L.CRS.Simple.pointToLatLng({ x, y } as L.PointExpression, maxZoom - 1); } - latLngToPoint(latLng: LatLngLiteral) { + latLngToPoint(latLng: LatLngLiteral): L.Point { return L.CRS.Simple.latLngToPoint(latLng, maxZoom - 1); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/index.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/index.ts index 50c0709fb3..e0f7fc37db 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/index.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/index.ts @@ -1,5 +1,5 @@ /// -/// Copyright © 2016-2021 The Thingsboard Authors +/// Copyright © 2016-2022 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. diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/openstreet-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/openstreet-map.ts index 01eb04ea40..1a27f26931 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/openstreet-map.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/openstreet-map.ts @@ -1,5 +1,5 @@ /// -/// Copyright © 2016-2021 The Thingsboard Authors +/// Copyright © 2016-2022 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. @@ -23,6 +23,7 @@ export class OpenStreetMap extends LeafletMap { constructor(ctx: WidgetContext, $container, options: UnitedMapSettings) { super(ctx, $container, options); const map = L.map($container, { + zoomControl: !this.options.disableZoomControl, tap: L.Browser.safari && L.Browser.mobile }).setView(options?.defaultCenterPosition, options?.defaultZoomLevel || DEFAULT_ZOOM_LEVEL); let tileLayer; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/tencent-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/tencent-map.ts index 5e71d92e5d..fb53f15b3e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/tencent-map.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/tencent-map.ts @@ -1,5 +1,5 @@ /// -/// Copyright © 2016-2021 The Thingsboard Authors +/// Copyright © 2016-2022 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. @@ -25,6 +25,7 @@ export class TencentMap extends LeafletMap { super(ctx, $container, options); const txUrl = 'http://rt{s}.map.gtimg.com/realtimerender?z={z}&x={x}&y={y}&type=vector&style=0'; const map = L.map($container, { + zoomControl: !this.options.disableZoomControl, tap: L.Browser.safari && L.Browser.mobile }).setView(options?.defaultCenterPosition, options?.defaultZoomLevel || DEFAULT_ZOOM_LEVEL); const txLayer = L.tileLayer(txUrl, { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/schemes.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/schemes.ts index 1c383b75e0..6f972e9d3f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/schemes.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/schemes.ts @@ -1,5 +1,5 @@ /// -/// Copyright © 2016-2021 The Thingsboard Authors +/// Copyright © 2016-2022 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. @@ -269,6 +269,11 @@ export const commonMapSettingsSchema = type: 'boolean', default: false }, + disableZoomControl: { + title: 'Disable zoom control buttons', + type: 'boolean', + default: false + }, latKeyName: { title: 'Latitude key name', type: 'string', @@ -424,6 +429,7 @@ export const commonMapSettingsSchema = 'mapPageSize', 'draggableMarker', 'disableScrollZooming', + 'disableZoomControl', { key: 'latKeyName', condition: 'model.provider !== "image-map"' @@ -1329,7 +1335,7 @@ export const editorSettingSchema = default: false }, hideAllControlButton: { - title: 'Hide all button', + title: 'Hide all edit control buttons', type: 'boolean', default: false }, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/markdown-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/markdown-widget.component.html index b808897845..2820a8bade 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/markdown-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/markdown-widget.component.html @@ -1,6 +1,6 @@ -
+
{{ error }} @@ -37,3 +37,14 @@
+ +
+
+ {{title}} + +
+ +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/switch.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/switch.component.scss index 293246c79e..a261ee6b71 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/switch.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/switch.component.scss @@ -1,5 +1,5 @@ /** - * Copyright © 2016-2021 The Thingsboard Authors + * Copyright © 2016-2022 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. diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/switch.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/switch.component.ts index 697c837a6e..96c36337a7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/switch.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/switch.component.ts @@ -1,5 +1,5 @@ /// -/// Copyright © 2016-2021 The Thingsboard Authors +/// Copyright © 2016-2022 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. @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, ElementRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { AfterViewInit, Component, ElementRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { PageComponent } from '@shared/components/page.component'; import { WidgetContext } from '@home/models/widget-component.models'; import { UtilsService } from '@core/services/utils.service'; @@ -26,11 +26,14 @@ import { DatasourceType, widgetType } from '@shared/models/widget.models'; import { EntityType } from '@shared/models/entity-type.models'; import { MatSlideToggle } from '@angular/material/slide-toggle'; import { ResizeObserver } from '@juggle/resize-observer'; +import { ThemePalette } from '@angular/material/core/common-behaviors/color'; const switchAspectRation = 2.7893; type RetrieveValueMethod = 'rpc' | 'attribute' | 'timeseries'; +type SwitchType = 'switch' | 'slide-toggle'; + interface SwitchSettings { initialValue: boolean; title: string; @@ -44,6 +47,8 @@ interface SwitchSettings { requestTimeout: number; requestPersistent: boolean; persistentPollingInterval: number; + labelPosition: 'before' | 'after'; + sliderColor: ThemePalette; } @Component({ @@ -51,28 +56,33 @@ interface SwitchSettings { templateUrl: './switch.component.html', styleUrls: ['./switch.component.scss'] }) -export class SwitchComponent extends PageComponent implements OnInit, OnDestroy { - - @ViewChild('switch', {static: true}) switchElementRef: ElementRef; - @ViewChild('switchContainer', {static: true}) switchContainerRef: ElementRef; - @ViewChild('matSlideToggle', {static: true}) matSlideToggleRef: MatSlideToggle; - @ViewChild('onoffContainer', {static: true}) onoffContainerRef: ElementRef; - @ViewChild('onLabel', {static: true}) onLabelRef: ElementRef; - @ViewChild('offLabel', {static: true}) offLabelRef: ElementRef; - @ViewChild('switchTitleContainer', {static: true}) switchTitleContainerRef: ElementRef; - @ViewChild('switchTitle', {static: true}) switchTitleRef: ElementRef; - @ViewChild('textMeasure', {static: true}) textMeasureRef: ElementRef; - @ViewChild('switchErrorContainer', {static: true}) switchErrorContainerRef: ElementRef; - @ViewChild('switchError', {static: true}) switchErrorRef: ElementRef; +export class SwitchComponent extends PageComponent implements OnInit, AfterViewInit, OnDestroy { + + @ViewChild('switch', {static: false}) switchElementRef: ElementRef; + @ViewChild('switchContainer', {static: false}) switchContainerRef: ElementRef; + @ViewChild('matSlideToggle', {static: false}) matSlideToggleRef: MatSlideToggle; + @ViewChild('onoffContainer', {static: false}) onoffContainerRef: ElementRef; + @ViewChild('onLabel', {static: false}) onLabelRef: ElementRef; + @ViewChild('offLabel', {static: false}) offLabelRef: ElementRef; + @ViewChild('switchTitleContainer', {static: false}) switchTitleContainerRef: ElementRef; + @ViewChild('switchTitle', {static: false}) switchTitleRef: ElementRef; + @ViewChild('textMeasure', {static: false}) textMeasureRef: ElementRef; + @ViewChild('switchErrorContainer', {static: false}) switchErrorContainerRef: ElementRef; + @ViewChild('switchError', {static: false}) switchErrorRef: ElementRef; @Input() ctx: WidgetContext; + @Input() + switchType: SwitchType = 'switch'; + showTitle = false; value = false; error = ''; title = ''; showOnOffLabels = false; + labelPosition: 'before' | 'after' = 'after'; + sliderColor: ThemePalette = 'accent'; private isSimulated: boolean; private requestTimeout: number; @@ -87,7 +97,7 @@ export class SwitchComponent extends PageComponent implements OnInit, OnDestroy private valueSubscription: IWidgetSubscription; - private executingUpdateValue: boolean; + public executingUpdateValue: boolean; private scheduledValue: boolean; private rpcValue: boolean; @@ -111,22 +121,27 @@ export class SwitchComponent extends PageComponent implements OnInit, OnDestroy } ngOnInit(): void { - this.switchElement = $(this.switchElementRef.nativeElement); - this.switchContainer = $(this.switchContainerRef.nativeElement); - this.matSlideToggle = $(this.matSlideToggleRef._elementRef.nativeElement); - this.onoffContainer = $(this.onoffContainerRef.nativeElement); - this.onLabel = $(this.onLabelRef.nativeElement); - this.offLabel = $(this.offLabelRef.nativeElement); - this.switchTitleContainer = $(this.switchTitleContainerRef.nativeElement); - this.switchTitle = $(this.switchTitleRef.nativeElement); - this.textMeasure = $(this.textMeasureRef.nativeElement); - this.switchErrorContainer = $(this.switchErrorContainerRef.nativeElement); - this.switchError = $(this.switchErrorRef.nativeElement); - - this.switchResize$ = new ResizeObserver(() => { - this.resize(); - }); - this.switchResize$.observe(this.switchContainerRef.nativeElement); + } + + ngAfterViewInit() { + if (this.switchType === 'switch') { + this.switchElement = $(this.switchElementRef.nativeElement); + this.switchContainer = $(this.switchContainerRef.nativeElement); + this.matSlideToggle = $(this.matSlideToggleRef._elementRef.nativeElement); + this.onoffContainer = $(this.onoffContainerRef.nativeElement); + this.onLabel = $(this.onLabelRef.nativeElement); + this.offLabel = $(this.offLabelRef.nativeElement); + this.switchTitleContainer = $(this.switchTitleContainerRef.nativeElement); + this.switchTitle = $(this.switchTitleRef.nativeElement); + this.textMeasure = $(this.textMeasureRef.nativeElement); + this.switchErrorContainer = $(this.switchErrorContainerRef.nativeElement); + this.switchError = $(this.switchErrorRef.nativeElement); + + this.switchResize$ = new ResizeObserver(() => { + this.resize(); + }); + this.switchResize$.observe(this.switchContainerRef.nativeElement); + } this.init(); } @@ -145,6 +160,8 @@ export class SwitchComponent extends PageComponent implements OnInit, OnDestroy this.title = isDefined(settings.title) ? settings.title : ''; this.showTitle = !!(this.title && this.title.length); this.showOnOffLabels = isDefined(settings.showOnOffLabels) ? settings.showOnOffLabels : true; + this.labelPosition = isDefined(settings.labelPosition) ? settings.labelPosition : 'after'; + this.sliderColor = isDefined(settings.sliderColor) ? settings.sliderColor : 'accent'; const initialValue = isDefined(settings.initialValue) ? settings.initialValue : false; this.setValue(initialValue); @@ -256,7 +273,9 @@ export class SwitchComponent extends PageComponent implements OnInit, OnDestroy private onError(error: string) { this.error = error; - this.setFontSize(this.switchError, this.error, this.switchErrorContainer.height(), this.switchErrorContainer.width()); + if (this.switchType === 'switch') { + this.setFontSize(this.switchError, this.error, this.switchErrorContainer.height(), this.switchErrorContainer.width()); + } this.ctx.detectChanges(); } @@ -278,6 +297,10 @@ export class SwitchComponent extends PageComponent implements OnInit, OnDestroy }, () => { const errorText = this.ctx.defaultSubscription.rpcErrorText; + if (this.switchType === 'slide-toggle') { + this.ctx.defaultSubscription.rpcErrorText = null; + this.ctx.hideToast(this.ctx.toastTargetId); + } this.onError(errorText); } ); @@ -304,6 +327,10 @@ export class SwitchComponent extends PageComponent implements OnInit, OnDestroy () => { this.executingUpdateValue = false; const errorText = this.ctx.defaultSubscription.rpcErrorText; + if (this.switchType === 'slide-toggle') { + this.ctx.defaultSubscription.rpcErrorText = null; + this.ctx.hideToast(this.ctx.toastTargetId); + } this.onError(errorText); } ); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings.models.ts index f7aac069a3..e5089d3070 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings.models.ts @@ -1,5 +1,5 @@ /// -/// Copyright © 2016-2021 The Thingsboard Authors +/// Copyright © 2016-2022 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. diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/table-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/table-widget.models.ts index 1d798a88ca..e4df1cd311 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/table-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/table-widget.models.ts @@ -1,5 +1,5 @@ /// -/// Copyright © 2016-2021 The Thingsboard Authors +/// Copyright © 2016-2022 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. diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/table-widget.scss b/ui-ngx/src/app/modules/home/components/widget/lib/table-widget.scss index 98c3dd67a2..0ef9fa5d12 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/table-widget.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/table-widget.scss @@ -1,5 +1,5 @@ /** - * Copyright © 2016-2021 The Thingsboard Authors + * Copyright © 2016-2022 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. diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.html index 4f193a6cd7..19f2429ff4 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.html @@ -1,6 +1,6 @@ - + {{section.icon}} {{section.name | translate}} diff --git a/ui-ngx/src/app/modules/home/menu/menu-link.component.scss b/ui-ngx/src/app/modules/home/menu/menu-link.component.scss index 3abcc4c738..da8df4b469 100644 --- a/ui-ngx/src/app/modules/home/menu/menu-link.component.scss +++ b/ui-ngx/src/app/modules/home/menu/menu-link.component.scss @@ -1,5 +1,5 @@ /** - * Copyright © 2016-2021 The Thingsboard Authors + * Copyright © 2016-2022 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. diff --git a/ui-ngx/src/app/modules/home/menu/menu-link.component.ts b/ui-ngx/src/app/modules/home/menu/menu-link.component.ts index 58724b89f9..cae14d32b6 100644 --- a/ui-ngx/src/app/modules/home/menu/menu-link.component.ts +++ b/ui-ngx/src/app/modules/home/menu/menu-link.component.ts @@ -1,5 +1,5 @@ /// -/// Copyright © 2016-2021 The Thingsboard Authors +/// Copyright © 2016-2022 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. diff --git a/ui-ngx/src/app/modules/home/menu/menu-toggle.component.html b/ui-ngx/src/app/modules/home/menu/menu-toggle.component.html index 0a41e75657..0635205581 100644 --- a/ui-ngx/src/app/modules/home/menu/menu-toggle.component.html +++ b/ui-ngx/src/app/modules/home/menu/menu-toggle.component.html @@ -1,6 +1,6 @@ + routerLinkActive="tb-active" [routerLinkActiveOptions]="{paths: 'exact', queryParams: 'ignored', matrixParams: 'ignored', fragment: 'ignored'}" routerLink="{{section.path}}"> {{section.icon}} {{section.name | translate}} diff --git a/ui-ngx/src/app/modules/home/menu/menu-toggle.component.scss b/ui-ngx/src/app/modules/home/menu/menu-toggle.component.scss index 87899b02b5..a076fa77d6 100644 --- a/ui-ngx/src/app/modules/home/menu/menu-toggle.component.scss +++ b/ui-ngx/src/app/modules/home/menu/menu-toggle.component.scss @@ -1,5 +1,5 @@ /** - * Copyright © 2016-2021 The Thingsboard Authors + * Copyright © 2016-2022 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. diff --git a/ui-ngx/src/app/modules/home/menu/menu-toggle.component.ts b/ui-ngx/src/app/modules/home/menu/menu-toggle.component.ts index 6bbc5414fe..76a818c16b 100644 --- a/ui-ngx/src/app/modules/home/menu/menu-toggle.component.ts +++ b/ui-ngx/src/app/modules/home/menu/menu-toggle.component.ts @@ -1,5 +1,5 @@ /// -/// Copyright © 2016-2021 The Thingsboard Authors +/// Copyright © 2016-2022 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. diff --git a/ui-ngx/src/app/modules/home/menu/side-menu.component.html b/ui-ngx/src/app/modules/home/menu/side-menu.component.html index 91275719e9..8a9f07f3c9 100644 --- a/ui-ngx/src/app/modules/home/menu/side-menu.component.html +++ b/ui-ngx/src/app/modules/home/menu/side-menu.component.html @@ -1,6 +1,6 @@