Browse Source

UI: Marker settings.

pull/12723/head
Igor Kulikov 2 years ago
parent
commit
2e5031faec
  1. 1
      ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet/leaflet-tb.ts
  2. 16
      ui-ngx/src/app/modules/home/components/widget/lib/maps/map-data-layer.ts
  3. 21
      ui-ngx/src/app/modules/home/components/widget/lib/maps/map.models.ts
  4. 58
      ui-ngx/src/app/modules/home/components/widget/lib/maps/map.scss
  5. 52
      ui-ngx/src/app/modules/home/components/widget/lib/maps/map.ts
  6. 69
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/data-layer-color-settings-panel.component.html
  7. 53
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/data-layer-color-settings-panel.component.scss
  8. 82
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/data-layer-color-settings-panel.component.ts
  9. 30
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/data-layer-color-settings.component.html
  10. 113
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/data-layer-color-settings.component.ts
  11. 34
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-dialog.component.html
  12. 25
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-dialog.component.ts
  13. 72
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-image-settings-panel.component.html
  14. 53
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-image-settings-panel.component.scss
  15. 101
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-image-settings-panel.component.ts
  16. 28
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-image-settings.component.html
  17. 96
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-image-settings.component.ts
  18. 16
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts
  19. 20
      ui-ngx/src/assets/locale/locale.constant-en_US.json

1
ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet/leaflet-tb.ts

@ -99,6 +99,7 @@ class SidebarPaneControl<O extends TB.SidebarPaneControlOptions> extends L.Contr
this.button = $("<a>") this.button = $("<a>")
.attr('class', 'tb-control-button') .attr('class', 'tb-control-button')
.attr('href', '#') .attr('href', '#')
.attr('role', 'button')
.html('<div class="' + this.options.uiClass + '"></div>') .html('<div class="' + this.options.uiClass + '"></div>')
.on('click', (e) => { .on('click', (e) => {
this.toggle(e); this.toggle(e);

16
ui-ngx/src/app/modules/home/components/widget/lib/maps/map-data-layer.ts

@ -16,6 +16,7 @@
import { import {
CirclesDataLayerSettings, CirclesDataLayerSettings,
createColorMarkerURI,
DataLayerColorSettings, DataLayerColorSettings,
DataLayerColorType, DataLayerColorType,
defaultBaseCirclesDataLayerSettings, defaultBaseCirclesDataLayerSettings,
@ -487,19 +488,6 @@ export class TbMarkersDataLayer extends TbMapDataLayer<MarkersDataLayerSettings,
} }
} }
private createColorIconURI(color: tinycolor.Instance): string {
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="-191.35 -351.18 1083.58 1730.46">` +
`<path fill-rule="evenodd" clip-rule="evenodd" fill="#${color.toHex()}" stroke="#000" stroke-width="37" ` +
`stroke-miterlimit="10" d="M351.833 1360.78c-38.766-190.3-107.116-348.665-189.903-495.44C100.523 756.469 ` +
`29.386 655.978-36.434 550.404c-21.972-35.244-40.934-72.477-62.047-109.054-42.216-73.137-76.444-157.935-74.269-267.932 ` +
`2.125-107.473 33.208-193.685 78.03-264.173C-21-206.69 102.481-301.745 268.164-326.724c135.466-20.425 262.475 14.082 ` +
`352.543 66.747 73.6 43.038 130.596 100.528 173.92 168.28 45.22 70.716 76.36 154.26 78.971 263.233 1.337 55.83-7.805 ` +
`107.532-20.684 150.417-13.034 43.41-33.996 79.695-52.646 118.455-36.406 75.659-82.049 144.981-127.855 214.345-136.437 ` +
`206.606-264.496 417.31-320.58 706.028z"/><circle fill-rule="evenodd" ` +
`clip-rule="evenodd" cx="352.891" cy="225.779" r="183.332"/></svg>`;
return 'data:image/svg+xml;base64,' + btoa(svg);
}
public createDefaultMarkerIcon(): MarkerIconInfo { public createDefaultMarkerIcon(): MarkerIconInfo {
const color = this.settings.markerColor.color || '#FE7569'; const color = this.settings.markerColor.color || '#FE7569';
return this.createColoredMarkerIcon(tinycolor(color)); return this.createColoredMarkerIcon(tinycolor(color));
@ -509,7 +497,7 @@ export class TbMarkersDataLayer extends TbMapDataLayer<MarkersDataLayerSettings,
return { return {
size: [21, 34], size: [21, 34],
icon: L.icon({ icon: L.icon({
iconUrl: this.createColorIconURI(color), iconUrl: createColorMarkerURI(color),
iconSize: [21, 34], iconSize: [21, 34],
iconAnchor: [21 * this.markerOffset[0], 34 * this.markerOffset[1]], iconAnchor: [21 * this.markerOffset[0], 34 * this.markerOffset[1]],
popupAnchor: [0, -34], popupAnchor: [0, -34],

21
ui-ngx/src/app/modules/home/components/widget/lib/maps/map.models.ts

@ -24,6 +24,20 @@ import { TbFunction } from '@shared/models/js-function.models';
import { Observable, Observer, of, switchMap } from 'rxjs'; import { Observable, Observer, of, switchMap } from 'rxjs';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { ImagePipe } from '@shared/pipe/image.pipe'; import { ImagePipe } from '@shared/pipe/image.pipe';
import tinycolor from 'tinycolor2';
export const createColorMarkerURI = (color: tinycolor.Instance): string => {
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="-191.35 -351.18 1083.58 1730.46">` +
`<path fill-rule="evenodd" clip-rule="evenodd" fill="#${color.toHex()}" stroke="#000" stroke-width="37" ` +
`stroke-miterlimit="10" d="M351.833 1360.78c-38.766-190.3-107.116-348.665-189.903-495.44C100.523 756.469 ` +
`29.386 655.978-36.434 550.404c-21.972-35.244-40.934-72.477-62.047-109.054-42.216-73.137-76.444-157.935-74.269-267.932 ` +
`2.125-107.473 33.208-193.685 78.03-264.173C-21-206.69 102.481-301.745 268.164-326.724c135.466-20.425 262.475 14.082 ` +
`352.543 66.747 73.6 43.038 130.596 100.528 173.92 168.28 45.22 70.716 76.36 154.26 78.971 263.233 1.337 55.83-7.805 ` +
`107.532-20.684 150.417-13.034 43.41-33.996 79.695-52.646 118.455-36.406 75.659-82.049 144.981-127.855 214.345-136.437 ` +
`206.606-264.496 417.31-320.58 706.028z"/><circle fill-rule="evenodd" ` +
`clip-rule="evenodd" cx="352.891" cy="225.779" r="183.332"/></svg>`;
return 'data:image/svg+xml;base64,' + btoa(svg);
}
export enum MapType { export enum MapType {
geoMap = 'geoMap', geoMap = 'geoMap',
@ -200,7 +214,12 @@ export const defaultBaseMarkersDataLayerSettings: Partial<MarkersDataLayerSettin
markerType: MarkerType.default, markerType: MarkerType.default,
markerColor: { markerColor: {
type: DataLayerColorType.constant, type: DataLayerColorType.constant,
color: '#FE7569' color: '#FE7569',
},
markerImage: {
type: MarkerImageType.image,
image: createColorMarkerURI(tinycolor('#FE7569')),
imageSize: 34
}, },
markerOffsetX: 0.5, markerOffsetX: 0.5,
markerOffsetY: 1 markerOffsetY: 1

58
ui-ngx/src/app/modules/home/components/widget/lib/maps/map.scss

@ -30,23 +30,67 @@
} }
.tb-map { .tb-map {
flex: 1; flex: 1;
&.leaflet-touch {
.leaflet-bar {
border: none;
border-radius: 15px;
box-shadow: 4px 4px 4px 0 rgba(0, 0, 0, 0.15);
background: #fff;
position: relative;
&:before {
content: "";
position: absolute;
inset: 0;
border-radius: 15px;
background: $tb-primary-color; // primary color
opacity: 0.05;
}
a {
color: rgba(0, 0, 0, 0.54);
border-bottom: none;
position: relative;
background: transparent;
&:hover {
border-bottom: none;
color: $tb-primary-color; // primary color
&:before {
content: "";
position: absolute;
inset: 0;
border-radius: 50%;
background-color: rgba(0, 105, 92, 0.10);
}
}
&:first-child {
border-top-left-radius: 50%;
border-top-right-radius: 50%;
}
&:last-child {
border-bottom-left-radius: 50%;
border-bottom-right-radius: 50%;
}
}
}
}
.leaflet-control { .leaflet-control {
.tb-control-button { .tb-control-button {
&.active { &.active, &:hover {
box-shadow: inset 0 -1px 5px 2px #514d4d4f; > div {
background: $tb-primary-color; // primary color
}
} }
> div { > div {
width: 30px; width: 30px;
height: 30px; height: 30px;
color: #5B5B5B; background: rgba(0, 0, 0, 0.54);
line-height: 30px; line-height: 30px;
background-repeat: no-repeat; mask-repeat: no-repeat;
background-position: center; mask-position: center;
&.tb-layers { &.tb-layers {
background-image: url('data:image/svg+xml,<svg width="20" height="20" viewBox="0 0 18 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9 9.5L0.75 5L9 0.5L17.25 5L9 9.5ZM9 12.5L1.18125 8.24375L2.75625 7.38125L9 10.7938L15.2438 7.38125L16.8188 8.24375L9 12.5ZM9 15.5L1.18125 11.2438L2.75625 10.3813L9 13.7938L15.2438 10.3813L16.8188 11.2438L9 15.5Z" fill="%235B5B5B"/></svg>'); mask-image: url('data:image/svg+xml,<svg width="20" height="20" viewBox="0 0 18 16" xmlns="http://www.w3.org/2000/svg"><path d="M9 9.5L0.75 5L9 0.5L17.25 5L9 9.5ZM9 12.5L1.18125 8.24375L2.75625 7.38125L9 10.7938L15.2438 7.38125L16.8188 8.24375L9 12.5ZM9 15.5L1.18125 11.2438L2.75625 10.3813L9 13.7938L15.2438 10.3813L16.8188 11.2438L9 15.5Z"/></svg>');
} }
&.tb-groups { &.tb-groups {
background-image: url('data:image/svg+xml,<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M5 3A2 2 0 0 0 3 5H5M7 3V5H9V3M11 3V5H13V3M15 3V5H17V3M19 3V5H21A2 2 0 0 0 19 3M3 7V9H5V7M7 7V11H11V7M13 7V11H17V7M19 7V9H21V7M3 11V13H5V11M19 11V13H21V11M7 13V17H11V13M13 13V17H17V13M3 15V17H5V15M19 15V17H21V15M3 19A2 2 0 0 0 5 21V19M7 19V21H9V19M11 19V21H13V19M15 19V21H17V19M19 19V21A2 2 0 0 0 21 19Z" fill="%235B5B5B"/></svg>'); mask-image: url('data:image/svg+xml,<svg width="20" height="20" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg"><path d="M6 13.5C5.5875 13.5 5.2345 13.3533 4.941 13.0597C4.6475 12.7662 4.5005 12.413 4.5 12V3C4.5 2.5875 4.647 2.2345 4.941 1.941C5.235 1.6475 5.588 1.5005 6 1.5H15C15.4125 1.5 15.7657 1.647 16.0597 1.941C16.3538 2.235 16.5005 2.588 16.5 3V12C16.5 12.4125 16.3533 12.7657 16.0597 13.0597C15.7662 13.3538 15.413 13.5005 15 13.5H6ZM6 4.5H15V3H6V4.5ZM3 16.5C2.5875 16.5 2.2345 16.3533 1.941 16.0597C1.6475 15.7662 1.5005 15.413 1.5 15V4.5H3V15H13.5V16.5H3Z"/></svg>');
} }
} }
} }

52
ui-ngx/src/app/modules/home/components/widget/lib/maps/map.ts

@ -50,6 +50,7 @@ import { IWidgetSubscription, WidgetSubscriptionOptions } from '@core/api/widget
import { widgetType } from '@shared/models/widget.models'; import { widgetType } from '@shared/models/widget.models';
import { EntityDataPageLink } from '@shared/models/query/query.models'; import { EntityDataPageLink } from '@shared/models/query/query.models';
import { CustomTranslatePipe } from '@shared/pipe/custom-translate.pipe'; import { CustomTranslatePipe } from '@shared/pipe/custom-translate.pipe';
import ITooltipsterInstance = JQueryTooltipster.ITooltipsterInstance;
export abstract class TbMap<S extends BaseMapSettings> { export abstract class TbMap<S extends BaseMapSettings> {
@ -81,6 +82,8 @@ export abstract class TbMap<S extends BaseMapSettings> {
private readonly mapResize$: ResizeObserver; private readonly mapResize$: ResizeObserver;
private tooltipInstances: ITooltipsterInstance[] = [];
protected constructor(protected ctx: WidgetContext, protected constructor(protected ctx: WidgetContext,
protected inputSettings: DeepPartial<S>, protected inputSettings: DeepPartial<S>,
protected containerElement: HTMLElement) { protected containerElement: HTMLElement) {
@ -133,6 +136,7 @@ export abstract class TbMap<S extends BaseMapSettings> {
this.map.panTo(this.defaultCenterPosition); this.map.panTo(this.defaultCenterPosition);
} }
this.setupDataLayers(); this.setupDataLayers();
this.createdControlButtonTooltip();
} }
private setupDataLayers() { private setupDataLayers() {
@ -229,6 +233,51 @@ export abstract class TbMap<S extends BaseMapSettings> {
} }
} }
private createdControlButtonTooltip() {
import('tooltipster').then(() => {
if ($.tooltipster) {
this.tooltipInstances.forEach((instance) => {
instance.destroy();
});
this.tooltipInstances = [];
}
$(this.mapElement)
.find('a[role="button"]:not(.leaflet-pm-action)')
.each((_index, element) => {
let title: string;
if (element.title) {
title = element.title;
$(element).removeAttr('title');
} else if (element.parentElement.title) {
title = element.parentElement.title;
$(element).parent().removeAttr('title');
}
const tooltip = $(element).tooltipster(
{
content: title,
theme: 'tooltipster-shadow',
delay: 10,
triggerClose: {
click: true,
tap: true,
scroll: true,
mouseleave: true
},
side: ['topleft', 'bottomleft'].includes(this.settings.controlsPosition) ? 'right' : 'left',
distance: 2,
trackOrigin: true,
functionBefore: (instance, helper) => {
if (helper.origin.ariaDisabled === 'true' || helper.origin.parentElement.classList.contains('active')) {
return false;
}
},
}
);
this.tooltipInstances.push(tooltip.tooltipster('instance'));
});
});
}
private update(subscription: IWidgetSubscription) { private update(subscription: IWidgetSubscription) {
const dsData = formattedDataFormDatasourceData<TbMapDatasource>(subscription.data, const dsData = formattedDataFormDatasourceData<TbMapDatasource>(subscription.data,
undefined, undefined, el => el.datasource.entityId + el.datasource.mapDataIds[0]); undefined, undefined, el => el.datasource.entityId + el.datasource.mapDataIds[0]);
@ -328,6 +377,9 @@ export abstract class TbMap<S extends BaseMapSettings> {
if (this.map) { if (this.map) {
this.map.remove(); this.map.remove();
} }
this.tooltipInstances.forEach((instance) => {
instance.destroy();
});
} }
public abstract positionToLatLng(position: {x: number; y: number}): L.LatLng; public abstract positionToLatLng(position: {x: number; y: number}): L.LatLng;

69
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/data-layer-color-settings-panel.component.html

@ -0,0 +1,69 @@
<!--
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.
-->
<div class="tb-data-layer-color-settings-panel" [formGroup]="colorSettingsFormGroup">
<div class="tb-data-layer-color-settings-title" translate>widgets.maps.data-layer.color-settings</div>
<div class="flex flex-row">
<tb-toggle-select formControlName="type" class="flex-1">
<tb-toggle-option [value]="DataLayerColorType.constant">
{{ 'widgets.maps.data-layer.color-type-constant' | translate }}
</tb-toggle-option>
<tb-toggle-option [value]="DataLayerColorType.function">
{{ 'widgets.maps.data-layer.color-type-function' | translate }}
</tb-toggle-option>
</tb-toggle-select>
</div>
<div class="tb-form-row space-between">
<div translate>widgets.maps.data-layer.color</div>
<tb-color-input asBoxInput
formControlName="color">
</tb-color-input>
</div>
<div class="tb-data-layer-color-settings-panel-body" [class.!hidden]="colorSettingsFormGroup.get('type').value !== DataLayerColorType.constant">
</div>
<div class="tb-data-layer-color-settings-panel-body" [class.!hidden]="colorSettingsFormGroup.get('type').value !== DataLayerColorType.function">
<ng-container *ngTemplateOutlet="function"></ng-container>
</div>
<div class="tb-data-layer-color-settings-panel-buttons">
<span class="flex-1"></span>
<button mat-button
color="primary"
type="button"
(click)="cancel()">
{{ 'action.cancel' | translate }}
</button>
<button mat-raised-button
color="primary"
type="button"
(click)="applyColorSettings()"
[disabled]="colorSettingsFormGroup.invalid || !colorSettingsFormGroup.dirty">
{{ 'action.apply' | translate }}
</button>
</div>
</div>
<ng-template #function>
<div class="tb-form-panel no-padding no-border" [formGroup]="colorSettingsFormGroup">
<tb-js-func formControlName="colorFunction"
withModules
[functionArgs]="['data', 'dsData']"
[globalVariables]="functionScopeVariables"
functionTitle="{{ 'widgets.maps.data-layer.color-function' | translate }}"
helpId="widget/lib/map/color_fn">
</tb-js-func>
</div>
</ng-template>

53
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/data-layer-color-settings-panel.component.scss

@ -0,0 +1,53 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@import '../../../../../../../../../scss/constants';
.tb-data-layer-color-settings-panel {
width: 700px;
max-width: 90vw;
min-height: 300px;
max-height: 90vh;
display: flex;
flex-direction: column;
gap: 16px;
@media #{$mat-xs} {
width: 90vw;
}
.tb-data-layer-color-settings-title {
font-size: 16px;
font-weight: 500;
line-height: 24px;
letter-spacing: 0.25px;
color: rgba(0, 0, 0, 0.87);
}
.tb-form-row {
height: auto;
}
.tb-data-layer-color-settings-panel-body {
flex: 1;
display: flex;
flex-direction: column;
min-height: 0;
}
.tb-data-layer-color-settings-panel-buttons {
height: 40px;
display: flex;
flex-direction: row;
gap: 16px;
justify-content: flex-end;
align-items: flex-end;
}
}

82
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/data-layer-color-settings-panel.component.ts

@ -0,0 +1,82 @@
///
/// 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 { Component, DestroyRef, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core';
import { PageComponent } from '@shared/components/page.component';
import { TbPopoverComponent } from '@shared/components/popover.component';
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { WidgetService } from '@core/http/widget.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { DataLayerColorSettings, DataLayerColorType } from '@home/components/widget/lib/maps/map.models';
@Component({
selector: 'tb-data-layer-color-settings-panel',
templateUrl: './data-layer-color-settings-panel.component.html',
providers: [],
styleUrls: ['./data-layer-color-settings-panel.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class DataLayerColorSettingsPanelComponent extends PageComponent implements OnInit {
@Input()
colorSettings: DataLayerColorSettings;
@Input()
popover: TbPopoverComponent<DataLayerColorSettingsPanelComponent>;
@Output()
colorSettingsApplied = new EventEmitter<DataLayerColorSettings>();
DataLayerColorType = DataLayerColorType;
colorSettingsFormGroup: UntypedFormGroup;
functionScopeVariables = this.widgetService.getWidgetScopeVariables();
constructor(private fb: UntypedFormBuilder,
private widgetService: WidgetService,
protected store: Store<AppState>,
private destroyRef: DestroyRef) {
super(store);
}
ngOnInit(): void {
this.colorSettingsFormGroup = this.fb.group(
{
type: [this.colorSettings?.type || DataLayerColorType.constant, []],
color: [this.colorSettings?.color, []],
colorFunction: [this.colorSettings?.colorFunction, []]
}
);
this.colorSettingsFormGroup.get('type').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
setTimeout(() => {this.popover?.updatePosition();}, 0);
});
}
cancel() {
this.popover?.hide();
}
applyColorSettings() {
const colorSettings: DataLayerColorSettings = this.colorSettingsFormGroup.value;
this.colorSettingsApplied.emit(colorSettings);
}
}

30
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/data-layer-color-settings.component.html

@ -0,0 +1,30 @@
<!--
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.
-->
<button type="button"
mat-stroked-button
class="tb-box-button"
[disabled]="disabled"
#matButton
(click)="openColorSettingsPopup($event, matButton)">
<tb-icon matButtonIcon *ngIf="modelValue?.type === DataLayerColorType.function; else colorPreview">mdi:function-variant</tb-icon>
</button>
<ng-template #colorPreview>
<div class="tb-color-preview box" [class.disabled]="disabled">
<div class="tb-color-result" [style]="colorStyle"></div>
</div>
</ng-template>

113
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/data-layer-color-settings.component.ts

@ -0,0 +1,113 @@
///
/// 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 { Component, forwardRef, Input, Renderer2, ViewContainerRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { ComponentStyle } from '@shared/models/widget-settings.models';
import { MatButton } from '@angular/material/button';
import { TbPopoverService } from '@shared/components/popover.service';
import { DataLayerColorSettings, DataLayerColorType } from '@home/components/widget/lib/maps/map.models';
import {
DataLayerColorSettingsPanelComponent
} from '@home/components/widget/lib/settings/common/map/data-layer-color-settings-panel.component';
@Component({
selector: 'tb-data-layer-color-settings',
templateUrl: './data-layer-color-settings.component.html',
styleUrls: [],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DataLayerColorSettingsComponent),
multi: true
}
]
})
export class DataLayerColorSettingsComponent implements ControlValueAccessor {
@Input()
disabled: boolean;
DataLayerColorType = DataLayerColorType;
modelValue: DataLayerColorSettings;
colorStyle: ComponentStyle = {};
private propagateChange: (v: any) => void = () => { };
constructor(private popoverService: TbPopoverService,
private renderer: Renderer2,
private viewContainerRef: ViewContainerRef) {}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void {
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
this.updateColorStyle();
}
writeValue(value: DataLayerColorSettings): void {
if (value) {
this.modelValue = value;
this.updateColorStyle();
}
}
openColorSettingsPopup($event: Event, matButton: MatButton) {
if ($event) {
$event.stopPropagation();
}
const trigger = matButton._elementRef.nativeElement;
if (this.popoverService.hasPopover(trigger)) {
this.popoverService.hidePopover(trigger);
} else {
const ctx: any = {
colorSettings: this.modelValue,
};
const colorSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer,
this.viewContainerRef, DataLayerColorSettingsPanelComponent, 'left', true, null,
ctx,
{},
{}, {}, true);
colorSettingsPanelPopover.tbComponentRef.instance.popover = colorSettingsPanelPopover;
colorSettingsPanelPopover.tbComponentRef.instance.colorSettingsApplied.subscribe((colorSettings) => {
colorSettingsPanelPopover.hide();
this.modelValue = colorSettings;
this.updateColorStyle();
this.propagateChange(this.modelValue);
});
}
}
private updateColorStyle() {
if (!this.disabled && this.modelValue) {
if (this.modelValue.type === DataLayerColorType.constant) {
this.colorStyle = {backgroundColor: this.modelValue.color};
} else {
this.colorStyle = {};
}
} else {
this.colorStyle = {};
}
}
}

34
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-dialog.component.html

@ -170,6 +170,40 @@
</div> </div>
<div class="tb-form-panel"> <div class="tb-form-panel">
<div class="tb-form-panel-title">{{ 'widget-config.appearance' | translate }}</div> <div class="tb-form-panel-title">{{ 'widget-config.appearance' | translate }}</div>
<ng-container *ngIf="dataLayerType === 'markers'">
<div class="tb-form-panel stroked">
<div class="flex flex-row items-center justify-between">
<div class="tb-form-panel-title">{{ 'widgets.maps.data-layer.marker.marker-type' | translate }}</div>
<tb-toggle-select formControlName="markerType">
<tb-toggle-option [value]="MarkerType.default">{{ 'widgets.maps.data-layer.marker.marker-type-default' | translate }}</tb-toggle-option>
<tb-toggle-option [value]="MarkerType.image">{{ 'widgets.maps.data-layer.marker.marker-type-image' | translate }}</tb-toggle-option>
</tb-toggle-select>
</div>
<div *ngIf="dataLayerFormGroup.get('markerType').value === MarkerType.default" class="tb-form-row space-between">
<div translate>widgets.maps.data-layer.color</div>
<tb-data-layer-color-settings formControlName="markerColor"></tb-data-layer-color-settings>
</div>
<div *ngIf="dataLayerFormGroup.get('markerType').value === MarkerType.image" class="tb-form-row space-between">
<div translate>widgets.maps.data-layer.marker.image</div>
<tb-marker-image-settings formControlName="markerImage"></tb-marker-image-settings>
</div>
</div>
<div class="tb-form-row space-between column-xs">
<div translate>widgets.maps.data-layer.marker.marker-offset</div>
<div class="flex flex-row items-center justify-start gap-2">
<div class="tb-small-label" translate>widgets.maps.data-layer.marker.offset-horizontal</div>
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic">
<input matInput formControlName="markerOffsetX"
type="number" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field>
<div class="tb-small-label" translate>widgets.maps.data-layer.marker.offset-vertical</div>
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic">
<input matInput formControlName="markerOffsetY"
type="number" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field>
</div>
</div>
</ng-container>
</div> </div>
<div class="tb-form-panel"> <div class="tb-form-panel">
<div class="tb-form-panel-title">{{ 'widgets.maps.data-layer.groups' | translate }}</div> <div class="tb-form-panel-title">{{ 'widgets.maps.data-layer.groups' | translate }}</div>

25
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-dialog.component.ts

@ -22,6 +22,7 @@ import {
MapDataLayerType, MapDataLayerType,
MapType, MapType,
MarkersDataLayerSettings, MarkersDataLayerSettings,
MarkerType,
PolygonsDataLayerSettings PolygonsDataLayerSettings
} from '@home/components/widget/lib/maps/map.models'; } from '@home/components/widget/lib/maps/map.models';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
@ -61,6 +62,8 @@ export class MapDataLayerDialogComponent extends DialogComponent<MapDataLayerDia
widgetType = widgetType; widgetType = widgetType;
MarkerType = MarkerType;
datasourceTypes: Array<DatasourceType> = []; datasourceTypes: Array<DatasourceType> = [];
datasourceTypesTranslations = datasourceTypeTranslationMap; datasourceTypesTranslations = datasourceTypeTranslationMap;
@ -103,7 +106,17 @@ export class MapDataLayerDialogComponent extends DialogComponent<MapDataLayerDia
const markersDataLayer = this.settings as MarkersDataLayerSettings; const markersDataLayer = this.settings as MarkersDataLayerSettings;
this.dialogTitle = 'widgets.maps.data-layer.marker.marker-configuration'; this.dialogTitle = 'widgets.maps.data-layer.marker.marker-configuration';
this.dataLayerFormGroup.addControl('xKey', this.fb.control(markersDataLayer.xKey, Validators.required)); this.dataLayerFormGroup.addControl('xKey', this.fb.control(markersDataLayer.xKey, Validators.required));
this.dataLayerFormGroup.addControl('yKey', this.fb.control(markersDataLayer.yKey, Validators.required)); this.dataLayerFormGroup.addControl('yKey', this.fb.control(markersDataLayer.yKey, Validators.required))
this.dataLayerFormGroup.addControl('markerType', this.fb.control(markersDataLayer.markerType, Validators.required));
this.dataLayerFormGroup.addControl('markerColor', this.fb.control(markersDataLayer.markerColor, Validators.required));
this.dataLayerFormGroup.addControl('markerImage', this.fb.control(markersDataLayer.markerImage, Validators.required));
this.dataLayerFormGroup.addControl('markerOffsetX', this.fb.control(markersDataLayer.markerOffsetX));
this.dataLayerFormGroup.addControl('markerOffsetY', this.fb.control(markersDataLayer.markerOffsetY));
this.dataLayerFormGroup.get('markerType').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() =>
this.updateValidators()
);
break; break;
case 'polygons': case 'polygons':
const polygonsDataLayer = this.settings as PolygonsDataLayerSettings; const polygonsDataLayer = this.settings as PolygonsDataLayerSettings;
@ -179,6 +192,16 @@ export class MapDataLayerDialogComponent extends DialogComponent<MapDataLayerDia
this.dataLayerFormGroup.get('dsDeviceId').disable({emitEvent: false}); this.dataLayerFormGroup.get('dsDeviceId').disable({emitEvent: false});
this.dataLayerFormGroup.get('dsEntityAliasId').enable({emitEvent: false}); this.dataLayerFormGroup.get('dsEntityAliasId').enable({emitEvent: false});
} }
if (this.dataLayerType === 'markers') {
const markerType: MarkerType = this.dataLayerFormGroup.get('markerType').value;
if (markerType === MarkerType.default) {
this.dataLayerFormGroup.get('markerColor').enable({emitEvent: false});
this.dataLayerFormGroup.get('markerImage').disable({emitEvent: false});
} else {
this.dataLayerFormGroup.get('markerColor').disable({emitEvent: false});
this.dataLayerFormGroup.get('markerImage').enable({emitEvent: false});
}
}
} }
editKey(keyType: 'xKey' | 'yKey' | 'polygonKey' | 'circleKey') { editKey(keyType: 'xKey' | 'yKey' | 'polygonKey' | 'circleKey') {

72
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-image-settings-panel.component.html

@ -0,0 +1,72 @@
<!--
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.
-->
<div class="tb-marker-image-settings-panel" [formGroup]="markerImageSettingsFormGroup">
<div class="tb-marker-image-settings-title" translate>widgets.maps.data-layer.marker.marker-image</div>
<div class="flex flex-row">
<tb-toggle-select formControlName="type" class="flex-1">
<tb-toggle-option [value]="MarkerImageType.image">
{{ 'widgets.maps.data-layer.marker.marker-image-type-image' | translate }}
</tb-toggle-option>
<tb-toggle-option [value]="MarkerImageType.function">
{{ 'widgets.maps.data-layer.marker.marker-image-type-function' | translate }}
</tb-toggle-option>
</tb-toggle-select>
</div>
<div class="tb-marker-image-settings-panel-body" [class.!hidden]="markerImageSettingsFormGroup.get('type').value !== MarkerImageType.image">
<div class="tb-form-panel no-padding no-border">
<tb-gallery-image-input required formControlName="image"></tb-gallery-image-input>
<div class="tb-form-row space-between">
<div>{{ 'widgets.maps.data-layer.marker.custom-marker-image-size' | translate }}</div>
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic">
<input matInput formControlName="imageSize" type="number" min="1" step="1" placeholder="{{ 'widget-config.set' | translate }}">
<div matSuffix>px</div>
</mat-form-field>
</div>
</div>
</div>
<div class="tb-marker-image-settings-panel-body" [class.!hidden]="markerImageSettingsFormGroup.get('type').value !== MarkerImageType.function">
<div class="tb-form-panel no-padding no-border">
<tb-js-func formControlName="imageFunction"
withModules
[functionArgs]="['data', 'images', 'dsData']"
[globalVariables]="functionScopeVariables"
functionTitle="{{ 'widgets.maps.data-layer.marker.marker-image-function' | translate }}"
helpId="widget/lib/map/marker_image_fn">
</tb-js-func>
<tb-multiple-gallery-image-input label="{{ 'widgets.maps.data-layer.marker.marker-images' | translate }}"
formControlName="images">
</tb-multiple-gallery-image-input>
</div>
</div>
<div class="tb-marker-image-settings-panel-buttons">
<span class="flex-1"></span>
<button mat-button
color="primary"
type="button"
(click)="cancel()">
{{ 'action.cancel' | translate }}
</button>
<button mat-raised-button
color="primary"
type="button"
(click)="applyMarkerImageSettings()"
[disabled]="markerImageSettingsFormGroup.invalid || !markerImageSettingsFormGroup.dirty">
{{ 'action.apply' | translate }}
</button>
</div>
</div>

53
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-image-settings-panel.component.scss

@ -0,0 +1,53 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@import '../../../../../../../../../scss/constants';
.tb-marker-image-settings-panel {
width: 700px;
max-width: 90vw;
min-height: 300px;
max-height: 90vh;
display: flex;
flex-direction: column;
gap: 16px;
@media #{$mat-xs} {
width: 90vw;
}
.tb-marker-image-settings-title {
font-size: 16px;
font-weight: 500;
line-height: 24px;
letter-spacing: 0.25px;
color: rgba(0, 0, 0, 0.87);
}
.tb-form-row {
height: auto;
}
.tb-marker-image-settings-panel-body {
flex: 1;
display: flex;
flex-direction: column;
min-height: 0;
}
.tb-marker-image-settings-panel-buttons {
height: 40px;
display: flex;
flex-direction: row;
gap: 16px;
justify-content: flex-end;
align-items: flex-end;
}
}

101
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-image-settings-panel.component.ts

@ -0,0 +1,101 @@
///
/// 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 { Component, DestroyRef, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core';
import { PageComponent } from '@shared/components/page.component';
import { TbPopoverComponent } from '@shared/components/popover.component';
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { WidgetService } from '@core/http/widget.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MarkerImageSettings, MarkerImageType } from '@home/components/widget/lib/maps/map.models';
@Component({
selector: 'tb-marker-image-settings-panel',
templateUrl: './marker-image-settings-panel.component.html',
providers: [],
styleUrls: ['./marker-image-settings-panel.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class MarkerImageSettingsPanelComponent extends PageComponent implements OnInit {
@Input()
markerImageSettings: MarkerImageSettings;
@Input()
popover: TbPopoverComponent<MarkerImageSettingsPanelComponent>;
@Output()
markerImageSettingsApplied = new EventEmitter<MarkerImageSettings>();
MarkerImageType = MarkerImageType;
markerImageSettingsFormGroup: UntypedFormGroup;
functionScopeVariables = this.widgetService.getWidgetScopeVariables();
constructor(private fb: UntypedFormBuilder,
private widgetService: WidgetService,
protected store: Store<AppState>,
private destroyRef: DestroyRef) {
super(store);
}
ngOnInit(): void {
this.markerImageSettingsFormGroup = this.fb.group(
{
type: [this.markerImageSettings?.type || MarkerImageType.image, []],
image: [this.markerImageSettings?.image, [Validators.required]],
imageSize: [this.markerImageSettings?.imageSize, [Validators.min(1)]],
imageFunction: [this.markerImageSettings?.imageFunction, [Validators.required]],
images: [this.markerImageSettings?.images, []]
}
);
this.markerImageSettingsFormGroup.get('type').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateValidators();
setTimeout(() => {this.popover?.updatePosition();}, 0);
});
this.updateValidators();
}
cancel() {
this.popover?.hide();
}
applyMarkerImageSettings() {
const markerImageSettings: MarkerImageSettings = this.markerImageSettingsFormGroup.value;
this.markerImageSettingsApplied.emit(markerImageSettings);
}
private updateValidators() {
const type: MarkerImageType = this.markerImageSettingsFormGroup.get('type').value;
if (type === MarkerImageType.image) {
this.markerImageSettingsFormGroup.get('image').enable({emitEvent: false});
this.markerImageSettingsFormGroup.get('imageSize').enable({emitEvent: false});
this.markerImageSettingsFormGroup.get('imageFunction').disable({emitEvent: false});
this.markerImageSettingsFormGroup.get('images').disable({emitEvent: false});
} else {
this.markerImageSettingsFormGroup.get('image').disable({emitEvent: false});
this.markerImageSettingsFormGroup.get('imageSize').disable({emitEvent: false});
this.markerImageSettingsFormGroup.get('imageFunction').enable({emitEvent: false});
this.markerImageSettingsFormGroup.get('images').enable({emitEvent: false});
}
}
}

28
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-image-settings.component.html

@ -0,0 +1,28 @@
<!--
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.
-->
<button type="button"
mat-stroked-button
class="tb-box-button"
[disabled]="disabled"
#matButton
(click)="openImageSettingsPopup($event, matButton)">
<tb-icon matButtonIcon *ngIf="modelValue?.type === MarkerImageType.function; else imagePreview">mdi:function-variant</tb-icon>
</button>
<ng-template #imagePreview>
<img width="24px" height="24px" style="object-fit: contain;" [src]="modelValue?.image | image : {preview: true} | async" [class.disabled]="disabled"/>
</ng-template>

96
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-image-settings.component.ts

@ -0,0 +1,96 @@
///
/// 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 { ChangeDetectorRef, Component, forwardRef, Input, Renderer2, ViewContainerRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { MatButton } from '@angular/material/button';
import { TbPopoverService } from '@shared/components/popover.service';
import { MarkerImageSettings, MarkerImageType } from '@home/components/widget/lib/maps/map.models';
import {
MarkerImageSettingsPanelComponent
} from '@home/components/widget/lib/settings/common/map/marker-image-settings-panel.component';
@Component({
selector: 'tb-marker-image-settings',
templateUrl: './marker-image-settings.component.html',
styleUrls: [],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => MarkerImageSettingsComponent),
multi: true
}
]
})
export class MarkerImageSettingsComponent implements ControlValueAccessor {
@Input()
disabled: boolean;
MarkerImageType = MarkerImageType;
modelValue: MarkerImageSettings;
private propagateChange: (v: any) => void = () => { };
constructor(private popoverService: TbPopoverService,
private renderer: Renderer2,
private cd: ChangeDetectorRef,
private viewContainerRef: ViewContainerRef) {}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void {
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
}
writeValue(value: MarkerImageSettings): void {
if (value) {
this.modelValue = value;
}
}
openImageSettingsPopup($event: Event, matButton: MatButton) {
if ($event) {
$event.stopPropagation();
}
const trigger = matButton._elementRef.nativeElement;
if (this.popoverService.hasPopover(trigger)) {
this.popoverService.hidePopover(trigger);
} else {
const ctx: any = {
markerImageSettings: this.modelValue,
};
const markerImageSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer,
this.viewContainerRef, MarkerImageSettingsPanelComponent, 'left', true, null,
ctx,
{},
{}, {}, true);
markerImageSettingsPanelPopover.tbComponentRef.instance.popover = markerImageSettingsPanelPopover;
markerImageSettingsPanelPopover.tbComponentRef.instance.markerImageSettingsApplied.subscribe((markerImageSettings) => {
markerImageSettingsPanelPopover.hide();
this.modelValue = markerImageSettings;
this.propagateChange(this.modelValue);
this.cd.detectChanges();
});
}
}
}

16
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts

@ -204,6 +204,18 @@ import {
} from '@home/components/widget/lib/settings/common/key/data-key-config-dialog.component'; } from '@home/components/widget/lib/settings/common/key/data-key-config-dialog.component';
import { DataKeyConfigComponent } from '@home/components/widget/lib/settings/common/key/data-key-config.component'; import { DataKeyConfigComponent } from '@home/components/widget/lib/settings/common/key/data-key-config.component';
import { WidgetSettingsComponent } from '@home/components/widget/lib/settings/common/widget/widget-settings.component'; import { WidgetSettingsComponent } from '@home/components/widget/lib/settings/common/widget/widget-settings.component';
import {
DataLayerColorSettingsComponent
} from '@home/components/widget/lib/settings/common/map/data-layer-color-settings.component';
import {
DataLayerColorSettingsPanelComponent
} from '@home/components/widget/lib/settings/common/map/data-layer-color-settings-panel.component';
import {
MarkerImageSettingsComponent
} from '@home/components/widget/lib/settings/common/map/marker-image-settings.component';
import {
MarkerImageSettingsPanelComponent
} from '@home/components/widget/lib/settings/common/map/marker-image-settings-panel.component';
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -277,6 +289,10 @@ import { WidgetSettingsComponent } from '@home/components/widget/lib/settings/co
MapLayerSettingsPanelComponent, MapLayerSettingsPanelComponent,
MapLayerRowComponent, MapLayerRowComponent,
MapLayersComponent, MapLayersComponent,
DataLayerColorSettingsComponent,
DataLayerColorSettingsPanelComponent,
MarkerImageSettingsComponent,
MarkerImageSettingsPanelComponent,
MapDataLayerDialogComponent, MapDataLayerDialogComponent,
MapDataLayerRowComponent, MapDataLayerRowComponent,
MapDataLayersComponent, MapDataLayersComponent,

20
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -6875,6 +6875,11 @@
"source": "Source", "source": "Source",
"additional-data-keys": "Additional data keys", "additional-data-keys": "Additional data keys",
"groups": "Groups", "groups": "Groups",
"color": "Color",
"color-settings": "Color settings",
"color-type-constant": "Constant",
"color-type-function": "Function",
"color-function": "Color function",
"marker": { "marker": {
"latitude-key": "Latitude key", "latitude-key": "Latitude key",
"longitude-key": "Longitude key", "longitude-key": "Longitude key",
@ -6887,7 +6892,20 @@
"no-markers": "No markers configured", "no-markers": "No markers configured",
"add-marker": "Add marker", "add-marker": "Add marker",
"marker-configuration": "Marker configuration", "marker-configuration": "Marker configuration",
"remove-marker": "Remove marker" "remove-marker": "Remove marker",
"marker-type": "Marker type",
"marker-type-default": "Default",
"marker-type-image": "Image",
"image": "Image",
"marker-image": "Marker image",
"marker-image-type-image": "Constant",
"marker-image-type-function": "Function",
"custom-marker-image-size": "Custom marker image size",
"marker-image-function": "Function",
"marker-images": "Marker images",
"marker-offset": "Marker offset",
"offset-horizontal": "Horizontal",
"offset-vertical": "Vertical"
}, },
"polygon": { "polygon": {
"polygon-key": "Polygon key", "polygon-key": "Polygon key",

Loading…
Cancel
Save