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 | 128 | } |
129 | 129 | } |
130 | 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 | 132 | return this.alarms; |
133 | 133 | } |
134 | 134 | ... | ... |
... | ... | @@ -22,13 +22,15 @@ import { TranslateService } from '@ngx-translate/core'; |
22 | 22 | import { Subscription } from 'rxjs'; |
23 | 23 | import { BreakpointObserver } from '@angular/cdk/layout'; |
24 | 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 | 26 | import { |
27 | 27 | FILTER_EDIT_PANEL_DATA, |
28 | 28 | FiltersEditPanelComponent, |
29 | 29 | FiltersEditPanelData |
30 | 30 | } from '@home/components/filter/filters-edit-panel.component'; |
31 | 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 | 35 | @Component({ |
34 | 36 | selector: 'tb-filters-edit', |
... | ... | @@ -65,7 +67,8 @@ export class FiltersEditComponent implements OnInit, OnDestroy { |
65 | 67 | constructor(private translate: TranslateService, |
66 | 68 | private overlay: Overlay, |
67 | 69 | private breakpointObserver: BreakpointObserver, |
68 | - private viewContainerRef: ViewContainerRef) { | |
70 | + private viewContainerRef: ViewContainerRef, | |
71 | + private dialog: MatDialog) { | |
69 | 72 | } |
70 | 73 | |
71 | 74 | private setupAliasController(aliasController: IAliasController) { |
... | ... | @@ -101,33 +104,53 @@ export class FiltersEditComponent implements OnInit, OnDestroy { |
101 | 104 | if (this.disabled || !this.hasEditableFilters) { |
102 | 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 | 156 | private _createFiltersEditPanelInjector(overlayRef: OverlayRef, data: FiltersEditPanelData): PortalInjector { | ... | ... |
... | ... | @@ -42,7 +42,7 @@ import { Polygon } from './polygon'; |
42 | 42 | import { createTooltip, parseArray, safeExecute } from '@home/components/widget/lib/maps/maps-utils'; |
43 | 43 | import { WidgetContext } from '@home/models/widget-component.models'; |
44 | 44 | import { DatasourceData } from '@shared/models/widget.models'; |
45 | -import { deepClone } from '@core/utils'; | |
45 | +import { deepClone, isDefinedAndNotNull } from '@core/utils'; | |
46 | 46 | |
47 | 47 | export default abstract class LeafletMap { |
48 | 48 | |
... | ... | @@ -249,10 +249,10 @@ export default abstract class LeafletMap { |
249 | 249 | if (!expression) return null; |
250 | 250 | const lat = expression[this.options.latKeyName]; |
251 | 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 | 258 | convertPositionPolygon(expression: Array<[number, number]>): L.LatLngExpression[] { | ... | ... |
... | ... | @@ -25,7 +25,7 @@ import { |
25 | 25 | } from './schemes'; |
26 | 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 | 30 | export type GenericFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string; |
31 | 31 | export type MarkerImageFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string; |
... | ... | @@ -68,6 +68,7 @@ export type MapSettings = { |
68 | 68 | removeOutsideVisibleBounds: boolean, |
69 | 69 | useCustomProvider: boolean, |
70 | 70 | customProviderTileUrl: string; |
71 | + mapPageSize: number; | |
71 | 72 | } |
72 | 73 | |
73 | 74 | export enum MapProviders { |
... | ... | @@ -267,7 +268,8 @@ export const defaultSettings: any = { |
267 | 268 | credentials: '', |
268 | 269 | markerClusteringSetting: null, |
269 | 270 | draggableMarker: false, |
270 | - fitMapBounds: true | |
271 | + fitMapBounds: true, | |
272 | + mapPageSize: DEFAULT_MAP_PAGE_SIZE | |
271 | 273 | }; |
272 | 274 | |
273 | 275 | export const hereProviders = [ | ... | ... |
... | ... | @@ -81,7 +81,7 @@ export class MapWidgetController implements MapWidgetInterface { |
81 | 81 | this.map.saveMarkerLocation = this.setMarkerLocation; |
82 | 82 | this.pageLink = { |
83 | 83 | page: 0, |
84 | - pageSize: DEFAULT_MAP_PAGE_SIZE, | |
84 | + pageSize: this.settings.mapPageSize, | |
85 | 85 | textSearch: null, |
86 | 86 | dynamic: true |
87 | 87 | }; | ... | ... |
... | ... | @@ -24,6 +24,7 @@ import { WidgetContext } from '@home/models/widget-component.models'; |
24 | 24 | import { DataSet, DatasourceType, widgetType } from '@shared/models/widget.models'; |
25 | 25 | import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; |
26 | 26 | import { WidgetSubscriptionOptions } from '@core/api/widget-api.models'; |
27 | +import { isDefinedAndNotNull } from '@core/utils'; | |
27 | 28 | |
28 | 29 | const maxZoom = 4;// ? |
29 | 30 | |
... | ... | @@ -209,11 +210,15 @@ export class ImageMap extends LeafletMap { |
209 | 210 | } |
210 | 211 | |
211 | 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 | 224 | convertPositionPolygon(expression: Array<[number, number]>): L.LatLngExpression[] { | ... | ... |
... | ... | @@ -14,6 +14,8 @@ |
14 | 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 | 19 | export const googleMapSettingsSchema = |
18 | 20 | { |
19 | 21 | schema: { |
... | ... | @@ -198,10 +200,6 @@ export const openstreetMapSettingsSchema = |
198 | 200 | label: 'OpenStreetMap.Mapnik (Default)' |
199 | 201 | }, |
200 | 202 | { |
201 | - value: 'OpenStreetMap.BlackAndWhite', | |
202 | - label: 'OpenStreetMap.BlackAndWhite' | |
203 | - }, | |
204 | - { | |
205 | 203 | value: 'OpenStreetMap.HOT', |
206 | 204 | label: 'OpenStreetMap.HOT' |
207 | 205 | }, |
... | ... | @@ -246,6 +244,11 @@ export const commonMapSettingsSchema = |
246 | 244 | type: 'boolean', |
247 | 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 | 252 | defaultCenterPosition: { |
250 | 253 | title: 'Default map center position (0,0)', |
251 | 254 | type: 'string', |
... | ... | @@ -408,6 +411,7 @@ export const commonMapSettingsSchema = |
408 | 411 | key: 'fitMapBounds', |
409 | 412 | condition: 'model.provider !== "image-map"' |
410 | 413 | }, |
414 | + 'mapPageSize', | |
411 | 415 | 'draggableMarker', |
412 | 416 | { |
413 | 417 | key: 'disableScrollZooming', | ... | ... |