20 changed files with 407 additions and 81 deletions
@ -0,0 +1,67 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
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-form-row gap-4"> |
||||
|
<div translate>widgets.maps.data-layer.tooltip-tag-actions</div> |
||||
|
<div class="flex flex-1 flex-row items-center justify-end gap-4"> |
||||
|
<mat-chip-listbox> |
||||
|
<mat-chip *ngFor="let action of this.actionsFormGroup.get('actions').value; let $index = index;" |
||||
|
class="tb-tag-action-chip" |
||||
|
[removable]="!disabled" (removed)="removeAction($index)"> |
||||
|
<div class="flex flex-row items-center justify-start gap-1"> |
||||
|
<div class="tb-chip-labels"> |
||||
|
<div class="tb-chip-label tb-chip-text"> |
||||
|
<strong> |
||||
|
{{ action.name }} |
||||
|
</strong> |
||||
|
</div> |
||||
|
</div> |
||||
|
<button *ngIf="!disabled" |
||||
|
#editButton |
||||
|
type="button" |
||||
|
mat-icon-button |
||||
|
class="tb-mat-24" |
||||
|
matTooltip="{{ 'widgets.maps.data-layer.edit-tooltip-tag-action' | translate }}" |
||||
|
matTooltipPosition="above" |
||||
|
(click)="editAction($event, editButton, $index)"> |
||||
|
<mat-icon class="tb-mat-18">edit</mat-icon> |
||||
|
</button> |
||||
|
<button *ngIf="!disabled" |
||||
|
matChipRemove |
||||
|
type="button" |
||||
|
mat-icon-button |
||||
|
class="tb-mat-24" |
||||
|
matTooltip="{{ 'widgets.maps.data-layer.remove-tooltip-tag-action' | translate }}" |
||||
|
matTooltipPosition="above"> |
||||
|
<mat-icon class="tb-mat-18">close</mat-icon> |
||||
|
</button> |
||||
|
</div> |
||||
|
</mat-chip> |
||||
|
</mat-chip-listbox> |
||||
|
<button *ngIf="!disabled" |
||||
|
#addButton |
||||
|
type="button" |
||||
|
mat-icon-button |
||||
|
class="tb-mat-40" |
||||
|
color="primary" |
||||
|
matTooltip="{{ 'widgets.maps.data-layer.add-tooltip-tag-action' | translate }}" |
||||
|
matTooltipPosition="above" |
||||
|
(click)="addAction($event, addButton)"> |
||||
|
<mat-icon>add</mat-icon> |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
@ -0,0 +1,49 @@ |
|||||
|
/** |
||||
|
* 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. |
||||
|
*/ |
||||
|
.mat-mdc-chip.mat-mdc-standard-chip.tb-tag-action-chip { |
||||
|
overflow: hidden; |
||||
|
line-height: 20px; |
||||
|
height: 32px; |
||||
|
|
||||
|
&.mdc-evolution-chip--with-trailing-action { |
||||
|
.mdc-evolution-chip__action--primary { |
||||
|
padding-left: 4px; |
||||
|
padding-right: 12px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.tb-chip-labels { |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
align-items: center; |
||||
|
min-width: 0; |
||||
|
padding: 2px 10px; |
||||
|
.tb-chip-label { |
||||
|
font-weight: normal; |
||||
|
font-size: 14px; |
||||
|
line-height: 20px; |
||||
|
&.tb-chip-text { |
||||
|
overflow: hidden; |
||||
|
text-overflow: ellipsis; |
||||
|
white-space: nowrap; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
.mat-mdc-chip-remove.mat-mdc-icon-button { |
||||
|
color: inherit; |
||||
|
opacity: inherit; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,176 @@ |
|||||
|
///
|
||||
|
/// 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, |
||||
|
forwardRef, |
||||
|
Input, |
||||
|
OnInit, |
||||
|
Renderer2, |
||||
|
ViewContainerRef, |
||||
|
ViewEncapsulation |
||||
|
} from '@angular/core'; |
||||
|
import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; |
||||
|
import { WidgetAction, WidgetActionType, widgetType } from '@shared/models/widget.models'; |
||||
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; |
||||
|
import { TbPopoverService } from '@shared/components/popover.service'; |
||||
|
import { MapSettingsContext } from '@home/components/widget/lib/settings/common/map/map-settings.component.models'; |
||||
|
import { |
||||
|
WidgetActionSettingsPanelComponent |
||||
|
} from '@home/components/widget/lib/settings/common/action/widget-action-settings-panel.component'; |
||||
|
import { MatButton } from '@angular/material/button'; |
||||
|
import { TranslateService } from '@ngx-translate/core'; |
||||
|
import { deepClone } from '@core/utils'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-map-tooltip-tag-actions-panel', |
||||
|
templateUrl: './map-tooltip-tag-actions.component.html', |
||||
|
styleUrls: ['./map-tooltip-tag-actions.component.scss'], |
||||
|
providers: [ |
||||
|
{ |
||||
|
provide: NG_VALUE_ACCESSOR, |
||||
|
useExisting: forwardRef(() => MapTooltipTagActionsComponent), |
||||
|
multi: true |
||||
|
} |
||||
|
], |
||||
|
encapsulation: ViewEncapsulation.None |
||||
|
}) |
||||
|
export class MapTooltipTagActionsComponent implements ControlValueAccessor, OnInit { |
||||
|
|
||||
|
@Input() |
||||
|
disabled: boolean; |
||||
|
|
||||
|
@Input() |
||||
|
context: MapSettingsContext; |
||||
|
|
||||
|
actionsFormGroup: UntypedFormGroup; |
||||
|
|
||||
|
private propagateChange = (_val: any) => {}; |
||||
|
|
||||
|
constructor(private fb: UntypedFormBuilder, |
||||
|
private popoverService: TbPopoverService, |
||||
|
private renderer: Renderer2, |
||||
|
private viewContainerRef: ViewContainerRef, |
||||
|
private translate: TranslateService, |
||||
|
private destroyRef: DestroyRef) { |
||||
|
} |
||||
|
|
||||
|
ngOnInit() { |
||||
|
this.actionsFormGroup = this.fb.group({ |
||||
|
actions: [null, []] |
||||
|
}); |
||||
|
this.actionsFormGroup.get('actions').valueChanges.pipe( |
||||
|
takeUntilDestroyed(this.destroyRef) |
||||
|
).subscribe( |
||||
|
(val) => this.propagateChange(val) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
writeValue(actions?: WidgetAction[]): void { |
||||
|
this.actionsFormGroup.get('actions').patchValue(actions || [], {emitEvent: false}); |
||||
|
} |
||||
|
|
||||
|
registerOnChange(fn: any): void { |
||||
|
this.propagateChange = fn; |
||||
|
} |
||||
|
|
||||
|
registerOnTouched(fn: any): void { |
||||
|
} |
||||
|
|
||||
|
setDisabledState(isDisabled: boolean): void { |
||||
|
this.disabled = isDisabled; |
||||
|
if (this.disabled) { |
||||
|
this.actionsFormGroup.disable({emitEvent: false}); |
||||
|
} else { |
||||
|
this.actionsFormGroup.enable({emitEvent: false}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
removeAction(index: number): void { |
||||
|
const actions: WidgetAction[] = this.actionsFormGroup.get('actions').value; |
||||
|
if (actions[index]) { |
||||
|
actions.splice(index, 1); |
||||
|
this.actionsFormGroup.get('actions').patchValue(actions); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
addAction($event: Event, matButton: MatButton): void { |
||||
|
if ($event) { |
||||
|
$event.stopPropagation(); |
||||
|
} |
||||
|
const action: WidgetAction = { |
||||
|
name: '', |
||||
|
type: WidgetActionType.doNothing |
||||
|
}; |
||||
|
const trigger = matButton._elementRef.nativeElement; |
||||
|
const actionNames = (this.actionsFormGroup.get('actions').value as WidgetAction[] || []).map(action => action.name); |
||||
|
this.openActionSettingsPopup(trigger, action, actionNames, true, (added) => { |
||||
|
if (added) { |
||||
|
const actions: WidgetAction[] = this.actionsFormGroup.get('actions').value || []; |
||||
|
actions.push(added); |
||||
|
this.actionsFormGroup.get('actions').patchValue(actions); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
editAction($event: Event, matButton: MatButton, index: number): void { |
||||
|
if ($event) { |
||||
|
$event.stopPropagation(); |
||||
|
} |
||||
|
const actions: WidgetAction[] = this.actionsFormGroup.get('actions').value; |
||||
|
if (actions[index]) { |
||||
|
const action = deepClone(actions[index]); |
||||
|
const trigger = matButton._elementRef.nativeElement; |
||||
|
const actionNames = actions.filter((_action, current) => current !== index).map(action => action.name); |
||||
|
this.openActionSettingsPopup(trigger, action, actionNames, false, (updated) => { |
||||
|
if (updated) { |
||||
|
actions[index] = updated; |
||||
|
this.actionsFormGroup.get('actions').patchValue(actions); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private openActionSettingsPopup(trigger: Element, action: WidgetAction, actionNames: string[], isAdd: boolean, callback: (action?: WidgetAction) => void) { |
||||
|
if (this.popoverService.hasPopover(trigger)) { |
||||
|
this.popoverService.hidePopover(trigger); |
||||
|
} else { |
||||
|
const title = this.translate.instant(isAdd ? 'widgets.maps.data-layer.add-tooltip-tag-action' : 'widgets.maps.data-layer.edit-tooltip-tag-action'); |
||||
|
const applyTitle = this.translate.instant(isAdd ? 'action.add' : 'action.apply'); |
||||
|
const ctx: any = { |
||||
|
widgetAction: action, |
||||
|
withName: true, |
||||
|
actionNames, |
||||
|
panelTitle: title, |
||||
|
applyTitle, |
||||
|
widgetType: widgetType.latest, |
||||
|
callbacks: this.context.callbacks |
||||
|
}; |
||||
|
const widgetActionSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer, |
||||
|
this.viewContainerRef, WidgetActionSettingsPanelComponent, |
||||
|
['leftTopOnly', 'leftOnly', 'leftBottomOnly'], true, null, |
||||
|
ctx, |
||||
|
{}, |
||||
|
{}, {}, true); |
||||
|
widgetActionSettingsPanelPopover.tbComponentRef.instance.widgetActionApplied.subscribe((widgetAction) => { |
||||
|
widgetActionSettingsPanelPopover.hide(); |
||||
|
callback(widgetAction); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
Loading…
Reference in new issue