Browse Source

Merge pull request #13084 from vvlladd28/improvement/action/place-map-item/tooltips

Add to place map item action  - customize tooltip
pull/13086/head
Igor Kulikov 2 years ago
committed by GitHub
parent
commit
ad7905f393
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      ui-ngx/src/app/core/services/utils.service.ts
  2. 41
      ui-ngx/src/app/modules/home/components/widget/lib/maps/map.ts
  3. 82
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/map-item-tooltips.component.html
  4. 123
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/map-item-tooltips.component.ts
  5. 4
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action.component.html
  6. 4
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action.component.ts
  7. 4
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts
  8. 25
      ui-ngx/src/app/shared/models/widget.models.ts
  9. 13
      ui-ngx/src/assets/locale/locale.constant-en_US.json

2
ui-ngx/src/app/core/services/utils.service.ts

@ -208,7 +208,7 @@ export class UtilsService {
return parseException(exception, lineOffset); return parseException(exception, lineOffset);
} }
public customTranslation(translationValue: string, defaultValue: string): string { public customTranslation(translationValue: string, defaultValue: string = translationValue): string {
if (translationValue && isString(translationValue)) { if (translationValue && isString(translationValue)) {
if (translationValue.includes(`{${i18nPrefix}`)) { if (translationValue.includes(`{${i18nPrefix}`)) {
const matches = translationValue.match(i18nRegExp); const matches = translationValue.match(i18nRegExp);

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

@ -50,7 +50,14 @@ import {
UnplacedMapDataItem, UnplacedMapDataItem,
} from '@home/components/widget/lib/maps/data-layer/latest-map-data-layer'; } from '@home/components/widget/lib/maps/data-layer/latest-map-data-layer';
import { IWidgetSubscription, PlaceMapItemActionData, WidgetSubscriptionOptions } from '@core/api/widget-api.models'; import { IWidgetSubscription, PlaceMapItemActionData, WidgetSubscriptionOptions } from '@core/api/widget-api.models';
import { FormattedData, MapItemType, WidgetAction, WidgetActionType, widgetType } from '@shared/models/widget.models'; import {
FormattedData,
mapItemTooltipsTranslation,
MapItemType,
WidgetAction,
WidgetActionType,
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 { TbMarkersDataLayer } from '@home/components/widget/lib/maps/data-layer/markers-data-layer'; import { TbMarkersDataLayer } from '@home/components/widget/lib/maps/data-layer/markers-data-layer';
@ -661,29 +668,45 @@ export abstract class TbMap<S extends BaseMapSettings> {
private createMarker(actionData: PlaceMapItemActionData) { private createMarker(actionData: PlaceMapItemActionData) {
this.createItem(actionData, () => this.prepareDrawMode('Marker', { this.createItem(actionData, () => this.prepareDrawMode('Marker', {
placeMarker: this.ctx.translate.instant('widgets.maps.data-layer.marker.place-marker-hint') placeMarker: actionData.action.mapItemTooltips.placeMarker
? this.ctx.utilsService.customTranslation(actionData.action.mapItemTooltips.placeMarker)
: this.ctx.translate.instant(mapItemTooltipsTranslation.placeMarker)
})); }));
} }
private createRectangle(actionData: PlaceMapItemActionData): void { private createRectangle(actionData: PlaceMapItemActionData): void {
this.createItem(actionData, () => this.prepareDrawMode('Rectangle', { this.createItem(actionData, () => this.prepareDrawMode('Rectangle', {
firstVertex: this.ctx.translate.instant('widgets.maps.data-layer.polygon.rectangle-place-first-point-hint'), firstVertex: actionData.action.mapItemTooltips.startRect
finishRect: this.ctx.translate.instant('widgets.maps.data-layer.polygon.finish-rectangle-hint') ? this.ctx.utilsService.customTranslation(actionData.action.mapItemTooltips.startRect)
: this.ctx.translate.instant(mapItemTooltipsTranslation.startRect),
finishRect: actionData.action.mapItemTooltips.finishRect
? this.ctx.utilsService.customTranslation(actionData.action.mapItemTooltips.finishRect)
: this.ctx.translate.instant(mapItemTooltipsTranslation.finishRect),
})); }));
} }
private createPolygon(actionData: PlaceMapItemActionData): void { private createPolygon(actionData: PlaceMapItemActionData): void {
this.createItem(actionData, () => this.prepareDrawMode('Polygon', { this.createItem(actionData, () => this.prepareDrawMode('Polygon', {
firstVertex: this.ctx.translate.instant('widgets.maps.data-layer.polygon.polygon-place-first-point-hint'), firstVertex: actionData.action.mapItemTooltips.firstVertex
continueLine: this.ctx.translate.instant('widgets.maps.data-layer.polygon.continue-polygon-hint'), ? this.ctx.utilsService.customTranslation(actionData.action.mapItemTooltips.firstVertex)
finishPoly: this.ctx.translate.instant('widgets.maps.data-layer.polygon.finish-polygon-hint') : this.ctx.translate.instant(mapItemTooltipsTranslation.firstVertex),
continueLine: actionData.action.mapItemTooltips.continueLine
? this.ctx.utilsService.customTranslation(actionData.action.mapItemTooltips.continueLine)
: this.ctx.translate.instant(mapItemTooltipsTranslation.continueLine),
finishPoly: actionData.action.mapItemTooltips.finishPoly
? this.ctx.utilsService.customTranslation(actionData.action.mapItemTooltips.finishPoly)
: this.ctx.translate.instant(mapItemTooltipsTranslation.finishPoly),
})); }));
} }
private createCircle(actionData: PlaceMapItemActionData): void { private createCircle(actionData: PlaceMapItemActionData): void {
this.createItem(actionData, () => this.prepareDrawMode('Circle', { this.createItem(actionData, () => this.prepareDrawMode('Circle', {
startCircle: this.ctx.translate.instant('widgets.maps.data-layer.circle.place-circle-center-hint'), startCircle: actionData.action.mapItemTooltips.startCircle
finishCircle: this.ctx.translate.instant('widgets.maps.data-layer.circle.finish-circle-hint') ? this.ctx.utilsService.customTranslation(actionData.action.mapItemTooltips.startCircle)
: this.ctx.translate.instant(mapItemTooltipsTranslation.startCircle),
finishCircle: actionData.action.mapItemTooltips.finishCircle
? this.ctx.utilsService.customTranslation(actionData.action.mapItemTooltips.finishCircle)
: this.ctx.translate.instant(mapItemTooltipsTranslation.finishCircle),
})); }));
} }

82
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/map-item-tooltips.component.html

@ -0,0 +1,82 @@
<!--
Copyright © 2016-2025 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-form-panel stroked" [formGroup]="tooltipsForm">
<mat-expansion-panel class="tb-settings">
<mat-expansion-panel-header>{{ 'widget-action.map-item-tooltip.customize-map-item-tooltips' | translate }}</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
@switch (mapItemType) {
@case (MapItemType.marker) {
<div class="tb-form-row">
<div class="fixed-title-width" translate>widget-action.map-item-tooltip.place-marker</div>
<mat-form-field class="flex-1" appearance="outline" subscriptSizing="dynamic">
<input matInput placeholder="{{ mapItemTooltipsDefaultTranslate.placeMarker | translate }}" formControlName="placeMarker">
</mat-form-field>
</div>
}
@case (MapItemType.rectangle) {
<div class="tb-form-row">
<div class="fixed-title-width" translate>widget-action.map-item-tooltip.start-draw-rectangle</div>
<mat-form-field class="flex-1" appearance="outline" subscriptSizing="dynamic">
<input matInput placeholder="{{ mapItemTooltipsDefaultTranslate.startRect | translate }}" formControlName="startRect">
</mat-form-field>
</div>
<div class="tb-form-row">
<div class="fixed-title-width" translate>widget-action.map-item-tooltip.finish-draw-rectangle</div>
<mat-form-field class="flex-1" appearance="outline" subscriptSizing="dynamic">
<input matInput placeholder="{{ mapItemTooltipsDefaultTranslate.finishRect | translate }}" formControlName="finishRect">
</mat-form-field>
</div>
}
@case (MapItemType.polygon) {
<div class="tb-form-row">
<div class="fixed-title-width" translate>widget-action.map-item-tooltip.start-draw-polygon</div>
<mat-form-field class="flex-1" appearance="outline" subscriptSizing="dynamic">
<input matInput placeholder="{{ mapItemTooltipsDefaultTranslate.firstVertex | translate }}" formControlName="firstVertex">
</mat-form-field>
</div>
<div class="tb-form-row">
<div class="fixed-title-width" translate>widget-action.map-item-tooltip.continue-draw-polygon</div>
<mat-form-field class="flex-1" appearance="outline" subscriptSizing="dynamic">
<input matInput placeholder="{{ mapItemTooltipsDefaultTranslate.continueLine | translate }}" formControlName="continueLine">
</mat-form-field>
</div>
<div class="tb-form-row">
<div class="fixed-title-width" translate>widget-action.map-item-tooltip.finish-draw-polygon</div>
<mat-form-field class="flex-1" appearance="outline" subscriptSizing="dynamic">
<input matInput placeholder="{{ mapItemTooltipsDefaultTranslate.finishPoly | translate }}" formControlName="finishPoly">
</mat-form-field>
</div>
}
@case (MapItemType.circle) {
<div class="tb-form-row">
<div class="fixed-title-width" translate>widget-action.map-item-tooltip.start-draw-circle</div>
<mat-form-field class="flex-1" appearance="outline" subscriptSizing="dynamic">
<input matInput placeholder="{{ mapItemTooltipsDefaultTranslate.startCircle | translate }}" formControlName="startCircle">
</mat-form-field>
</div>
<div class="tb-form-row">
<div class="fixed-title-width" translate>widget-action.map-item-tooltip.finish-draw-circle</div>
<mat-form-field class="flex-1" appearance="outline" subscriptSizing="dynamic">
<input matInput placeholder="{{ mapItemTooltipsDefaultTranslate.finishCircle | translate }}" formControlName="finishCircle">
</mat-form-field>
</div>
}
}
</ng-template>
</mat-expansion-panel>
</div>

123
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/map-item-tooltips.component.ts

@ -0,0 +1,123 @@
///
/// Copyright © 2016-2025 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, OnChanges, SimpleChanges } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
import { MapItemTooltips, MapItemType, mapItemTooltipsTranslation } from '@shared/models/widget.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { deepTrim, isEqual } from '@core/utils';
@Component({
selector: 'tb-map-item-tooltips',
templateUrl: './map-item-tooltips.component.html',
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => MapItemTooltipsComponent),
multi: true
}
]
})
export class MapItemTooltipsComponent implements ControlValueAccessor, OnChanges {
@Input({required: true})
mapItemType: MapItemType;
tooltipsForm: FormGroup;
MapItemType = MapItemType;
readonly mapItemTooltipsDefaultTranslate = mapItemTooltipsTranslation;
private modelValue: MapItemTooltips;
private propagateChange = (_val: any) => {};
constructor(private fd: FormBuilder) {
this.tooltipsForm = this.fd.group({
placeMarker: [''],
firstVertex: [''],
continueLine: [''],
finishPoly: [''],
startRect: [''],
finishRect: [''],
startCircle: [''],
finishCircle: ['']
});
this.tooltipsForm.valueChanges.pipe(
takeUntilDestroyed()
).subscribe(this.updatedModel.bind(this));
}
ngOnChanges(changes: SimpleChanges) {
if (changes.mapItemType) {
const mapItemTypeChanges = changes.mapItemType;
if (!mapItemTypeChanges.firstChange && mapItemTypeChanges.currentValue !== mapItemTypeChanges.previousValue) {
this.updatedValidators(true);
}
}
}
registerOnChange(fn: any) {
this.propagateChange = fn;
}
registerOnTouched(_fn: any) {
}
setDisabledState(isDisabled: boolean) {
if (isDisabled) {
this.tooltipsForm.disable({emitEvent: false});
} else {
this.tooltipsForm.enable({emitEvent: false});
}
}
writeValue(obj: MapItemTooltips) {
this.modelValue = obj;
this.tooltipsForm.patchValue(obj, {emitEvent: false});
this.updatedValidators();
}
private updatedValidators(emitNewValue = false) {
this.tooltipsForm.disable({emitEvent: false});
switch (this.mapItemType) {
case MapItemType.marker:
this.tooltipsForm.get('placeMarker').enable({emitEvent: false});
break;
case MapItemType.rectangle:
this.tooltipsForm.get('startRect').enable({emitEvent: false});
this.tooltipsForm.get('finishRect').enable({emitEvent: false});
break;
case MapItemType.polygon:
this.tooltipsForm.get('firstVertex').enable({emitEvent: false});
this.tooltipsForm.get('continueLine').enable({emitEvent: false});
this.tooltipsForm.get('finishPoly').enable({emitEvent: false});
break;
case MapItemType.circle:
this.tooltipsForm.get('startCircle').enable({emitEvent: false});
this.tooltipsForm.get('finishCircle').enable({emitEvent: false});
break;
}
this.tooltipsForm.updateValueAndValidity({emitEvent: emitNewValue})
}
private updatedModel(value: MapItemTooltips) {
const currentValue = Object.fromEntries(Object.entries(deepTrim(value)).filter(([_, v]) => v != ''));
if (!isEqual(currentValue, this.modelValue)) {
this.modelValue = currentValue;
this.propagateChange(currentValue);
}
}
}

4
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action.component.html

@ -270,6 +270,10 @@
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</div> </div>
<tb-map-item-tooltips
[mapItemType]="actionTypeFormGroup.get('mapItemType').value"
formControlName="mapItemTooltips">
</tb-map-item-tooltips>
</ng-template> </ng-template>
<ng-template [ngSwitchCase]="widgetActionFormGroup.get('type').value === widgetActionType.customPretty <ng-template [ngSwitchCase]="widgetActionFormGroup.get('type').value === widgetActionType.customPretty
|| widgetActionFormGroup.get('type').value === widgetActionType.placeMapItem || widgetActionFormGroup.get('type').value === widgetActionType.placeMapItem

4
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action.component.ts

@ -329,6 +329,7 @@ export class WidgetActionComponent implements ControlValueAccessor, OnInit, Vali
'mapItemType', 'mapItemType',
this.fb.control(action?.mapItemType ?? MapItemType.marker, [Validators.required]) this.fb.control(action?.mapItemType ?? MapItemType.marker, [Validators.required])
); );
this.actionTypeFormGroup.addControl('mapItemTooltips', this.fb.control(action?.mapItemTooltips ?? {}));
this.actionTypeFormGroup.addControl( this.actionTypeFormGroup.addControl(
'customAction', 'customAction',
this.fb.control(toPlaceMapItemAction(action), [Validators.required]) this.fb.control(toPlaceMapItemAction(action), [Validators.required])
@ -528,7 +529,8 @@ export class WidgetActionComponent implements ControlValueAccessor, OnInit, Vali
result = { result = {
...this.widgetActionFormGroup.value, ...this.widgetActionFormGroup.value,
...this.actionTypeFormGroup.get('customAction').value, ...this.actionTypeFormGroup.get('customAction').value,
mapItemType: this.actionTypeFormGroup.get('mapItemType').value mapItemType: this.actionTypeFormGroup.get('mapItemType').value,
mapItemTooltips: this.actionTypeFormGroup.get('mapItemTooltips').value,
}; };
} else { } else {
result = {...this.widgetActionFormGroup.value, ...this.actionTypeFormGroup.value}; result = {...this.widgetActionFormGroup.value, ...this.actionTypeFormGroup.value};

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

@ -69,6 +69,9 @@ import {
} from '@home/components/widget/lib/settings/common/action/set-value-action-settings-panel.component'; } from '@home/components/widget/lib/settings/common/action/set-value-action-settings-panel.component';
import { CssSizeInputComponent } from '@home/components/widget/lib/settings/common/css-size-input.component'; import { CssSizeInputComponent } from '@home/components/widget/lib/settings/common/css-size-input.component';
import { WidgetActionComponent } from '@home/components/widget/lib/settings/common/action/widget-action.component'; import { WidgetActionComponent } from '@home/components/widget/lib/settings/common/action/widget-action.component';
import {
MapItemTooltipsComponent
} from '@home/components/widget/lib/settings/common/action/map-item-tooltips.component';
import { import {
CustomActionPrettyResourcesTabsComponent CustomActionPrettyResourcesTabsComponent
} from '@home/components/widget/lib/settings/common/action/custom-action-pretty-resources-tabs.component'; } from '@home/components/widget/lib/settings/common/action/custom-action-pretty-resources-tabs.component';
@ -283,6 +286,7 @@ import {
SetValueActionSettingsComponent, SetValueActionSettingsComponent,
SetValueActionSettingsPanelComponent, SetValueActionSettingsPanelComponent,
WidgetActionComponent, WidgetActionComponent,
MapItemTooltipsComponent,
CustomActionPrettyResourcesTabsComponent, CustomActionPrettyResourcesTabsComponent,
CustomActionPrettyEditorComponent, CustomActionPrettyEditorComponent,
MobileActionEditorComponent, MobileActionEditorComponent,

25
ui-ngx/src/app/shared/models/widget.models.ts

@ -770,7 +770,30 @@ export interface WidgetAction extends CustomActionDescriptor {
mobileAction?: WidgetMobileActionDescriptor; mobileAction?: WidgetMobileActionDescriptor;
url?: string; url?: string;
mapItemType?: MapItemType; mapItemType?: MapItemType;
} mapItemTooltips?: MapItemTooltips;
}
export interface MapItemTooltips {
placeMarker?: string;
firstVertex?: string;
continueLine?: string;
finishPoly?: string;
startRect?: string;
finishRect?: string;
startCircle?: string;
finishCircle?: string;
}
export const mapItemTooltipsTranslation: Required<MapItemTooltips> = Object.freeze({
placeMarker: 'widgets.maps.data-layer.marker.place-marker-hint',
firstVertex: 'widgets.maps.data-layer.polygon.polygon-place-first-point-hint',
continueLine: 'widgets.maps.data-layer.polygon.continue-polygon-hint',
finishPoly: 'widgets.maps.data-layer.polygon.finish-polygon-hint',
startRect: 'widgets.maps.data-layer.polygon.rectangle-place-first-point-hint',
finishRect: 'widgets.maps.data-layer.polygon.finish-rectangle-hint',
startCircle: 'widgets.maps.data-layer.circle.place-circle-center-hint',
finishCircle: 'widgets.maps.data-layer.circle.finish-circle-hint'
})
export interface WidgetActionDescriptor extends WidgetAction { export interface WidgetActionDescriptor extends WidgetAction {
id: string; id: string;

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

@ -6592,7 +6592,18 @@
"rectangle": "Rectangle", "rectangle": "Rectangle",
"circle": "Circle" "circle": "Circle"
}, },
"place-map-item": "Place map item" "place-map-item": "Place map item",
"map-item-tooltip": {
"customize-map-item-tooltips": "Customize map item tooltips",
"place-marker": "Place marker",
"start-draw-rectangle": "Start draw rectangle",
"finish-draw-rectangle": "Finish draw rectangle",
"start-draw-polygon": "Start draw polygon",
"continue-draw-polygon": "Continue draw polygon",
"finish-draw-polygon": "Finish draw polygon",
"start-draw-circle": "Start draw circle",
"finish-draw-circle": "Finish draw circle"
}
}, },
"widgets-bundle": { "widgets-bundle": {
"current": "Current bundle", "current": "Current bundle",

Loading…
Cancel
Save