UI: Maps - add color range type to color settings.
This commit is contained in:
parent
480e6a4935
commit
2d86e816f2
@ -15,15 +15,24 @@
|
||||
///
|
||||
|
||||
import {
|
||||
DataLayerColorSettings, DataLayerColorType,
|
||||
DataLayerPatternSettings, DataLayerPatternType,
|
||||
MapDataLayerSettings, MapDataLayerType, mapDataSourceSettingsToDatasource,
|
||||
MapStringFunction, MapType,
|
||||
DataLayerColorSettings,
|
||||
DataLayerColorType,
|
||||
DataLayerPatternSettings,
|
||||
DataLayerPatternType,
|
||||
MapDataLayerSettings,
|
||||
MapDataLayerType,
|
||||
mapDataSourceSettingsToDatasource,
|
||||
MapStringFunction,
|
||||
MapType,
|
||||
TbMapDatasource
|
||||
} from '@shared/models/widget/maps/map.models';
|
||||
import {
|
||||
createLabelFromPattern,
|
||||
guid, isDefined,
|
||||
guid,
|
||||
isDefined,
|
||||
isDefinedAndNotNull,
|
||||
isNumber,
|
||||
isNumeric,
|
||||
mergeDeepIgnoreArray,
|
||||
parseTbFunction,
|
||||
safeExecuteTbFunction
|
||||
@ -32,10 +41,11 @@ import L from 'leaflet';
|
||||
import { CompiledTbFunction } from '@shared/models/js-function.models';
|
||||
import { forkJoin, Observable, of } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { FormattedData } from '@shared/models/widget.models';
|
||||
import { DataKey, FormattedData } from '@shared/models/widget.models';
|
||||
import { CustomTranslatePipe } from '@shared/pipe/custom-translate.pipe';
|
||||
import { TbMap } from '@home/components/widget/lib/maps/map';
|
||||
import { WidgetContext } from '@home/models/widget-component.models';
|
||||
import { ColorRange } from '@shared/models/widget-settings.models';
|
||||
|
||||
export class DataLayerPatternProcessor {
|
||||
|
||||
@ -77,22 +87,26 @@ export class DataLayerColorProcessor {
|
||||
|
||||
private colorFunction: CompiledTbFunction<MapStringFunction>;
|
||||
private color: string;
|
||||
private rangeKey: DataKey;
|
||||
private range: ColorRange[];
|
||||
|
||||
constructor(private dataLayer: TbMapDataLayer,
|
||||
private settings: DataLayerColorSettings) {}
|
||||
|
||||
public setup(): Observable<void> {
|
||||
this.color = this.settings.color;
|
||||
if (this.settings.type === DataLayerColorType.function) {
|
||||
if (this.settings.type === DataLayerColorType.range) {
|
||||
this.rangeKey = this.settings.rangeKey;
|
||||
this.range = this.settings.range;
|
||||
} else if (this.settings.type === DataLayerColorType.function) {
|
||||
return parseTbFunction<MapStringFunction>(this.dataLayer.getCtx().http, this.settings.colorFunction, ['data', 'dsData']).pipe(
|
||||
map((parsed) => {
|
||||
this.colorFunction = parsed;
|
||||
return null;
|
||||
})
|
||||
);
|
||||
} else {
|
||||
return of(null)
|
||||
}
|
||||
return of(null)
|
||||
}
|
||||
|
||||
public processColor(data: FormattedData<TbMapDatasource>, dsData: FormattedData<TbMapDatasource>[]): string {
|
||||
@ -102,12 +116,33 @@ export class DataLayerColorProcessor {
|
||||
if (!color) {
|
||||
color = this.color;
|
||||
}
|
||||
} else if (this.settings.type === DataLayerColorType.range) {
|
||||
color = this.color;
|
||||
if (this.rangeKey && this.range?.length) {
|
||||
const value = data[this.rangeKey.label];
|
||||
if (isDefinedAndNotNull(value) && isNumeric(value)) {
|
||||
const num = Number(value);
|
||||
for (const range of this.range) {
|
||||
if (DataLayerColorProcessor.constantRange(range) && range.from === num) {
|
||||
color = range.color;
|
||||
break;
|
||||
} else if ((!isNumber(range.from) || num >= range.from) && (!isNumber(range.to) || num < range.to)) {
|
||||
color = range.color;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
color = this.color;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
static constantRange(range: ColorRange): boolean {
|
||||
return isNumber(range.from) && isNumber(range.to) && range.from === range.to;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export abstract class TbDataLayerItem<S extends MapDataLayerSettings = MapDataLayerSettings, D extends TbMapDataLayer = TbMapDataLayer, L extends L.Layer = L.Layer> {
|
||||
@ -169,6 +204,9 @@ export abstract class TbMapDataLayer<S extends MapDataLayerSettings = MapDataLay
|
||||
public setup(): Observable<any> {
|
||||
this.datasource = mapDataSourceSettingsToDatasource(this.settings);
|
||||
this.datasource.dataKeys = this.settings.additionalDataKeys ? [...this.settings.additionalDataKeys] : [];
|
||||
const colorRangeKeys = this.allColorSettings().filter(settings => settings.type === DataLayerColorType.range && settings.rangeKey)
|
||||
.map(settings => settings.rangeKey);
|
||||
this.datasource.dataKeys.push(...colorRangeKeys);
|
||||
this.mapDataId = this.datasource.mapDataIds[0];
|
||||
this.datasource = this.setupDatasource(this.datasource);
|
||||
return forkJoin(
|
||||
@ -251,6 +289,10 @@ export abstract class TbMapDataLayer<S extends MapDataLayerSettings = MapDataLay
|
||||
return datasource;
|
||||
}
|
||||
|
||||
protected allColorSettings(): DataLayerColorSettings[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
protected onDataLayerEnabled(): void {}
|
||||
|
||||
protected onDataLayerDisabled(): void {}
|
||||
|
||||
@ -17,11 +17,12 @@
|
||||
import {
|
||||
BaseMarkerShapeSettings,
|
||||
ClusterMarkerColorFunction,
|
||||
DataLayerColorSettings,
|
||||
DataLayerColorType,
|
||||
defaultBaseMarkersDataLayerSettings,
|
||||
isValidLatLng,
|
||||
loadImageWithAspect, MapDataLayerType,
|
||||
MapStringFunction,
|
||||
loadImageWithAspect,
|
||||
MapDataLayerType,
|
||||
MapType,
|
||||
MarkerIconInfo,
|
||||
MarkerIconSettings,
|
||||
@ -53,17 +54,19 @@ import { ImagePipe } from '@shared/pipe/image.pipe';
|
||||
import { TbMap } from '@home/components/widget/lib/maps/map';
|
||||
import {
|
||||
createColorMarkerIconElement,
|
||||
createColorMarkerShapeURI, MarkerIconContainer,
|
||||
createColorMarkerShapeURI,
|
||||
MarkerIconContainer,
|
||||
MarkerShape
|
||||
} from '@shared/models/widget/maps/marker-shape.models';
|
||||
import { MatIconRegistry } from '@angular/material/icon';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
import {
|
||||
TbLatestDataLayerItem,
|
||||
TbLatestMapDataLayer, UnplacedMapDataItem
|
||||
TbLatestMapDataLayer,
|
||||
UnplacedMapDataItem
|
||||
} from '@home/components/widget/lib/maps/data-layer/latest-map-data-layer';
|
||||
import { TbImageMap } from '@home/components/widget/lib/maps/image-map';
|
||||
import { TbMapDataLayer } from '@home/components/widget/lib/maps/data-layer/map-data-layer';
|
||||
import { DataLayerColorProcessor, TbMapDataLayer } from '@home/components/widget/lib/maps/data-layer/map-data-layer';
|
||||
|
||||
export class MarkerDataProcessor<S extends MarkersDataLayerSettings = MarkersDataLayerSettings> {
|
||||
|
||||
@ -218,8 +221,7 @@ abstract class MarkerIconProcessor<S> {
|
||||
|
||||
abstract class BaseColorMarkerShapeProcessor<S extends BaseMarkerShapeSettings> extends MarkerIconProcessor<S> {
|
||||
|
||||
private markerColorFunction: CompiledTbFunction<MapStringFunction>;
|
||||
|
||||
private colorProcessor: DataLayerColorProcessor;
|
||||
private defaultMarkerIconInfo: MarkerIconInfo;
|
||||
|
||||
protected constructor(protected dataProcessor: MarkerDataProcessor,
|
||||
@ -229,40 +231,28 @@ abstract class BaseColorMarkerShapeProcessor<S extends BaseMarkerShapeSettings>
|
||||
|
||||
public setup(): Observable<void> {
|
||||
const colorSettings = this.settings.color;
|
||||
if (colorSettings.type === DataLayerColorType.function) {
|
||||
return parseTbFunction<MapStringFunction>(this.dataProcessor.dataLayer.getCtx().http, colorSettings.colorFunction, ['data', 'dsData']).pipe(
|
||||
map((parsed) => {
|
||||
this.markerColorFunction = parsed;
|
||||
return null;
|
||||
})
|
||||
);
|
||||
} else {
|
||||
this.colorProcessor = new DataLayerColorProcessor(this.dataProcessor.dataLayer, colorSettings);
|
||||
const setup$: Observable<void>[] = [this.colorProcessor.setup()];
|
||||
if (colorSettings.type === DataLayerColorType.constant) {
|
||||
const color = tinycolor(colorSettings.color);
|
||||
return this.createMarkerShape(color, 0, this.settings.size).pipe(
|
||||
map((info) => {
|
||||
this.defaultMarkerIconInfo = info;
|
||||
return null;
|
||||
}
|
||||
));
|
||||
setup$.push(
|
||||
this.createMarkerShape(color, 0, this.settings.size).pipe(
|
||||
map((info) => {
|
||||
this.defaultMarkerIconInfo = info;
|
||||
return null;
|
||||
}))
|
||||
);
|
||||
}
|
||||
return forkJoin(setup$).pipe(map(() => null));
|
||||
}
|
||||
|
||||
public createMarkerIcon(data: FormattedData<TbMapDatasource>, dsData: FormattedData<TbMapDatasource>[], rotationAngle = 0): Observable<MarkerIconInfo> {
|
||||
const colorSettings = this.settings.color;
|
||||
let color: tinycolor.Instance;
|
||||
if (colorSettings.type === DataLayerColorType.function) {
|
||||
const functionColor = safeExecuteTbFunction(this.markerColorFunction, [data, dsData]);
|
||||
if (isDefinedAndNotNull(functionColor)) {
|
||||
color = tinycolor(functionColor);
|
||||
} else {
|
||||
color = tinycolor(colorSettings.color);
|
||||
}
|
||||
return this.createMarkerShape(color, rotationAngle, this.settings.size);
|
||||
} else if (rotationAngle === 0) {
|
||||
if (colorSettings.type === DataLayerColorType.constant && rotationAngle === 0) {
|
||||
return of(this.defaultMarkerIconInfo);
|
||||
} else {
|
||||
color = tinycolor(colorSettings.color);
|
||||
return this.createMarkerShape(color, rotationAngle, this.settings.size);
|
||||
const color = this.colorProcessor.processColor(data, dsData);
|
||||
return this.createMarkerShape(tinycolor(color), rotationAngle, this.settings.size);
|
||||
}
|
||||
}
|
||||
|
||||
@ -639,6 +629,15 @@ export class TbMarkersDataLayer extends TbLatestMapDataLayer<MarkersDataLayerSet
|
||||
return datasource;
|
||||
}
|
||||
|
||||
protected allColorSettings(): DataLayerColorSettings[] {
|
||||
if (this.settings.markerType === MarkerType.shape) {
|
||||
return [this.settings.markerShape.color];
|
||||
} else if (this.settings.markerType === MarkerType.icon) {
|
||||
return [this.settings.markerIcon.color];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
protected defaultBaseSettings(map: TbMap<any>): Partial<MarkersDataLayerSettings> {
|
||||
return defaultBaseMarkersDataLayerSettings(map.type());
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
import { ShapeDataLayerSettings, TbMapDatasource } from '@shared/models/widget/maps/map.models';
|
||||
import { DataLayerColorSettings, ShapeDataLayerSettings, TbMapDatasource } from '@shared/models/widget/maps/map.models';
|
||||
import L from 'leaflet';
|
||||
import { TbMap } from '@home/components/widget/lib/maps/map';
|
||||
import { forkJoin, Observable } from 'rxjs';
|
||||
@ -45,6 +45,10 @@ export abstract class TbShapesDataLayer<S extends ShapeDataLayerSettings, L exte
|
||||
};
|
||||
}
|
||||
|
||||
protected allColorSettings(): DataLayerColorSettings[] {
|
||||
return [this.settings.fillColor, this.settings.strokeColor];
|
||||
}
|
||||
|
||||
protected doSetup(): Observable<any> {
|
||||
this.fillColorProcessor = new DataLayerColorProcessor(this, this.settings.fillColor);
|
||||
this.strokeColorProcessor = new DataLayerColorProcessor(this, this.settings.strokeColor);
|
||||
|
||||
@ -16,16 +16,16 @@
|
||||
|
||||
import {
|
||||
calculateInterpolationRatio,
|
||||
calculateLastPoints,
|
||||
calculateLastPoints, DataLayerColorSettings, DataLayerColorType,
|
||||
defaultBaseTripsDataLayerSettings,
|
||||
findRotationAngle,
|
||||
interpolateLineSegment,
|
||||
MapDataLayerType,
|
||||
MapDataLayerType, MarkerType,
|
||||
TbMapDatasource,
|
||||
TripsDataLayerSettings
|
||||
} from '@shared/models/widget/maps/map.models';
|
||||
import { forkJoin, Observable } from 'rxjs';
|
||||
import { FormattedData, WidgetActionType } from '@shared/models/widget.models';
|
||||
import { DataKey, FormattedData, WidgetActionType } from '@shared/models/widget.models';
|
||||
import { map } from 'rxjs/operators';
|
||||
import L from 'leaflet';
|
||||
import { deepClone, isDefined, isUndefined } from '@core/utils';
|
||||
@ -530,9 +530,14 @@ export class TbTripsDataLayer extends TbMapDataLayer<TripsDataLayerSettings, TbT
|
||||
|
||||
protected setupDatasource(datasource: TbMapDatasource): TbMapDatasource {
|
||||
datasource.dataKeys = [this.settings.xKey, this.settings.yKey];
|
||||
const additionalKeys = this.allColorSettings().filter(settings => settings.type === DataLayerColorType.range && settings.rangeKey)
|
||||
.map(settings => settings.rangeKey);
|
||||
if (this.settings.additionalDataKeys?.length) {
|
||||
const tsKeys = this.settings.additionalDataKeys.filter(key => key.type === DataKeyType.timeseries);
|
||||
const latestKeys = this.settings.additionalDataKeys.filter(key => key.type !== DataKeyType.timeseries);
|
||||
additionalKeys.push(...this.settings.additionalDataKeys);
|
||||
}
|
||||
if (additionalKeys.length) {
|
||||
const tsKeys = additionalKeys.filter(key => key.type === DataKeyType.timeseries);
|
||||
const latestKeys = additionalKeys.filter(key => key.type !== DataKeyType.timeseries);
|
||||
datasource.dataKeys.push(...tsKeys);
|
||||
if (latestKeys.length) {
|
||||
datasource.latestDataKeys = latestKeys;
|
||||
@ -541,6 +546,24 @@ export class TbTripsDataLayer extends TbMapDataLayer<TripsDataLayerSettings, TbT
|
||||
return datasource;
|
||||
}
|
||||
|
||||
protected allColorSettings(): DataLayerColorSettings[] {
|
||||
const colorSettings: DataLayerColorSettings[] = [];
|
||||
if (this.settings.showMarker) {
|
||||
if (this.settings.markerType === MarkerType.shape) {
|
||||
colorSettings.push(this.settings.markerShape.color);
|
||||
} else if (this.settings.markerType === MarkerType.icon) {
|
||||
colorSettings.push(this.settings.markerIcon.color);
|
||||
}
|
||||
}
|
||||
if (this.settings.showPath) {
|
||||
colorSettings.push(this.settings.pathStrokeColor);
|
||||
}
|
||||
if (this.settings.showPoints) {
|
||||
colorSettings.push(this.settings.pointColor);
|
||||
}
|
||||
return colorSettings;
|
||||
}
|
||||
|
||||
protected defaultBaseSettings(map: TbMap<any>): Partial<TripsDataLayerSettings> {
|
||||
return defaultBaseTripsDataLayerSettings(map.type());
|
||||
}
|
||||
|
||||
@ -85,6 +85,10 @@ export class ColorRangeListComponent implements OnInit, ControlValueAccessor, On
|
||||
@Input()
|
||||
datasource: Datasource;
|
||||
|
||||
@Input()
|
||||
@coerceBoolean()
|
||||
simpleRange = false;
|
||||
|
||||
@Input()
|
||||
@coerceBoolean()
|
||||
advancedMode = false;
|
||||
@ -133,7 +137,7 @@ export class ColorRangeListComponent implements OnInit, ControlValueAccessor, On
|
||||
writeValue(value: any): void {
|
||||
if (value) {
|
||||
let rangeList: ColorRangeSettings = {};
|
||||
if (isUndefined(value?.advancedMode) && value?.length) {
|
||||
if (this.simpleRange || (isUndefined(value?.advancedMode) && value?.length)) {
|
||||
rangeList.advancedMode = false;
|
||||
rangeList.range = value;
|
||||
} else {
|
||||
@ -229,7 +233,11 @@ export class ColorRangeListComponent implements OnInit, ControlValueAccessor, On
|
||||
}
|
||||
|
||||
updateModel() {
|
||||
this.propagateChange(this.colorRangeListFormGroup.value);
|
||||
if (this.simpleRange) {
|
||||
this.propagateChange(this.colorRangeListFormGroup.get('range').value);
|
||||
} else {
|
||||
this.propagateChange(this.colorRangeListFormGroup.value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -22,6 +22,9 @@
|
||||
<tb-toggle-option [value]="DataLayerColorType.constant">
|
||||
{{ 'widgets.maps.data-layer.color-type-constant' | translate }}
|
||||
</tb-toggle-option>
|
||||
<tb-toggle-option [value]="DataLayerColorType.range">
|
||||
{{ 'widgets.maps.data-layer.color-type-range' | translate }}
|
||||
</tb-toggle-option>
|
||||
<tb-toggle-option [value]="DataLayerColorType.function">
|
||||
{{ 'widgets.maps.data-layer.color-type-function' | translate }}
|
||||
</tb-toggle-option>
|
||||
@ -35,6 +38,35 @@
|
||||
</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.range">
|
||||
<tb-data-key-input
|
||||
inlineField="false"
|
||||
appearance="outline"
|
||||
subscriptSizing="fixed"
|
||||
label="{{ 'widgets.maps.data-layer.color-range-source-key' | translate }}"
|
||||
required
|
||||
requiredText="{{ 'widgets.maps.data-layer.color-range-source-key-required' }}"
|
||||
[datasourceType]="dsType"
|
||||
[entityAliasId]="dsEntityAliasId"
|
||||
[deviceId]="dsDeviceId"
|
||||
[aliasController]="context.aliasController"
|
||||
[widgetType]="widgetType.latest"
|
||||
[dataKeyType]="context.functionsOnly ? DataKeyType.function : null"
|
||||
[dataKeyTypes]="[DataKeyType.attribute, DataKeyType.timeseries]"
|
||||
[callbacks]="context.callbacks"
|
||||
[generateKey]="context.generateDataKey"
|
||||
(keyEdit)="editRangeKey()"
|
||||
formControlName="rangeKey">
|
||||
</tb-data-key-input>
|
||||
<div class="tb-form-panel stroked">
|
||||
<div translate>widgets.maps.data-layer.color-range</div>
|
||||
<tb-color-range-list class="tb-color-ranges-panel"
|
||||
simpleRange
|
||||
formControlName="range"
|
||||
[popover]="popover">
|
||||
</tb-color-range-list>
|
||||
</div>
|
||||
</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>
|
||||
|
||||
@ -16,6 +16,8 @@
|
||||
@import '../../../../../../../../../scss/constants';
|
||||
|
||||
.tb-data-layer-color-settings-panel {
|
||||
--mdc-outlined-text-field-outline-color: rgba(0,0,0,0.12);
|
||||
--mat-form-field-trailing-icon-color: rgba(0,0,0,0.38);
|
||||
width: 700px;
|
||||
max-width: 90vw;
|
||||
min-height: 300px;
|
||||
@ -50,4 +52,14 @@
|
||||
justify-content: flex-end;
|
||||
align-items: flex-end;
|
||||
}
|
||||
.tb-color-ranges-panel {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
gap: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.tb-color-ranges {
|
||||
--mat-icon-color: rgba(0, 0, 0, 0.38);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,12 +17,15 @@
|
||||
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 { 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 { DataLayerColorSettings, DataLayerColorType } from '@shared/models/widget/maps/map.models';
|
||||
import { DataLayerColorSettings, DataLayerColorType, MapType } from '@shared/models/widget/maps/map.models';
|
||||
import { DataKey, DatasourceType, widgetType } from '@shared/models/widget.models';
|
||||
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
|
||||
import { MapSettingsContext } from '@home/components/widget/lib/settings/common/map/map-settings.component.models';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-data-layer-color-settings-panel',
|
||||
@ -33,9 +36,25 @@ import { DataLayerColorSettings, DataLayerColorType } from '@shared/models/widge
|
||||
})
|
||||
export class DataLayerColorSettingsPanelComponent extends PageComponent implements OnInit {
|
||||
|
||||
widgetType = widgetType;
|
||||
|
||||
DataKeyType = DataKeyType;
|
||||
|
||||
@Input()
|
||||
colorSettings: DataLayerColorSettings;
|
||||
|
||||
@Input()
|
||||
context: MapSettingsContext;
|
||||
|
||||
@Input()
|
||||
dsType: DatasourceType;
|
||||
|
||||
@Input()
|
||||
dsEntityAliasId: string;
|
||||
|
||||
@Input()
|
||||
dsDeviceId: string;
|
||||
|
||||
@Input()
|
||||
helpId = 'widget/lib/map/color_fn';
|
||||
|
||||
@ -63,14 +82,18 @@ export class DataLayerColorSettingsPanelComponent extends PageComponent implemen
|
||||
{
|
||||
type: [this.colorSettings?.type || DataLayerColorType.constant, []],
|
||||
color: [this.colorSettings?.color, []],
|
||||
rangeKey: [this.colorSettings?.rangeKey, [Validators.required]],
|
||||
range: [this.colorSettings?.range, []],
|
||||
colorFunction: [this.colorSettings?.colorFunction, []]
|
||||
}
|
||||
);
|
||||
this.colorSettingsFormGroup.get('type').valueChanges.pipe(
|
||||
takeUntilDestroyed(this.destroyRef)
|
||||
).subscribe(() => {
|
||||
this.updateValidators();
|
||||
setTimeout(() => {this.popover?.updatePosition();}, 0);
|
||||
});
|
||||
this.updateValidators();
|
||||
}
|
||||
|
||||
cancel() {
|
||||
@ -82,4 +105,25 @@ export class DataLayerColorSettingsPanelComponent extends PageComponent implemen
|
||||
this.colorSettingsApplied.emit(colorSettings);
|
||||
}
|
||||
|
||||
public editRangeKey() {
|
||||
const targetDataKey: DataKey = this.colorSettingsFormGroup.get('rangeKey').value;
|
||||
this.context.editKey(targetDataKey,
|
||||
this.dsDeviceId, this.dsEntityAliasId, widgetType.latest).subscribe(
|
||||
(updatedDataKey) => {
|
||||
if (updatedDataKey) {
|
||||
this.colorSettingsFormGroup.get('rangeKey').patchValue(updatedDataKey);
|
||||
this.colorSettingsFormGroup.markAsDirty();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private updateValidators() {
|
||||
const type: DataLayerColorType = this.colorSettingsFormGroup.get('type').value;
|
||||
if (type === DataLayerColorType.range) {
|
||||
this.colorSettingsFormGroup.get('rangeKey').enable({emitEvent: false});
|
||||
} else {
|
||||
this.colorSettingsFormGroup.get('rangeKey').disable({emitEvent: false});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,13 +16,15 @@
|
||||
|
||||
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 { ColorType, ComponentStyle } from '@shared/models/widget-settings.models';
|
||||
import { MatButton } from '@angular/material/button';
|
||||
import { TbPopoverService } from '@shared/components/popover.service';
|
||||
import { DataLayerColorSettings, DataLayerColorType } from '@shared/models/widget/maps/map.models';
|
||||
import {
|
||||
DataLayerColorSettingsPanelComponent
|
||||
} from '@home/components/widget/lib/settings/common/map/data-layer-color-settings-panel.component';
|
||||
import { MapSettingsContext } from '@home/components/widget/lib/settings/common/map/map-settings.component.models';
|
||||
import { DatasourceType } from '@shared/models/widget.models';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-data-layer-color-settings',
|
||||
@ -41,6 +43,18 @@ export class DataLayerColorSettingsComponent implements ControlValueAccessor {
|
||||
@Input()
|
||||
disabled: boolean;
|
||||
|
||||
@Input()
|
||||
context: MapSettingsContext;
|
||||
|
||||
@Input()
|
||||
dsType: DatasourceType;
|
||||
|
||||
@Input()
|
||||
dsEntityAliasId: string;
|
||||
|
||||
@Input()
|
||||
dsDeviceId: string;
|
||||
|
||||
@Input()
|
||||
helpId = 'widget/lib/map/color_fn';
|
||||
|
||||
@ -85,6 +99,10 @@ export class DataLayerColorSettingsComponent implements ControlValueAccessor {
|
||||
} else {
|
||||
const ctx: any = {
|
||||
colorSettings: this.modelValue,
|
||||
context: this.context,
|
||||
dsType: this.dsType,
|
||||
dsEntityAliasId: this.dsEntityAliasId,
|
||||
dsDeviceId: this.dsDeviceId,
|
||||
helpId: this.helpId
|
||||
};
|
||||
const colorSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer,
|
||||
@ -103,11 +121,23 @@ export class DataLayerColorSettingsComponent implements ControlValueAccessor {
|
||||
}
|
||||
|
||||
private updateColorStyle() {
|
||||
if (!this.disabled && this.modelValue) {
|
||||
if (this.modelValue.type === DataLayerColorType.constant) {
|
||||
this.colorStyle = {backgroundColor: this.modelValue.color};
|
||||
if (!this.disabled && this.modelValue && this.modelValue.type !== DataLayerColorType.function) {
|
||||
let colors: string[] = [this.modelValue.color];
|
||||
const rangeList = this.modelValue.range;
|
||||
if (this.modelValue.type === DataLayerColorType.range && rangeList?.length) {
|
||||
const rangeColors = rangeList.slice(0, Math.min(2, rangeList.length)).map(r => r.color);
|
||||
colors = colors.concat(rangeColors);
|
||||
}
|
||||
if (colors.length === 1) {
|
||||
this.colorStyle = {backgroundColor: colors[0]};
|
||||
} else {
|
||||
this.colorStyle = {};
|
||||
const gradientValues: string[] = [];
|
||||
const step = 100 / colors.length;
|
||||
for (let i = 0; i < colors.length; i++) {
|
||||
gradientValues.push(`${colors[i]} ${step*i}%`);
|
||||
gradientValues.push(`${colors[i]} ${step*(i+1)}%`);
|
||||
}
|
||||
this.colorStyle = {background: `linear-gradient(90deg, ${gradientValues.join(', ')})`};
|
||||
}
|
||||
} else {
|
||||
this.colorStyle = {};
|
||||
|
||||
@ -200,11 +200,21 @@
|
||||
<ng-template matExpansionPanelContent>
|
||||
<div *ngIf="dataLayerFormGroup.get('markerType').value === MarkerType.shape" class="tb-form-row space-between column-xs">
|
||||
<div translate>widgets.maps.data-layer.marker.shape</div>
|
||||
<tb-marker-shape-settings formControlName="markerShape" [trip]="dataLayerType === 'trips'" [markerType]="MarkerType.shape"></tb-marker-shape-settings>
|
||||
<tb-marker-shape-settings formControlName="markerShape"
|
||||
[context]="context"
|
||||
[dsType]="dataLayerFormGroup.get('dsType').value"
|
||||
[dsEntityAliasId]="dataLayerFormGroup.get('dsEntityAliasId').value"
|
||||
[dsDeviceId]="dataLayerFormGroup.get('dsDeviceId').value"
|
||||
[trip]="dataLayerType === 'trips'" [markerType]="MarkerType.shape"></tb-marker-shape-settings>
|
||||
</div>
|
||||
<div *ngIf="dataLayerFormGroup.get('markerType').value === MarkerType.icon" class="tb-form-row space-between column-xs">
|
||||
<div translate>widgets.maps.data-layer.marker.icon</div>
|
||||
<tb-marker-shape-settings formControlName="markerIcon" [trip]="dataLayerType === 'trips'" [markerType]="MarkerType.icon"></tb-marker-shape-settings>
|
||||
<tb-marker-shape-settings formControlName="markerIcon"
|
||||
[context]="context"
|
||||
[dsType]="dataLayerFormGroup.get('dsType').value"
|
||||
[dsEntityAliasId]="dataLayerFormGroup.get('dsEntityAliasId').value"
|
||||
[dsDeviceId]="dataLayerFormGroup.get('dsDeviceId').value"
|
||||
[trip]="dataLayerType === 'trips'" [markerType]="MarkerType.icon"></tb-marker-shape-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>
|
||||
@ -275,7 +285,12 @@
|
||||
<input matInput type="number" min="0" formControlName="pathStrokeWeight" placeholder="{{ 'widget-config.set' | translate }}">
|
||||
<span matSuffix>px</span>
|
||||
</mat-form-field>
|
||||
<tb-data-layer-color-settings helpId="widget/lib/map/path_color_fn" formControlName="pathStrokeColor"></tb-data-layer-color-settings>
|
||||
<tb-data-layer-color-settings
|
||||
[context]="context"
|
||||
[dsType]="dataLayerFormGroup.get('dsType').value"
|
||||
[dsEntityAliasId]="dataLayerFormGroup.get('dsEntityAliasId').value"
|
||||
[dsDeviceId]="dataLayerFormGroup.get('dsDeviceId').value"
|
||||
helpId="widget/lib/map/path_color_fn" formControlName="pathStrokeColor"></tb-data-layer-color-settings>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tb-form-panel tb-slide-toggle stroked">
|
||||
@ -361,7 +376,12 @@
|
||||
<input matInput type="number" min="0" formControlName="pointSize" placeholder="{{ 'widget-config.set' | translate }}">
|
||||
<span matSuffix>px</span>
|
||||
</mat-form-field>
|
||||
<tb-data-layer-color-settings helpId="widget/lib/map/path_point_color_fn" formControlName="pointColor"></tb-data-layer-color-settings>
|
||||
<tb-data-layer-color-settings
|
||||
[context]="context"
|
||||
[dsType]="dataLayerFormGroup.get('dsType').value"
|
||||
[dsEntityAliasId]="dataLayerFormGroup.get('dsEntityAliasId').value"
|
||||
[dsDeviceId]="dataLayerFormGroup.get('dsDeviceId').value"
|
||||
helpId="widget/lib/map/path_point_color_fn" formControlName="pointColor"></tb-data-layer-color-settings>
|
||||
</div>
|
||||
</div>
|
||||
<tb-data-layer-pattern-settings
|
||||
@ -379,7 +399,12 @@
|
||||
<ng-container *ngIf="['polygons', 'circles'].includes(dataLayerType)">
|
||||
<div class="tb-form-row space-between">
|
||||
<div translate>widgets.maps.data-layer.fill-color</div>
|
||||
<tb-data-layer-color-settings helpId="{{ dataLayerType === 'polygons' ? 'widget/lib/map/polygon_fill_color_fn' : 'widget/lib/map/circle_fill_color_fn' }}" formControlName="fillColor"></tb-data-layer-color-settings>
|
||||
<tb-data-layer-color-settings
|
||||
[context]="context"
|
||||
[dsType]="dataLayerFormGroup.get('dsType').value"
|
||||
[dsEntityAliasId]="dataLayerFormGroup.get('dsEntityAliasId').value"
|
||||
[dsDeviceId]="dataLayerFormGroup.get('dsDeviceId').value"
|
||||
helpId="{{ dataLayerType === 'polygons' ? 'widget/lib/map/polygon_fill_color_fn' : 'widget/lib/map/circle_fill_color_fn' }}" formControlName="fillColor"></tb-data-layer-color-settings>
|
||||
</div>
|
||||
<div class="tb-form-row space-between">
|
||||
<div translate>widgets.maps.data-layer.stroke</div>
|
||||
@ -388,7 +413,12 @@
|
||||
<input matInput type="number" min="0" formControlName="strokeWeight" placeholder="{{ 'widget-config.set' | translate }}">
|
||||
<span matSuffix>px</span>
|
||||
</mat-form-field>
|
||||
<tb-data-layer-color-settings helpId="{{ dataLayerType === 'polygons' ? 'widget/lib/map/polygon_stroke_color_fn' : 'widget/lib/map/circle_stroke_color_fn' }}" formControlName="strokeColor"></tb-data-layer-color-settings>
|
||||
<tb-data-layer-color-settings
|
||||
[context]="context"
|
||||
[dsType]="dataLayerFormGroup.get('dsType').value"
|
||||
[dsEntityAliasId]="dataLayerFormGroup.get('dsEntityAliasId').value"
|
||||
[dsDeviceId]="dataLayerFormGroup.get('dsDeviceId').value"
|
||||
helpId="{{ dataLayerType === 'polygons' ? 'widget/lib/map/polygon_stroke_color_fn' : 'widget/lib/map/circle_stroke_color_fn' }}" formControlName="strokeColor"></tb-data-layer-color-settings>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
@ -30,5 +30,9 @@
|
||||
<div *ngIf="markerType === MarkerType.icon" matButtonIcon style="object-fit: contain; width: 24px; height: 24px;" [innerHTML]="iconPreview$ | async" [class.disabled]="disabled">
|
||||
</div>
|
||||
</button>
|
||||
<tb-data-layer-color-settings helpId="widget/lib/map/color_fn" formControlName="color"></tb-data-layer-color-settings>
|
||||
<tb-data-layer-color-settings [context]="context"
|
||||
[dsType]="dsType"
|
||||
[dsEntityAliasId]="dsEntityAliasId"
|
||||
[dsDeviceId]="dsDeviceId"
|
||||
helpId="widget/lib/map/color_fn" formControlName="color"></tb-data-layer-color-settings>
|
||||
</div>
|
||||
|
||||
@ -49,6 +49,8 @@ import { coerceBoolean } from '@shared/decorators/coercion';
|
||||
import {
|
||||
MarkerIconShapesComponent
|
||||
} from '@home/components/widget/lib/settings/common/map/marker-icon-shapes.component';
|
||||
import { MapSettingsContext } from '@home/components/widget/lib/settings/common/map/map-settings.component.models';
|
||||
import { DatasourceType } from '@shared/models/widget.models';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-marker-shape-settings',
|
||||
@ -69,6 +71,18 @@ export class MarkerShapeSettingsComponent implements ControlValueAccessor, OnIni
|
||||
@Input()
|
||||
disabled: boolean;
|
||||
|
||||
@Input()
|
||||
context: MapSettingsContext;
|
||||
|
||||
@Input()
|
||||
dsType: DatasourceType;
|
||||
|
||||
@Input()
|
||||
dsEntityAliasId: string;
|
||||
|
||||
@Input()
|
||||
dsDeviceId: string;
|
||||
|
||||
@Input()
|
||||
markerType: MarkerType;
|
||||
|
||||
|
||||
@ -41,7 +41,7 @@ import { Observable, Observer, of, switchMap } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { ImagePipe } from '@shared/pipe/image.pipe';
|
||||
import { MarkerIconContainer, MarkerShape } from '@shared/models/widget/maps/marker-shape.models';
|
||||
import { DateFormatSettings, simpleDateFormat } from '@shared/models/widget-settings.models';
|
||||
import { ColorRange, DateFormatSettings, simpleDateFormat } from '@shared/models/widget-settings.models';
|
||||
|
||||
export enum MapType {
|
||||
geoMap = 'geoMap',
|
||||
@ -233,12 +233,15 @@ export enum MarkerType {
|
||||
|
||||
export enum DataLayerColorType {
|
||||
constant = 'constant',
|
||||
range = 'range',
|
||||
function = 'function'
|
||||
}
|
||||
|
||||
export interface DataLayerColorSettings {
|
||||
type: DataLayerColorType;
|
||||
color: string;
|
||||
rangeKey?: DataKey;
|
||||
range?: ColorRange[];
|
||||
colorFunction?: TbFunction;
|
||||
}
|
||||
|
||||
|
||||
@ -7979,7 +7979,11 @@
|
||||
"stroke": "Stroke",
|
||||
"color-settings": "Color settings",
|
||||
"color-type-constant": "Constant",
|
||||
"color-type-range": "Range",
|
||||
"color-type-function": "Function",
|
||||
"color-range-source-key": "Color range source key",
|
||||
"color-range-source-key-required": "Color range source key is required",
|
||||
"color-range": "Color range",
|
||||
"color-function": "Color function",
|
||||
"label": "Label",
|
||||
"tooltip": "Tooltip",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user