committed by
GitHub
30 changed files with 855 additions and 151 deletions
@ -0,0 +1,47 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
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 [formGroup]="mapActionButton" class="tb-form-table-row"> |
||||
|
<mat-form-field class="tb-inline-field tb-label-field min-w-32" style="flex: 1 1 40%" appearance="outline" subscriptSizing="dynamic"> |
||||
|
<input matInput formControlName="label" placeholder="{{ 'widget-config.set' | translate }}"> |
||||
|
<mat-icon matSuffix |
||||
|
matTooltipPosition="above" |
||||
|
matTooltipClass="tb-error-tooltip" |
||||
|
matTooltip="{{ 'widgets.maps.map-action.button-requires' | translate }}" |
||||
|
*ngIf="mapActionButton.hasError('invalidButtonConfig')" |
||||
|
class="tb-error"> |
||||
|
warning |
||||
|
</mat-icon> |
||||
|
</mat-form-field> |
||||
|
<tb-material-icon-select formControlName="icon" asBoxInput iconClearButton [color]="this.mapActionButton.get('color').value"></tb-material-icon-select> |
||||
|
<tb-color-input formControlName="color" asBoxInput required></tb-color-input> |
||||
|
<tb-widget-action-settings class="min-w-32" style="flex: 1 1 60%" |
||||
|
formControlName="action" |
||||
|
panelTitle="{{ 'widgets.maps.map-action.map-action-button' | translate }}" |
||||
|
[widgetType]="widgetType.latest" |
||||
|
[additionalWidgetActionTypes]="additionalWidgetActionTypes"> |
||||
|
</tb-widget-action-settings> |
||||
|
<div class="tb-form-table-row-cell-buttons w-10 min-w-10"> |
||||
|
<button type="button" |
||||
|
mat-icon-button |
||||
|
(click)="buttonRemoved.emit()" |
||||
|
matTooltip="{{ 'widgets.maps.map-action.remove-action-button' | translate }}" |
||||
|
matTooltipPosition="above"> |
||||
|
<mat-icon>delete</mat-icon> |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
@ -0,0 +1,100 @@ |
|||||
|
///
|
||||
|
/// 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, EventEmitter, forwardRef, Output } from '@angular/core'; |
||||
|
import { |
||||
|
ControlValueAccessor, |
||||
|
FormBuilder, |
||||
|
FormGroup, |
||||
|
NG_VALIDATORS, |
||||
|
NG_VALUE_ACCESSOR, |
||||
|
ValidationErrors, |
||||
|
Validator |
||||
|
} from '@angular/forms'; |
||||
|
import { MapActionButtonSettings } from '@home/components/widget/lib/maps/models/map.models'; |
||||
|
import { WidgetAction, WidgetActionType, widgetType } from '@shared/models/widget.models'; |
||||
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; |
||||
|
import { isEmptyStr } from '@core/utils'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-map-action-button-row', |
||||
|
templateUrl: 'map-action-button-row.component.html', |
||||
|
providers: [{ |
||||
|
provide: NG_VALUE_ACCESSOR, |
||||
|
useExisting: forwardRef(() => MapActionButtonRowComponent), |
||||
|
multi: true |
||||
|
}, |
||||
|
{ |
||||
|
provide: NG_VALIDATORS, |
||||
|
useExisting: forwardRef(() => MapActionButtonRowComponent), |
||||
|
multi: true |
||||
|
}] |
||||
|
}) |
||||
|
export class MapActionButtonRowComponent implements ControlValueAccessor, Validator { |
||||
|
|
||||
|
@Output() |
||||
|
buttonRemoved = new EventEmitter(); |
||||
|
|
||||
|
mapActionButton = this.fb.group({ |
||||
|
label: [''], |
||||
|
icon: [''], |
||||
|
color: [''], |
||||
|
action: this.fb.control<WidgetAction>(null) |
||||
|
}, {validators: this.validateButtonConfig()}); |
||||
|
|
||||
|
additionalWidgetActionTypes = [WidgetActionType.placeMapItem]; |
||||
|
readonly widgetType = widgetType; |
||||
|
|
||||
|
private propagateChange = (_val: any) => {}; |
||||
|
|
||||
|
constructor(private fb: FormBuilder) { |
||||
|
this.mapActionButton.valueChanges.pipe( |
||||
|
takeUntilDestroyed() |
||||
|
).subscribe(value => this.propagateChange(value)) |
||||
|
} |
||||
|
|
||||
|
registerOnChange(fn: any) { |
||||
|
this.propagateChange = fn; |
||||
|
} |
||||
|
|
||||
|
registerOnTouched(_fn: any): void { } |
||||
|
|
||||
|
setDisabledState(isDisabled: boolean) { |
||||
|
if (isDisabled) { |
||||
|
this.mapActionButton.disable({emitEvent: false}); |
||||
|
} else { |
||||
|
this.mapActionButton.enable({emitEvent: false}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
validate(): ValidationErrors | null { |
||||
|
return this.mapActionButton.valid ? null : { |
||||
|
mapButtonAction: false |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
writeValue(value: MapActionButtonSettings) { |
||||
|
this.mapActionButton.patchValue(value, {emitEvent: false}); |
||||
|
} |
||||
|
|
||||
|
private validateButtonConfig() { |
||||
|
return (c: FormGroup) => { |
||||
|
return !c.value.icon && isEmptyStr(c.value.label) ? { |
||||
|
invalidButtonConfig: true |
||||
|
} : null; |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,63 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
--> |
||||
|
<section class="tb-form-panel"> |
||||
|
<mat-expansion-panel class="tb-settings" expanded> |
||||
|
<mat-expansion-panel-header> |
||||
|
{{ 'widgets.maps.map-action.map-action-buttons' | translate }} |
||||
|
</mat-expansion-panel-header> |
||||
|
<ng-template matExpansionPanelContent> |
||||
|
<div class="tb-form-table overflow-y-auto"> |
||||
|
<div class="tb-form-table-header no-padding-right"> |
||||
|
<div class="tb-form-table-header-cell min-w-32" style="flex: 1 1 40%" translate>widgets.maps.map-action.label</div> |
||||
|
<div class="tb-form-table-header-cell w-10 min-w-10" translate>widgets.maps.map-action.icon</div> |
||||
|
<div class="tb-form-table-header-cell w-10 min-w-10" translate>widgets.maps.map-action.color</div> |
||||
|
<div class="tb-form-table-header-cell min-w-32" style="flex: 1 1 60%" translate>widgets.maps.map-action.action</div> |
||||
|
<div class="tb-form-table-header-cell w-20 min-w-20 xs:w-10 xs:min-w-10"></div> |
||||
|
</div> |
||||
|
<div class="tb-form-table-body" cdkDropList cdkDropListOrientation="vertical" |
||||
|
[cdkDropListDisabled]="!dragEnabled" |
||||
|
(cdkDropListDropped)="buttonDrop($event)"> |
||||
|
@for (mapAction of mapActionButtonsForm.controls; track mapAction) { |
||||
|
<div cdkDrag [cdkDragDisabled]="!dragEnabled" class="tb-draggable-form-table-row"> |
||||
|
<tb-map-action-button-row class="flex-1" |
||||
|
[formControl]="mapAction" |
||||
|
(buttonRemoved)="removeButton($event)"> |
||||
|
</tb-map-action-button-row> |
||||
|
<div class="tb-form-table-row-cell-buttons"> |
||||
|
<button class="xs:!hidden" |
||||
|
[class.tb-hidden]="!dragEnabled" |
||||
|
mat-icon-button |
||||
|
type="button" |
||||
|
cdkDragHandle |
||||
|
matTooltip="{{ 'action.drag' | translate }}" |
||||
|
matTooltipPosition="above"> |
||||
|
<mat-icon>drag_indicator</mat-icon> |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
} @empty { |
||||
|
<span class="tb-prompt flex items-center justify-center">{{ 'widgets.maps.map-action.no-action-buttons-configured' | translate }}</span> |
||||
|
} |
||||
|
</div> |
||||
|
</div> |
||||
|
<button type="button" mat-stroked-button color="primary" class="max-w-fit" (click)="addButton()"> |
||||
|
{{ 'widgets.maps.map-action.add-button' | translate }} |
||||
|
</button> |
||||
|
</ng-template> |
||||
|
</mat-expansion-panel> |
||||
|
</section> |
||||
@ -0,0 +1,107 @@ |
|||||
|
///
|
||||
|
/// 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 } from '@angular/core'; |
||||
|
import { |
||||
|
ControlValueAccessor, |
||||
|
FormBuilder, |
||||
|
NG_VALIDATORS, |
||||
|
NG_VALUE_ACCESSOR, |
||||
|
ValidationErrors, |
||||
|
Validator |
||||
|
} from '@angular/forms'; |
||||
|
import { |
||||
|
defaultMapActionButtonSettings, |
||||
|
MapActionButtonSettings |
||||
|
} from '@home/components/widget/lib/maps/models/map.models'; |
||||
|
import { CdkDragDrop } from '@angular/cdk/drag-drop'; |
||||
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-map-action-button-settings', |
||||
|
templateUrl: './map-action-buttons-settings.component.html', |
||||
|
providers: [{ |
||||
|
provide: NG_VALUE_ACCESSOR, |
||||
|
useExisting: forwardRef(() => MapActionButtonsSettingsComponent), |
||||
|
multi: true |
||||
|
}, |
||||
|
{ |
||||
|
provide: NG_VALIDATORS, |
||||
|
useExisting: forwardRef(() => MapActionButtonsSettingsComponent), |
||||
|
multi: true |
||||
|
}] |
||||
|
}) |
||||
|
export class MapActionButtonsSettingsComponent implements ControlValueAccessor, Validator { |
||||
|
|
||||
|
mapActionButtonsForm = this.fb.array<MapActionButtonSettings>([]); |
||||
|
|
||||
|
private propagateChange = (_val: any) => {}; |
||||
|
|
||||
|
constructor(private fb: FormBuilder) { |
||||
|
this.mapActionButtonsForm.valueChanges.pipe( |
||||
|
takeUntilDestroyed() |
||||
|
).subscribe(value => this.propagateChange(value)); |
||||
|
} |
||||
|
|
||||
|
registerOnChange(fn: any): void { |
||||
|
this.propagateChange = fn; |
||||
|
} |
||||
|
|
||||
|
registerOnTouched(_fn: any): void { } |
||||
|
|
||||
|
setDisabledState(isDisabled: boolean): void { |
||||
|
if (isDisabled) { |
||||
|
this.mapActionButtonsForm.disable({emitEvent: false}); |
||||
|
} else { |
||||
|
this.mapActionButtonsForm.enable({emitEvent: false}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
validate(): ValidationErrors | null { |
||||
|
return this.mapActionButtonsForm.valid ? null : { |
||||
|
mapActionButtons: false |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
writeValue(buttons: MapActionButtonSettings[] = []) { |
||||
|
if (buttons?.length === this.mapActionButtonsForm.length) { |
||||
|
this.mapActionButtonsForm.patchValue(buttons, {emitEvent: false}); |
||||
|
} else { |
||||
|
this.mapActionButtonsForm.clear({emitEvent: false}); |
||||
|
buttons.forEach( |
||||
|
button => this.mapActionButtonsForm.push(this.fb.control(button), {emitEvent: false}) |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
get dragEnabled(): boolean { |
||||
|
return this.mapActionButtonsForm.length > 1; |
||||
|
} |
||||
|
|
||||
|
buttonDrop(event: CdkDragDrop<string[]>) { |
||||
|
const actionButton = this.mapActionButtonsForm.at(event.previousIndex); |
||||
|
this.mapActionButtonsForm.removeAt(event.previousIndex, {emitEvent: false}); |
||||
|
this.mapActionButtonsForm.insert(event.currentIndex, actionButton); |
||||
|
} |
||||
|
|
||||
|
addButton() { |
||||
|
this.mapActionButtonsForm.push(this.fb.control(defaultMapActionButtonSettings)); |
||||
|
} |
||||
|
|
||||
|
removeButton(index: number) { |
||||
|
this.mapActionButtonsForm.removeAt(index); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue