Commit 257f1a7ce78c043a4f765f41629e9fb1e23034c4
Merge branch 'master' of github.com:thingsboard/thingsboard
Showing
7 changed files
with
81 additions
and
47 deletions
@@ -128,7 +128,7 @@ public class TbAlarmDataSubCtx extends TbAbstractDataSubCtx<AlarmDataQuery> { | @@ -128,7 +128,7 @@ public class TbAlarmDataSubCtx extends TbAbstractDataSubCtx<AlarmDataQuery> { | ||
128 | } | 128 | } |
129 | } | 129 | } |
130 | alarmsMap.clear(); | 130 | alarmsMap.clear(); |
131 | - alarmsMap.putAll(alarms.getData().stream().collect(Collectors.toMap(AlarmData::getId, Function.identity()))); | 131 | + alarmsMap.putAll(alarms.getData().stream().collect(Collectors.toMap(AlarmData::getId, Function.identity(), (a, b) -> a))); |
132 | return this.alarms; | 132 | return this.alarms; |
133 | } | 133 | } |
134 | 134 |
@@ -22,13 +22,15 @@ import { TranslateService } from '@ngx-translate/core'; | @@ -22,13 +22,15 @@ import { TranslateService } from '@ngx-translate/core'; | ||
22 | import { Subscription } from 'rxjs'; | 22 | import { Subscription } from 'rxjs'; |
23 | import { BreakpointObserver } from '@angular/cdk/layout'; | 23 | import { BreakpointObserver } from '@angular/cdk/layout'; |
24 | import { deepClone } from '@core/utils'; | 24 | import { deepClone } from '@core/utils'; |
25 | -import { FilterInfo, isFilterEditable } from '@shared/models/query/query.models'; | 25 | +import { Filter, FilterInfo, isFilterEditable } from '@shared/models/query/query.models'; |
26 | import { | 26 | import { |
27 | FILTER_EDIT_PANEL_DATA, | 27 | FILTER_EDIT_PANEL_DATA, |
28 | FiltersEditPanelComponent, | 28 | FiltersEditPanelComponent, |
29 | FiltersEditPanelData | 29 | FiltersEditPanelData |
30 | } from '@home/components/filter/filters-edit-panel.component'; | 30 | } from '@home/components/filter/filters-edit-panel.component'; |
31 | import { ComponentPortal, PortalInjector } from '@angular/cdk/portal'; | 31 | import { ComponentPortal, PortalInjector } from '@angular/cdk/portal'; |
32 | +import { UserFilterDialogComponent, UserFilterDialogData } from '@home/components/filter/user-filter-dialog.component'; | ||
33 | +import { MatDialog } from '@angular/material/dialog'; | ||
32 | 34 | ||
33 | @Component({ | 35 | @Component({ |
34 | selector: 'tb-filters-edit', | 36 | selector: 'tb-filters-edit', |
@@ -65,7 +67,8 @@ export class FiltersEditComponent implements OnInit, OnDestroy { | @@ -65,7 +67,8 @@ export class FiltersEditComponent implements OnInit, OnDestroy { | ||
65 | constructor(private translate: TranslateService, | 67 | constructor(private translate: TranslateService, |
66 | private overlay: Overlay, | 68 | private overlay: Overlay, |
67 | private breakpointObserver: BreakpointObserver, | 69 | private breakpointObserver: BreakpointObserver, |
68 | - private viewContainerRef: ViewContainerRef) { | 70 | + private viewContainerRef: ViewContainerRef, |
71 | + private dialog: MatDialog) { | ||
69 | } | 72 | } |
70 | 73 | ||
71 | private setupAliasController(aliasController: IAliasController) { | 74 | private setupAliasController(aliasController: IAliasController) { |
@@ -101,33 +104,53 @@ export class FiltersEditComponent implements OnInit, OnDestroy { | @@ -101,33 +104,53 @@ export class FiltersEditComponent implements OnInit, OnDestroy { | ||
101 | if (this.disabled || !this.hasEditableFilters) { | 104 | if (this.disabled || !this.hasEditableFilters) { |
102 | return; | 105 | return; |
103 | } | 106 | } |
104 | - const position = this.overlay.position(); | ||
105 | - const config = new OverlayConfig({ | ||
106 | - panelClass: 'tb-filters-edit-panel', | ||
107 | - backdropClass: 'cdk-overlay-transparent-backdrop', | ||
108 | - hasBackdrop: true, | ||
109 | - }); | ||
110 | - const connectedPosition: ConnectedPosition = { | ||
111 | - originX: 'start', | ||
112 | - originY: 'bottom', | ||
113 | - overlayX: 'start', | ||
114 | - overlayY: 'top' | ||
115 | - }; | ||
116 | - config.positionStrategy = position.flexibleConnectedTo(this.filtersEditPanelOrigin.elementRef) | ||
117 | - .withPositions([connectedPosition]); | ||
118 | - const overlayRef = this.overlay.create(config); | ||
119 | - overlayRef.backdropClick().subscribe(() => { | ||
120 | - overlayRef.dispose(); | ||
121 | - }); | ||
122 | - | ||
123 | - const injector = this._createFiltersEditPanelInjector( | ||
124 | - overlayRef, | ||
125 | - { | ||
126 | - aliasController: this.aliasController, | ||
127 | - filtersInfo: deepClone(this.filtersInfo) | ||
128 | - } | ||
129 | - ); | ||
130 | - overlayRef.attach(new ComponentPortal(FiltersEditPanelComponent, this.viewContainerRef, injector)); | 107 | + const filteredArray = Object.entries(this.filtersInfo); |
108 | + | ||
109 | + if (filteredArray.length === 1) { | ||
110 | + const singleFilter: Filter = {id: filteredArray[0][0], ...filteredArray[0][1]}; | ||
111 | + this.dialog.open<UserFilterDialogComponent, UserFilterDialogData, | ||
112 | + Filter>(UserFilterDialogComponent, { | ||
113 | + disableClose: true, | ||
114 | + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], | ||
115 | + data: { | ||
116 | + filter: singleFilter | ||
117 | + } | ||
118 | + }).afterClosed().subscribe( | ||
119 | + (result) => { | ||
120 | + if (result) { | ||
121 | + this.filtersInfo[result.id] = result; | ||
122 | + this.aliasController.updateUserFilter(result); | ||
123 | + } | ||
124 | + }); | ||
125 | + } else { | ||
126 | + const position = this.overlay.position(); | ||
127 | + const config = new OverlayConfig({ | ||
128 | + panelClass: 'tb-filters-edit-panel', | ||
129 | + backdropClass: 'cdk-overlay-transparent-backdrop', | ||
130 | + hasBackdrop: true, | ||
131 | + }); | ||
132 | + const connectedPosition: ConnectedPosition = { | ||
133 | + originX: 'start', | ||
134 | + originY: 'bottom', | ||
135 | + overlayX: 'start', | ||
136 | + overlayY: 'top' | ||
137 | + }; | ||
138 | + config.positionStrategy = position.flexibleConnectedTo(this.filtersEditPanelOrigin.elementRef) | ||
139 | + .withPositions([connectedPosition]); | ||
140 | + const overlayRef = this.overlay.create(config); | ||
141 | + overlayRef.backdropClick().subscribe(() => { | ||
142 | + overlayRef.dispose(); | ||
143 | + }); | ||
144 | + | ||
145 | + const injector = this._createFiltersEditPanelInjector( | ||
146 | + overlayRef, | ||
147 | + { | ||
148 | + aliasController: this.aliasController, | ||
149 | + filtersInfo: deepClone(this.filtersInfo) | ||
150 | + } | ||
151 | + ); | ||
152 | + overlayRef.attach(new ComponentPortal(FiltersEditPanelComponent, this.viewContainerRef, injector)); | ||
153 | + } | ||
131 | } | 154 | } |
132 | 155 | ||
133 | private _createFiltersEditPanelInjector(overlayRef: OverlayRef, data: FiltersEditPanelData): PortalInjector { | 156 | private _createFiltersEditPanelInjector(overlayRef: OverlayRef, data: FiltersEditPanelData): PortalInjector { |
@@ -42,7 +42,7 @@ import { Polygon } from './polygon'; | @@ -42,7 +42,7 @@ import { Polygon } from './polygon'; | ||
42 | import { createTooltip, parseArray, safeExecute } from '@home/components/widget/lib/maps/maps-utils'; | 42 | import { createTooltip, parseArray, safeExecute } from '@home/components/widget/lib/maps/maps-utils'; |
43 | import { WidgetContext } from '@home/models/widget-component.models'; | 43 | import { WidgetContext } from '@home/models/widget-component.models'; |
44 | import { DatasourceData } from '@shared/models/widget.models'; | 44 | import { DatasourceData } from '@shared/models/widget.models'; |
45 | -import { deepClone } from '@core/utils'; | 45 | +import { deepClone, isDefinedAndNotNull } from '@core/utils'; |
46 | 46 | ||
47 | export default abstract class LeafletMap { | 47 | export default abstract class LeafletMap { |
48 | 48 | ||
@@ -249,10 +249,10 @@ export default abstract class LeafletMap { | @@ -249,10 +249,10 @@ export default abstract class LeafletMap { | ||
249 | if (!expression) return null; | 249 | if (!expression) return null; |
250 | const lat = expression[this.options.latKeyName]; | 250 | const lat = expression[this.options.latKeyName]; |
251 | const lng = expression[this.options.lngKeyName]; | 251 | const lng = expression[this.options.lngKeyName]; |
252 | - if (isNaN(lat) || isNaN(lng)) | ||
253 | - return null; | ||
254 | - else | ||
255 | - return L.latLng(lat, lng) as L.LatLng; | 252 | + if (!isDefinedAndNotNull(lat) || isNaN(lat) || !isDefinedAndNotNull(lng) || isNaN(lng)) { |
253 | + return null; | ||
254 | + } | ||
255 | + return L.latLng(lat, lng) as L.LatLng; | ||
256 | } | 256 | } |
257 | 257 | ||
258 | convertPositionPolygon(expression: Array<[number, number]>): L.LatLngExpression[] { | 258 | convertPositionPolygon(expression: Array<[number, number]>): L.LatLngExpression[] { |
@@ -25,7 +25,7 @@ import { | @@ -25,7 +25,7 @@ import { | ||
25 | } from './schemes'; | 25 | } from './schemes'; |
26 | import { EntityType } from '@shared/models/entity-type.models'; | 26 | import { EntityType } from '@shared/models/entity-type.models'; |
27 | 27 | ||
28 | -export const DEFAULT_MAP_PAGE_SIZE = 1024; | 28 | +export const DEFAULT_MAP_PAGE_SIZE = 16384; |
29 | 29 | ||
30 | export type GenericFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string; | 30 | export type GenericFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string; |
31 | export type MarkerImageFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string; | 31 | export type MarkerImageFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string; |
@@ -68,6 +68,7 @@ export type MapSettings = { | @@ -68,6 +68,7 @@ export type MapSettings = { | ||
68 | removeOutsideVisibleBounds: boolean, | 68 | removeOutsideVisibleBounds: boolean, |
69 | useCustomProvider: boolean, | 69 | useCustomProvider: boolean, |
70 | customProviderTileUrl: string; | 70 | customProviderTileUrl: string; |
71 | + mapPageSize: number; | ||
71 | } | 72 | } |
72 | 73 | ||
73 | export enum MapProviders { | 74 | export enum MapProviders { |
@@ -267,7 +268,8 @@ export const defaultSettings: any = { | @@ -267,7 +268,8 @@ export const defaultSettings: any = { | ||
267 | credentials: '', | 268 | credentials: '', |
268 | markerClusteringSetting: null, | 269 | markerClusteringSetting: null, |
269 | draggableMarker: false, | 270 | draggableMarker: false, |
270 | - fitMapBounds: true | 271 | + fitMapBounds: true, |
272 | + mapPageSize: DEFAULT_MAP_PAGE_SIZE | ||
271 | }; | 273 | }; |
272 | 274 | ||
273 | export const hereProviders = [ | 275 | export const hereProviders = [ |
@@ -81,7 +81,7 @@ export class MapWidgetController implements MapWidgetInterface { | @@ -81,7 +81,7 @@ export class MapWidgetController implements MapWidgetInterface { | ||
81 | this.map.saveMarkerLocation = this.setMarkerLocation; | 81 | this.map.saveMarkerLocation = this.setMarkerLocation; |
82 | this.pageLink = { | 82 | this.pageLink = { |
83 | page: 0, | 83 | page: 0, |
84 | - pageSize: DEFAULT_MAP_PAGE_SIZE, | 84 | + pageSize: this.settings.mapPageSize, |
85 | textSearch: null, | 85 | textSearch: null, |
86 | dynamic: true | 86 | dynamic: true |
87 | }; | 87 | }; |
@@ -24,6 +24,7 @@ import { WidgetContext } from '@home/models/widget-component.models'; | @@ -24,6 +24,7 @@ import { WidgetContext } from '@home/models/widget-component.models'; | ||
24 | import { DataSet, DatasourceType, widgetType } from '@shared/models/widget.models'; | 24 | import { DataSet, DatasourceType, widgetType } from '@shared/models/widget.models'; |
25 | import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; | 25 | import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; |
26 | import { WidgetSubscriptionOptions } from '@core/api/widget-api.models'; | 26 | import { WidgetSubscriptionOptions } from '@core/api/widget-api.models'; |
27 | +import { isDefinedAndNotNull } from '@core/utils'; | ||
27 | 28 | ||
28 | const maxZoom = 4;// ? | 29 | const maxZoom = 4;// ? |
29 | 30 | ||
@@ -209,11 +210,15 @@ export class ImageMap extends LeafletMap { | @@ -209,11 +210,15 @@ export class ImageMap extends LeafletMap { | ||
209 | } | 210 | } |
210 | 211 | ||
211 | convertPosition(expression): L.LatLng { | 212 | convertPosition(expression): L.LatLng { |
212 | - if (isNaN(expression[this.options.xPosKeyName]) || isNaN(expression[this.options.yPosKeyName])) return null; | ||
213 | - Object.assign(expression, this.posFunction(expression[this.options.xPosKeyName], expression[this.options.yPosKeyName])); | ||
214 | - return this.pointToLatLng( | ||
215 | - expression.x * this.width, | ||
216 | - expression.y * this.height); | 213 | + const xPos = expression[this.options.xPosKeyName]; |
214 | + const yPos = expression[this.options.yPosKeyName]; | ||
215 | + if (!isDefinedAndNotNull(xPos) || isNaN(xPos) || !isDefinedAndNotNull(yPos) || isNaN(yPos)) { | ||
216 | + return null; | ||
217 | + } | ||
218 | + Object.assign(expression, this.posFunction(xPos, yPos)); | ||
219 | + return this.pointToLatLng( | ||
220 | + expression.x * this.width, | ||
221 | + expression.y * this.height); | ||
217 | } | 222 | } |
218 | 223 | ||
219 | convertPositionPolygon(expression: Array<[number, number]>): L.LatLngExpression[] { | 224 | convertPositionPolygon(expression: Array<[number, number]>): L.LatLngExpression[] { |
@@ -14,6 +14,8 @@ | @@ -14,6 +14,8 @@ | ||
14 | /// limitations under the License. | 14 | /// limitations under the License. |
15 | /// | 15 | /// |
16 | 16 | ||
17 | +import { DEFAULT_MAP_PAGE_SIZE } from '@home/components/widget/lib/maps/map-models'; | ||
18 | + | ||
17 | export const googleMapSettingsSchema = | 19 | export const googleMapSettingsSchema = |
18 | { | 20 | { |
19 | schema: { | 21 | schema: { |
@@ -198,10 +200,6 @@ export const openstreetMapSettingsSchema = | @@ -198,10 +200,6 @@ export const openstreetMapSettingsSchema = | ||
198 | label: 'OpenStreetMap.Mapnik (Default)' | 200 | label: 'OpenStreetMap.Mapnik (Default)' |
199 | }, | 201 | }, |
200 | { | 202 | { |
201 | - value: 'OpenStreetMap.BlackAndWhite', | ||
202 | - label: 'OpenStreetMap.BlackAndWhite' | ||
203 | - }, | ||
204 | - { | ||
205 | value: 'OpenStreetMap.HOT', | 203 | value: 'OpenStreetMap.HOT', |
206 | label: 'OpenStreetMap.HOT' | 204 | label: 'OpenStreetMap.HOT' |
207 | }, | 205 | }, |
@@ -246,6 +244,11 @@ export const commonMapSettingsSchema = | @@ -246,6 +244,11 @@ export const commonMapSettingsSchema = | ||
246 | type: 'boolean', | 244 | type: 'boolean', |
247 | default: false | 245 | default: false |
248 | }, | 246 | }, |
247 | + mapPageSize: { | ||
248 | + title: 'Map page size load entities', | ||
249 | + type: 'number', | ||
250 | + default: DEFAULT_MAP_PAGE_SIZE | ||
251 | + }, | ||
249 | defaultCenterPosition: { | 252 | defaultCenterPosition: { |
250 | title: 'Default map center position (0,0)', | 253 | title: 'Default map center position (0,0)', |
251 | type: 'string', | 254 | type: 'string', |
@@ -408,6 +411,7 @@ export const commonMapSettingsSchema = | @@ -408,6 +411,7 @@ export const commonMapSettingsSchema = | ||
408 | key: 'fitMapBounds', | 411 | key: 'fitMapBounds', |
409 | condition: 'model.provider !== "image-map"' | 412 | condition: 'model.provider !== "image-map"' |
410 | }, | 413 | }, |
414 | + 'mapPageSize', | ||
411 | 'draggableMarker', | 415 | 'draggableMarker', |
412 | { | 416 | { |
413 | key: 'disableScrollZooming', | 417 | key: 'disableScrollZooming', |