UI: Error icon with tooltip for toggle select
This commit is contained in:
parent
052bfc09dd
commit
d3e20161c9
@ -41,10 +41,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<tb-toggle-select [(ngModel)]="dataLayerMode"
|
<tb-toggle-select [(ngModel)]="dataLayerMode"
|
||||||
[ngModelOptions]="{ standalone: true }">
|
[ngModelOptions]="{ standalone: true }">
|
||||||
<tb-toggle-option *ngIf="trip" value="trips">{{ 'widgets.maps.overlays.trips' | translate }}</tb-toggle-option>
|
<tb-toggle-option *ngIf="trip" value="trips" [error]="mapSettingsFormGroup.get('trips').invalid" errorText="widgets.maps.overlays.required-fields">{{ 'widgets.maps.overlays.trips' | translate }}</tb-toggle-option>
|
||||||
<tb-toggle-option value="markers">{{ 'widgets.maps.overlays.markers' | translate }}</tb-toggle-option>
|
<tb-toggle-option value="markers" [error]="mapSettingsFormGroup.get('markers').invalid" errorText="widgets.maps.overlays.required-fields">{{ 'widgets.maps.overlays.markers' | translate }}</tb-toggle-option>
|
||||||
<tb-toggle-option value="polygons">{{ 'widgets.maps.overlays.polygons' | translate }}</tb-toggle-option>
|
<tb-toggle-option value="polygons" [error]="mapSettingsFormGroup.get('polygons').invalid" errorText="widgets.maps.overlays.required-fields">{{ 'widgets.maps.overlays.polygons' | translate }}</tb-toggle-option>
|
||||||
<tb-toggle-option value="circles">{{ 'widgets.maps.overlays.circles' | translate }}</tb-toggle-option>
|
<tb-toggle-option value="circles" [error]="mapSettingsFormGroup.get('circles').invalid" errorText="widgets.maps.overlays.required-fields">{{ 'widgets.maps.overlays.circles' | translate }}</tb-toggle-option>
|
||||||
</tb-toggle-select>
|
</tb-toggle-select>
|
||||||
</div>
|
</div>
|
||||||
<tb-map-data-layers *ngIf="trip"
|
<tb-map-data-layers *ngIf="trip"
|
||||||
|
|||||||
@ -41,7 +41,16 @@
|
|||||||
[name]="name"
|
[name]="name"
|
||||||
[(ngModel)]="value"
|
[(ngModel)]="value"
|
||||||
(ngModelChange)="valueChange.emit(value)">
|
(ngModelChange)="valueChange.emit(value)">
|
||||||
<mat-button-toggle *ngFor="let option of options; trackBy: trackByHeaderOption" [value]="option.value" [disabled]="disabled">{{ option.name }}</mat-button-toggle>
|
<mat-button-toggle *ngFor="let option of options; trackBy: trackByHeaderOption" [value]="option.value" [disabled]="disabled">
|
||||||
|
{{ option.name }}
|
||||||
|
<mat-icon matTooltipPosition="above"
|
||||||
|
matTooltipClass="tb-error-tooltip"
|
||||||
|
[matTooltip]="option.errorText | translate"
|
||||||
|
*ngIf="option.error && value !== option.value"
|
||||||
|
class="tb-error tb-error-icon">
|
||||||
|
warning
|
||||||
|
</mat-icon>
|
||||||
|
</mat-button-toggle>
|
||||||
</mat-button-toggle-group>
|
</mat-button-toggle-group>
|
||||||
</div>
|
</div>
|
||||||
<button mat-icon-button
|
<button mat-icon-button
|
||||||
|
|||||||
@ -43,6 +43,12 @@
|
|||||||
.tb-toggle-header {
|
.tb-toggle-header {
|
||||||
transition: transform 500ms cubic-bezier(0.35, 0, 0.25, 1);
|
transition: transform 500ms cubic-bezier(0.35, 0, 0.25, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tb-error-icon {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:host ::ng-deep {
|
:host ::ng-deep {
|
||||||
|
|||||||
@ -16,7 +16,8 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
AfterContentChecked,
|
AfterContentChecked,
|
||||||
AfterContentInit, AfterViewChecked,
|
AfterContentInit,
|
||||||
|
AfterViewChecked,
|
||||||
AfterViewInit,
|
AfterViewInit,
|
||||||
ChangeDetectorRef,
|
ChangeDetectorRef,
|
||||||
Component,
|
Component,
|
||||||
@ -25,11 +26,14 @@ import {
|
|||||||
ElementRef,
|
ElementRef,
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
HostBinding,
|
HostBinding,
|
||||||
Input, NgZone,
|
Input,
|
||||||
|
NgZone,
|
||||||
|
OnChanges,
|
||||||
OnDestroy,
|
OnDestroy,
|
||||||
OnInit,
|
OnInit,
|
||||||
Output,
|
Output,
|
||||||
QueryList,
|
QueryList,
|
||||||
|
SimpleChanges,
|
||||||
ViewChild
|
ViewChild
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { PageComponent } from '@shared/components/page.component';
|
import { PageComponent } from '@shared/components/page.component';
|
||||||
@ -42,10 +46,13 @@ import { coerceBoolean } from '@shared/decorators/coercion';
|
|||||||
import { startWith, takeUntil } from 'rxjs/operators';
|
import { startWith, takeUntil } from 'rxjs/operators';
|
||||||
import { Platform } from '@angular/cdk/platform';
|
import { Platform } from '@angular/cdk/platform';
|
||||||
import { MatButtonToggle, MatButtonToggleGroup } from '@angular/material/button-toggle';
|
import { MatButtonToggle, MatButtonToggleGroup } from '@angular/material/button-toggle';
|
||||||
|
import { isDefinedAndNotNull } from '@core/utils';
|
||||||
|
|
||||||
export interface ToggleHeaderOption {
|
export interface ToggleHeaderOption {
|
||||||
name: string;
|
name: string;
|
||||||
value: any;
|
value: any;
|
||||||
|
error?: boolean;
|
||||||
|
errorText?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ToggleHeaderAppearance = 'fill' | 'fill-invert' | 'stroked';
|
export type ToggleHeaderAppearance = 'fill' | 'fill-invert' | 'stroked';
|
||||||
@ -59,10 +66,16 @@ export type ScrollDirection = 'after' | 'before';
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
// eslint-disable-next-line @angular-eslint/directive-class-suffix
|
// eslint-disable-next-line @angular-eslint/directive-class-suffix
|
||||||
export class ToggleOption {
|
export class ToggleOption implements OnChanges {
|
||||||
|
|
||||||
@Input() value: any;
|
@Input() value: any;
|
||||||
|
|
||||||
|
@Input() error: boolean;
|
||||||
|
|
||||||
|
@Input() errorText: any;
|
||||||
|
|
||||||
|
@Output() errorChange = new EventEmitter<boolean>();
|
||||||
|
|
||||||
get viewValue(): string {
|
get viewValue(): string {
|
||||||
return (this._element?.nativeElement.textContent || '').trim();
|
return (this._element?.nativeElement.textContent || '').trim();
|
||||||
}
|
}
|
||||||
@ -70,6 +83,14 @@ export class ToggleOption {
|
|||||||
constructor(
|
constructor(
|
||||||
private _element: ElementRef<HTMLElement>
|
private _element: ElementRef<HTMLElement>
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
|
if (changes['error']) {
|
||||||
|
if (!changes['error'].firstChange && changes['error'].currentValue !== changes['error'].previousValue) {
|
||||||
|
this.errorChange.emit(this.error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Directive()
|
@Directive()
|
||||||
@ -88,6 +109,7 @@ export abstract class _ToggleBase extends PageComponent implements AfterContentI
|
|||||||
|
|
||||||
ngAfterContentInit(): void {
|
ngAfterContentInit(): void {
|
||||||
this.toggleOptions.changes.pipe(startWith(null), takeUntil(this._destroyed)).subscribe(() => {
|
this.toggleOptions.changes.pipe(startWith(null), takeUntil(this._destroyed)).subscribe(() => {
|
||||||
|
this.subscribeToToggleOptions();
|
||||||
this.syncToggleHeaderOptions();
|
this.syncToggleHeaderOptions();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -97,13 +119,26 @@ export abstract class _ToggleBase extends PageComponent implements AfterContentI
|
|||||||
this._destroyed.complete();
|
this._destroyed.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private subscribeToToggleOptions() {
|
||||||
|
this.toggleOptions.forEach(option => {
|
||||||
|
if (isDefinedAndNotNull(option.error) || isDefinedAndNotNull(option.errorText)) {
|
||||||
|
option.errorChange.pipe(takeUntil(this._destroyed)).subscribe(() => {
|
||||||
|
this.syncToggleHeaderOptions();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private syncToggleHeaderOptions() {
|
private syncToggleHeaderOptions() {
|
||||||
if (this.toggleOptions?.length) {
|
if (this.toggleOptions?.length) {
|
||||||
this.options.length = 0;
|
this.options.length = 0;
|
||||||
this.toggleOptions.forEach(option => {
|
this.toggleOptions.forEach(option => {
|
||||||
this.options.push(
|
this.options.push(
|
||||||
{ name: option.viewValue,
|
{
|
||||||
value: option.value
|
name: option.viewValue,
|
||||||
|
value: option.value,
|
||||||
|
error: option.error,
|
||||||
|
errorText: option.errorText
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -7980,7 +7980,8 @@
|
|||||||
"trips": "Trips",
|
"trips": "Trips",
|
||||||
"markers": "Markers",
|
"markers": "Markers",
|
||||||
"polygons": "Polygons",
|
"polygons": "Polygons",
|
||||||
"circles": "Circles"
|
"circles": "Circles",
|
||||||
|
"required-fields": "Required fields are not filled in."
|
||||||
},
|
},
|
||||||
"data-layer": {
|
"data-layer": {
|
||||||
"source": "Source",
|
"source": "Source",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user