Merge pull request #8880 from kalutkaz/filterRuleNode_CE

Updated components that using in enrichment/filter rule nodes
This commit is contained in:
Igor Kulikov 2023-08-14 16:37:03 +03:00 committed by GitHub
commit 6ea888d143
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 206 additions and 144 deletions

View File

@ -15,49 +15,47 @@
limitations under the License. limitations under the License.
--> -->
<div class="tb-relation-filters" [formGroup]="relationFiltersFormGroup"> <div class="tb-form-panel no-border no-padding">
<div class="container"> <div class="tb-form-table" [formGroup]="relationFiltersFormGroup">
<div class="body" [fxShow]="relationFiltersFormArray.length"> <div class="tb-form-table-header">
<div style="margin-bottom: 10px;" class="row" fxFlex fxLayout="row" <div class="tb-form-table-header-cell flex-50">{{ 'relation.type' | translate }}</div>
fxLayoutAlign="start center" formArrayName="relationFilters" <div class="tb-form-table-header-cell flex-50">{{ 'entity.entity-types' | translate }}</div>
<div class="tb-form-table-header-cell actions-header"></div>
</div>
<div class="tb-form-table-body" formArrayName="relationFilters">
<div class="tb-form-table-row"
*ngFor="let relationFilterControl of relationFiltersFormArray.controls; let $index = index"> *ngFor="let relationFilterControl of relationFiltersFormArray.controls; let $index = index">
<div fxFlex fxLayout="row" fxLayoutAlign="start center"> <tb-relation-type-autocomplete subscriptSizing="dynamic"
<div class="input-block" fxFlex fxLayout="row" fxLayoutGap="28px" fxLayoutAlign="start center" style="margin-right: 8px"> class="flex-50"
<tb-relation-type-autocomplete fxFlex="50" [additionalClasses]="['tb-inline-field']"
subscriptSizing="dynamic" appearance="outline"
style="min-width: 200px;"
[formControl]="relationFilterControl.get('relationType')"> [formControl]="relationFilterControl.get('relationType')">
</tb-relation-type-autocomplete> </tb-relation-type-autocomplete>
<tb-entity-type-list fxFlex="50" <tb-entity-type-list class="entity-type-list flex-50" subscriptSizing="dynamic" appearance="outline"
subscriptSizing="dynamic" [additionalClasses]="['tb-inline-field', 'tb-chips', 'flex']"
[label]="'entity.entity-types' | translate" filledInputPlaceholder="{{ 'entity.add-entity-type' | translate }}"
floatLabel="always"
[allowedEntityTypes]="allowedEntityTypes" [allowedEntityTypes]="allowedEntityTypes"
[formControl]="relationFilterControl.get('entityTypes')"> [formControl]="relationFilterControl.get('entityTypes')">
</tb-entity-type-list> </tb-entity-type-list>
</div> <div class="tb-form-table-row-cell-buttons">
<button mat-icon-button color="primary" <button type="button"
type="button" mat-icon-button
(click)="removeFilter($index)" (click)="removeFilter($index)"
[disabled]="isLoading$ | async" [disabled]="isLoading$ | async"
matTooltip="{{ 'relation.remove-relation-filter' | translate }}" matTooltip="{{ 'relation.remove-filter' | translate }}"
matTooltipPosition="above"> matTooltipPosition="above">
<mat-icon>close</mat-icon> <mat-icon>delete</mat-icon>
</button> </button>
</div> </div>
</div> </div>
<div [fxShow]="!relationFiltersFormArray.length">
<span fxLayoutAlign="center center" class="tb-prompt" translate>relation.any-relation</span>
</div> </div>
<div class="any-filter" [fxShow]="!relationFiltersFormArray.length">
<span fxLayoutAlign="center center"
class="tb-prompt" translate>relation.any-relation</span>
</div> </div>
<button mat-stroked-button color="primary" </div>
class="add-button" <div>
[disabled]="isLoading$ | async" <button type="button" mat-stroked-button color="primary" (click)="addFilter()">
(click)="addFilter()" {{ 'filter.add' | translate }}
type="button"
matTooltip="{{ 'relation.add-relation-filter' | translate }}"
matTooltipPosition="above">
{{ 'relation.add-relation-filter' | translate }}
</button> </button>
</div> </div>
</div>

View File

@ -14,43 +14,15 @@
* limitations under the License. * limitations under the License.
*/ */
:host { :host {
.tb-relation-filters { .flex-50 {
max-width: calc(100vw - 48px); flex: 1 1 50%;
margin-top: 2px;
overflow: hidden;
.container{
width: 100%;
} }
.map-label { .actions-header {
font-weight: 400; width: 40px
font-size: 12px;
} }
.body { .entity-type-list {
max-height: 363px; display: flex;
overflow: auto;
.row {
padding-top: 5px;
.input-block {
border: 1px solid #E0E0E0;
width: 100%;
border-radius: 6px;
padding: 24px;
align-items: center;
}
}
}
.any-filter{
margin: 10px 0 20px;
}
.add-button {
margin: 5px 0px 15px;
}
} }
} }

View File

@ -15,7 +15,7 @@
limitations under the License. limitations under the License.
--> -->
<div [formGroup]="keyRowFormGroup" class="tb-form-table-row tb-data-key-row"> <div [formGroup]="keyRowFormGroup" class="tb-form-table-row tb-data-key-row no-padding-right">
<mat-form-field *ngIf="hasAdditionalLatestDataKeys" class="tb-inline-field tb-source-field" appearance="outline" subscriptSizing="dynamic"> <mat-form-field *ngIf="hasAdditionalLatestDataKeys" class="tb-inline-field tb-source-field" appearance="outline" subscriptSizing="dynamic">
<mat-select formControlName="latest"> <mat-select formControlName="latest">
<mat-option [value]="false">{{ 'datakey.timeseries' | translate }}</mat-option> <mat-option [value]="false">{{ 'datakey.timeseries' | translate }}</mat-option>

View File

@ -18,7 +18,7 @@
<div class="tb-form-panel"> <div class="tb-form-panel">
<div class="tb-form-panel-title">{{ panelTitle }}</div> <div class="tb-form-panel-title">{{ panelTitle }}</div>
<div class="tb-form-table"> <div class="tb-form-table">
<div class="tb-form-table-header"> <div class="tb-form-table-header no-padding-right">
<div *ngIf="hasAdditionalLatestDataKeys" class="tb-form-table-header-cell tb-source-header" translate>datakey.source</div> <div *ngIf="hasAdditionalLatestDataKeys" class="tb-form-table-header-cell tb-source-header" translate>datakey.source</div>
<div class="tb-form-table-header-cell tb-key-header" translate>datakey.key</div> <div class="tb-form-table-header-cell tb-key-header" translate>datakey.key</div>
<div class="tb-form-table-header-cell tb-label-header" translate>datakey.label</div> <div class="tb-form-table-header-cell tb-label-header" translate>datakey.label</div>
@ -32,7 +32,7 @@
[cdkDropListDisabled]="!dragEnabled" [cdkDropListDisabled]="!dragEnabled"
(cdkDropListDropped)="keyDrop($event)"> (cdkDropListDropped)="keyDrop($event)">
<div cdkDrag [cdkDragDisabled]="!dragEnabled" <div cdkDrag [cdkDragDisabled]="!dragEnabled"
class="tb-form-table-row tb-draggable" class="tb-form-table-row tb-draggable no-padding-right"
*ngFor="let keyControl of keysFormArray().controls; trackBy: trackByKey; let $index = index;"> *ngFor="let keyControl of keysFormArray().controls; trackBy: trackByKey; let $index = index;">
<tb-data-key-row fxFlex <tb-data-key-row fxFlex
[formControl]="keyControl" [formControl]="keyControl"

View File

@ -20,6 +20,10 @@
min-width: 750px !important; min-width: 750px !important;
max-width: 750px !important; max-width: 750px !important;
.mat-mdc-dialog-content {
padding: 8px;
}
@media #{$mat-lt-md} { @media #{$mat-lt-md} {
min-width: 100% !important; min-width: 100% !important;
} }

View File

@ -27,10 +27,7 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 8px; gap: 8px;
margin-bottom: 22px;
@media #{$mat-gt-sm} {
margin-bottom: 24px;
}
} }
@media #{$mat-gt-sm} { @media #{$mat-gt-sm} {

View File

@ -15,7 +15,8 @@
limitations under the License. limitations under the License.
--> -->
<mat-form-field [formGroup]="entitySubtypeListFormGroup" [ngClass]="label ? '' : 'tb-chip-list'" class="mat-block"> <mat-form-field [formGroup]="entitySubtypeListFormGroup" [floatLabel]="floatLabel"
[class]="label ? '' : 'tb-chip-list'" class="mat-block">
<mat-label *ngIf="label">{{ label }}</mat-label> <mat-label *ngIf="label">{{ label }}</mat-label>
<mat-chip-grid #chipList formControlName="entitySubtypeList"> <mat-chip-grid #chipList formControlName="entitySubtypeList">
<mat-chip-row <mat-chip-row
@ -38,13 +39,6 @@
[matChipInputSeparatorKeyCodes]="separatorKeysCodes" [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
(matChipInputTokenEnd)="chipAdd($event)"> (matChipInputTokenEnd)="chipAdd($event)">
</mat-chip-grid> </mat-chip-grid>
<button *ngIf="entitySubtypeListFormGroup.get('entitySubtypeList').value?.length > 1"
type="button"
matTooltip="{{'entity.clear-selected-profiles' | translate}}"
matSuffix mat-icon-button
(click)="clearChipGrid()">
<mat-icon color="primary" class="material-icons">close</mat-icon>
</button>
<mat-autocomplete #entitySubtypeAutocomplete="matAutocomplete" <mat-autocomplete #entitySubtypeAutocomplete="matAutocomplete"
class="tb-autocomplete" class="tb-autocomplete"
(optionSelected)="selected($event)" (optionSelected)="selected($event)"
@ -58,6 +52,9 @@
</span> </span>
</mat-option> </mat-option>
</mat-autocomplete> </mat-autocomplete>
<div matSuffix>
<ng-content select="[additionalButtons]"></ng-content>
</div>
<mat-error *ngIf="entitySubtypeListFormGroup.get('entitySubtypeList').hasError('required')"> <mat-error *ngIf="entitySubtypeListFormGroup.get('entitySubtypeList').hasError('required')">
{{ subtypeListEmptyText | translate }} {{ subtypeListEmptyText | translate }}
</mat-error> </mat-error>

View File

@ -15,7 +15,7 @@
/// ///
import { AfterViewInit, Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { AfterViewInit, Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import { Observable, Subscription, throwError } from 'rxjs'; import { Observable, Subscription, throwError } from 'rxjs';
import { map, mergeMap, publishReplay, refCount, share } from 'rxjs/operators'; import { map, mergeMap, publishReplay, refCount, share } from 'rxjs/operators';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
@ -23,14 +23,15 @@ import { AppState } from '@app/core/core.state';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { EntitySubtype, EntityType } from '@shared/models/entity-type.models'; import { EntitySubtype, EntityType } from '@shared/models/entity-type.models';
import { MatAutocomplete, MatAutocompleteSelectedEvent } from '@angular/material/autocomplete'; import { MatAutocomplete, MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { MatChipInputEvent, MatChipGrid } from '@angular/material/chips'; import { MatChipGrid, MatChipInputEvent } from '@angular/material/chips';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { AssetService } from '@core/http/asset.service'; import { AssetService } from '@core/http/asset.service';
import { DeviceService } from '@core/http/device.service'; import { DeviceService } from '@core/http/device.service';
import { EdgeService } from '@core/http/edge.service'; import { EdgeService } from '@core/http/edge.service';
import { EntityViewService } from '@core/http/entity-view.service'; import { EntityViewService } from '@core/http/entity-view.service';
import { BroadcastService } from '@core/services/broadcast.service'; import { BroadcastService } from '@core/services/broadcast.service';
import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes'; import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes';
import { coerceBoolean } from '@shared/decorators/coercion';
import { FloatLabelType } from '@angular/material/form-field';
@Component({ @Component({
selector: 'tb-entity-subtype-list', selector: 'tb-entity-subtype-list',
@ -46,32 +47,43 @@ import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes';
}) })
export class EntitySubTypeListComponent implements ControlValueAccessor, OnInit, AfterViewInit, OnDestroy { export class EntitySubTypeListComponent implements ControlValueAccessor, OnInit, AfterViewInit, OnDestroy {
entitySubtypeListFormGroup: UntypedFormGroup; entitySubtypeListFormGroup: FormGroup;
modelValue: Array<string> | null; modelValue: Array<string> | null;
private requiredValue: boolean; private requiredValue: boolean;
get required(): boolean { get required(): boolean {
return this.requiredValue; return this.requiredValue;
} }
@Input() label: string;
@Input() @Input()
@coerceBoolean()
set required(value: boolean) { set required(value: boolean) {
const newVal = coerceBooleanProperty(value); if (this.requiredValue !== value) {
if (this.requiredValue !== newVal) { this.requiredValue = value;
this.requiredValue = newVal;
this.updateValidators(); this.updateValidators();
} }
} }
@Input()
floatLabel: FloatLabelType = 'auto';
@Input()
label: string;
@Input() @Input()
disabled: boolean; disabled: boolean;
@Input() @Input()
entityType: EntityType; entityType: EntityType;
@Input()
emptyInputPlaceholder: string;
@Input()
filledInputPlaceholder: string;
@ViewChild('entitySubtypeInput') entitySubtypeInput: ElementRef<HTMLInputElement>; @ViewChild('entitySubtypeInput') entitySubtypeInput: ElementRef<HTMLInputElement>;
@ViewChild('entitySubtypeAutocomplete') entitySubtypeAutocomplete: MatAutocomplete; @ViewChild('entitySubtypeAutocomplete') entitySubtypeAutocomplete: MatAutocomplete;
@ViewChild('chipList', {static: true}) chipList: MatChipGrid; @ViewChild('chipList', {static: true}) chipList: MatChipGrid;
@ -102,13 +114,14 @@ export class EntitySubTypeListComponent implements ControlValueAccessor, OnInit,
private deviceService: DeviceService, private deviceService: DeviceService,
private edgeService: EdgeService, private edgeService: EdgeService,
private entityViewService: EntityViewService, private entityViewService: EntityViewService,
private fb: UntypedFormBuilder) { private fb: FormBuilder) {
this.entitySubtypeListFormGroup = this.fb.group({ this.entitySubtypeListFormGroup = this.fb.group({
entitySubtypeList: [this.entitySubtypeList, this.required ? [Validators.required] : []], entitySubtypeList: [this.entitySubtypeList, this.required ? [Validators.required] : []],
entitySubtype: [null] entitySubtype: [null]
}); });
} }
updateValidators() { updateValidators() {
this.entitySubtypeListFormGroup.get('entitySubtypeList').setValidators(this.required ? [Validators.required] : []); this.entitySubtypeListFormGroup.get('entitySubtypeList').setValidators(this.required ? [Validators.required] : []);
this.entitySubtypeListFormGroup.get('entitySubtypeList').updateValueAndValidity(); this.entitySubtypeListFormGroup.get('entitySubtypeList').updateValueAndValidity();
@ -122,7 +135,6 @@ export class EntitySubTypeListComponent implements ControlValueAccessor, OnInit,
} }
ngOnInit() { ngOnInit() {
switch (this.entityType) { switch (this.entityType) {
case EntityType.ASSET: case EntityType.ASSET:
this.placeholder = this.required ? this.translate.instant('asset.enter-asset-type') this.placeholder = this.required ? this.translate.instant('asset.enter-asset-type')
@ -166,6 +178,13 @@ export class EntitySubTypeListComponent implements ControlValueAccessor, OnInit,
break; break;
} }
if (this.emptyInputPlaceholder) {
this.placeholder = this.emptyInputPlaceholder;
}
if (this.filledInputPlaceholder) {
this.secondaryPlaceholder = this.filledInputPlaceholder;
}
this.filteredEntitySubtypeList = this.entitySubtypeListFormGroup.get('entitySubtype').valueChanges this.filteredEntitySubtypeList = this.entitySubtypeListFormGroup.get('entitySubtype').valueChanges
.pipe( .pipe(
map(value => value ? value : ''), map(value => value ? value : ''),
@ -225,13 +244,6 @@ export class EntitySubTypeListComponent implements ControlValueAccessor, OnInit,
} }
this.clear(''); this.clear('');
} }
clearChipGrid() {
this.entitySubtypeList = [];
this.modelValue = null;
this.entitySubtypeListFormGroup.get('entitySubtypeList').patchValue([], {emitEvent: true});
}
remove(entitySubtype: string) { remove(entitySubtype: string) {
const index = this.entitySubtypeList.indexOf(entitySubtype); const index = this.entitySubtypeList.indexOf(entitySubtype);
if (index >= 0) { if (index >= 0) {

View File

@ -15,7 +15,11 @@
limitations under the License. limitations under the License.
--> -->
<mat-form-field [formGroup]="entityTypeListFormGroup" class="mat-block" [floatLabel]="floatLabel" subscriptSizing="{{ subscriptSizing }}"> <mat-form-field [formGroup]="entityTypeListFormGroup"
[appearance]="appearance"
class="mat-block" [class]="additionalClasses"
[floatLabel]="floatLabel"
subscriptSizing="{{ subscriptSizing }}">
<mat-label *ngIf="label"> {{ label }}</mat-label> <mat-label *ngIf="label"> {{ label }}</mat-label>
<mat-chip-grid #chipList formControlName="entityTypeList"> <mat-chip-grid #chipList formControlName="entityTypeList">
<mat-chip-row <mat-chip-row
@ -48,6 +52,9 @@
</span> </span>
</mat-option> </mat-option>
</mat-autocomplete> </mat-autocomplete>
<div matSuffix>
<ng-content select="[matSuffix]"></ng-content>
</div>
<mat-error *ngIf="entityTypeListFormGroup.get('entityTypeList').hasError('required')"> <mat-error *ngIf="entityTypeListFormGroup.get('entityTypeList').hasError('required')">
{{ 'entity.entity-type-list-empty' | translate }} {{ 'entity.entity-type-list-empty' | translate }}
</mat-error> </mat-error>

View File

@ -15,7 +15,7 @@
/// ///
import { AfterViewInit, Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; import { AfterViewInit, Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core';
import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Observable, of } from 'rxjs'; import { Observable, of } from 'rxjs';
import { filter, map, mergeMap, share, tap } from 'rxjs/operators'; import { filter, map, mergeMap, share, tap } from 'rxjs/operators';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
@ -25,9 +25,8 @@ import { AliasEntityType, EntityType, entityTypeTranslations } from '@shared/mod
import { EntityService } from '@core/http/entity.service'; import { EntityService } from '@core/http/entity.service';
import { MatAutocomplete } from '@angular/material/autocomplete'; import { MatAutocomplete } from '@angular/material/autocomplete';
import { MatChipGrid } from '@angular/material/chips'; import { MatChipGrid } from '@angular/material/chips';
import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { FloatLabelType, MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form-field';
import { FloatLabelType, SubscriptSizing } from '@angular/material/form-field'; import { coerceArray, coerceBoolean } from '@shared/decorators/coercion';
import { coerceBoolean } from '@shared/decorators/coercion';
interface EntityTypeInfo { interface EntityTypeInfo {
name: string; name: string;
@ -48,7 +47,7 @@ interface EntityTypeInfo {
}) })
export class EntityTypeListComponent implements ControlValueAccessor, OnInit, AfterViewInit { export class EntityTypeListComponent implements ControlValueAccessor, OnInit, AfterViewInit {
entityTypeListFormGroup: UntypedFormGroup; entityTypeListFormGroup: FormGroup;
modelValue: Array<EntityType> | null; modelValue: Array<EntityType> | null;
@ -57,19 +56,28 @@ export class EntityTypeListComponent implements ControlValueAccessor, OnInit, Af
return this.requiredValue; return this.requiredValue;
} }
@Input() label: string;
@Input() floatLabel: FloatLabelType = 'auto';
@Input() @Input()
@coerceBoolean()
set required(value: boolean) { set required(value: boolean) {
const newVal = coerceBooleanProperty(value); if (this.requiredValue !== value) {
if (this.requiredValue !== newVal) { this.requiredValue = value;
this.requiredValue = newVal;
this.updateValidators(); this.updateValidators();
} }
} }
@Input()
@coerceArray()
additionalClasses: Array<string>;
@Input()
appearance: MatFormFieldAppearance = 'fill';
@Input()
label: string;
@Input()
floatLabel: FloatLabelType = 'auto';
@Input() @Input()
disabled: boolean; disabled: boolean;
@ -79,6 +87,12 @@ export class EntityTypeListComponent implements ControlValueAccessor, OnInit, Af
@Input() @Input()
allowedEntityTypes: Array<EntityType | AliasEntityType>; allowedEntityTypes: Array<EntityType | AliasEntityType>;
@Input()
emptyInputPlaceholder: string;
@Input()
filledInputPlaceholder: string;
@Input() @Input()
@coerceBoolean() @coerceBoolean()
ignoreAuthorityFilter: boolean; ignoreAuthorityFilter: boolean;
@ -103,7 +117,7 @@ export class EntityTypeListComponent implements ControlValueAccessor, OnInit, Af
constructor(private store: Store<AppState>, constructor(private store: Store<AppState>,
public translate: TranslateService, public translate: TranslateService,
private entityService: EntityService, private entityService: EntityService,
private fb: UntypedFormBuilder) { private fb: FormBuilder) {
this.entityTypeListFormGroup = this.fb.group({ this.entityTypeListFormGroup = this.fb.group({
entityTypeList: [this.entityTypeList, this.required ? [Validators.required] : []], entityTypeList: [this.entityTypeList, this.required ? [Validators.required] : []],
entityType: [null] entityType: [null]
@ -123,11 +137,17 @@ export class EntityTypeListComponent implements ControlValueAccessor, OnInit, Af
} }
ngOnInit() { ngOnInit() {
if (this.emptyInputPlaceholder) {
this.placeholder = this.required ? this.translate.instant('entity.enter-entity-type') this.placeholder = this.emptyInputPlaceholder;
: this.translate.instant('entity.any-entity'); } else {
this.placeholder = this.required ? this.translate.instant('entity.enter-entity-type') :
this.translate.instant('entity.any-entity');
}
if (this.filledInputPlaceholder) {
this.secondaryPlaceholder = this.filledInputPlaceholder;
} else {
this.secondaryPlaceholder = '+' + this.translate.instant('entity.entity-type'); this.secondaryPlaceholder = '+' + this.translate.instant('entity.entity-type');
}
let entityTypes: Array<EntityType | AliasEntityType>; let entityTypes: Array<EntityType | AliasEntityType>;
if (this.ignoreAuthorityFilter && this.allowedEntityTypes if (this.ignoreAuthorityFilter && this.allowedEntityTypes
&& this.allowedEntityTypes.length) { && this.allowedEntityTypes.length) {
@ -250,5 +270,4 @@ export class EntityTypeListComponent implements ControlValueAccessor, OnInit, Af
this.entityTypeInput.nativeElement.focus(); this.entityTypeInput.nativeElement.focus();
}, 0); }, 0);
} }
} }

View File

@ -30,19 +30,21 @@
</button> </button>
</div> </div>
</fieldset> </fieldset>
<fieldset class="tb-help-popup-button-container" *ngIf="textMode"> <fieldset class="tb-help-popup-button-container" [class.hint-button]="hintMode" *ngIf="textMode">
<div #toggleHelpTextButton <div #toggleHelpTextButton
(click)="toggleHelp()"> (click)="toggleHelp()">
<button mat-button <button mat-button
color="primary" color="primary"
class="tb-help-popup-text-button" class="tb-help-popup-text-button"
[ngClass]="{'mat-mdc-outlined-button mdc-button--outlined': popoverVisible && popoverReady}"> [class]="{'mat-mdc-outlined-button mdc-button--outlined': popoverVisible && popoverReady,
'hint-button': hintMode}">
<span> <span>
<ng-container *ngIf="triggerSafeHtml"> <ng-container *ngIf="triggerSafeHtml">
<span [style]="triggerStyle" [innerHTML]="triggerSafeHtml"></span> <span [style]="triggerStyle" [innerHTML]="triggerSafeHtml"></span>
</ng-container> </ng-container>
<mat-icon *ngIf="!popoverVisible || popoverReady" class="tb-mat-16">open_in_new</mat-icon> <mat-icon *ngIf="(!popoverVisible || popoverReady)"
<mat-spinner *ngIf="popoverVisible && !popoverReady" mode="indeterminate" diameter="16" strokeWidth="2"></mat-spinner> [ngClass]="hintMode ? 'tb-mat-12' : 'tb-mat-16'">open_in_new</mat-icon>
<mat-spinner *ngIf="popoverVisible && !popoverReady" mode="indeterminate" [diameter]="hintMode ? 12 : 16" strokeWidth="2"></mat-spinner>
</span> </span>
</button> </button>
</div> </div>

View File

@ -17,6 +17,9 @@
width: initial; width: initial;
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
&.hint-button {
line-height: 1;
}
} }
.tb-help-popup-button { .tb-help-popup-button {
@ -65,4 +68,19 @@
vertical-align: middle; vertical-align: middle;
} }
} }
&.hint-button {
padding: 2px 3px;
line-height: 1;
&.mat-mdc-outlined-button {
padding: 1px 2px;
}
.mdc-button__label > span {
.mat-icon {
margin-right: 0;
}
.mat-mdc-progress-spinner {
margin-right: 0;
}
}
}
} }

View File

@ -28,6 +28,7 @@ import { TbPopoverService } from '@shared/components/popover.service';
import { PopoverPlacement } from '@shared/components/popover.models'; import { PopoverPlacement } from '@shared/components/popover.models';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { isDefinedAndNotNull } from '@core/utils'; import { isDefinedAndNotNull } from '@core/utils';
import { coerceBoolean } from '@shared/decorators/coercion';
@Component({ @Component({
// eslint-disable-next-line @angular-eslint/component-selector // eslint-disable-next-line @angular-eslint/component-selector
@ -62,6 +63,11 @@ export class HelpPopupComponent implements OnChanges, OnDestroy {
popoverVisible = false; popoverVisible = false;
popoverReady = true; popoverReady = true;
@Input()
@coerceBoolean()
hintMode = false;
triggerSafeHtml: SafeHtml = null; triggerSafeHtml: SafeHtml = null;
textMode = false; textMode = false;

View File

@ -15,7 +15,8 @@
limitations under the License. limitations under the License.
--> -->
<mat-form-field [formGroup]="relationTypeFormGroup" [floatLabel]="floatLabel" class="mat-block" subscriptSizing="{{ subscriptSizing }}"> <mat-form-field [formGroup]="relationTypeFormGroup" [appearance]="appearance" [floatLabel]="floatLabel"
class="mat-block" [class]="additionalClasses" subscriptSizing="{{ subscriptSizing }}">
<mat-label *ngIf="label">{{ label }}</mat-label> <mat-label *ngIf="label">{{ label }}</mat-label>
<input matInput type="text" <input matInput type="text"
#relationTypeInput #relationTypeInput

View File

@ -24,7 +24,8 @@ import { TranslateService } from '@ngx-translate/core';
import { BroadcastService } from '@app/core/services/broadcast.service'; import { BroadcastService } from '@app/core/services/broadcast.service';
import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { RelationTypes } from '@app/shared/models/relation.models'; import { RelationTypes } from '@app/shared/models/relation.models';
import { FloatLabelType, SubscriptSizing } from '@angular/material/form-field'; import { FloatLabelType, MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form-field';
import { coerceArray, coerceBoolean } from '@shared/decorators/coercion';
@Component({ @Component({
selector: 'tb-relation-type-autocomplete', selector: 'tb-relation-type-autocomplete',
@ -42,19 +43,22 @@ export class RelationTypeAutocompleteComponent implements ControlValueAccessor,
modelValue: string | null; modelValue: string | null;
private requiredValue: boolean; @Input()
get required(): boolean { label: string;
return this.requiredValue;
}
@Input() label: string;
@Input() floatLabel: FloatLabelType = 'auto';
@Input() @Input()
set required(value: boolean) { @coerceArray()
this.requiredValue = coerceBooleanProperty(value); additionalClasses: Array<string>;
}
@Input()
appearance: MatFormFieldAppearance = 'fill';
@Input()
floatLabel: FloatLabelType = 'auto';
@Input()
@coerceBoolean()
required: boolean;
@Input() @Input()
disabled: boolean; disabled: boolean;

View File

@ -54,7 +54,15 @@
{{ 'common.not-found' | translate }} {{ 'common.not-found' | translate }}
</mat-option> </mat-option>
</mat-autocomplete> </mat-autocomplete>
<mat-hint>{{ hint }}</mat-hint> <mat-hint [hidden]="!hint">
{{ hint }}
</mat-hint>
<mat-hint align="end" [hidden]="!matHintEnd.children.length" #matHintEnd>
<ng-content select="[matHintEnd]"></ng-content>
</mat-hint>
<div matSuffix>
<ng-content select="[matSuffix]"></ng-content>
</div>
<mat-error *ngIf="stringItemsForm.get('items').hasError('required')"> <mat-error *ngIf="stringItemsForm.get('items').hasError('required')">
{{ requiredText }} {{ requiredText }}
</mat-error> </mat-error>

View File

@ -2044,6 +2044,7 @@
"entity-types": "Entity types", "entity-types": "Entity types",
"entity-type-list": "Entity type list", "entity-type-list": "Entity type list",
"any-entity": "Any entity", "any-entity": "Any entity",
"add-entity-type": "Add entity type",
"enter-entity-type": "Enter entity type", "enter-entity-type": "Enter entity type",
"no-entities-matching": "No entities matching '{{entity}}' were found.", "no-entities-matching": "No entities matching '{{entity}}' were found.",
"no-entity-types-matching": "No entity types matching '{{entityType}}' were found.", "no-entity-types-matching": "No entity types matching '{{entityType}}' were found.",
@ -3338,6 +3339,7 @@
"delete-from-relations-title": "Are you sure you want to delete { count, plural, =1 {1 relation} other {# relations} }?", "delete-from-relations-title": "Are you sure you want to delete { count, plural, =1 {1 relation} other {# relations} }?",
"delete-from-relations-text": "Be careful, after the confirmation all selected relations will be removed and current entity will be unrelated from the corresponding entities.", "delete-from-relations-text": "Be careful, after the confirmation all selected relations will be removed and current entity will be unrelated from the corresponding entities.",
"remove-relation-filter": "Remove relation filter", "remove-relation-filter": "Remove relation filter",
"remove-filter": "Remove filter",
"add-relation-filter": "Add relation filter", "add-relation-filter": "Add relation filter",
"any-relation": "Any relation", "any-relation": "Any relation",
"relation-filters": "Relation filters", "relation-filters": "Relation filters",

View File

@ -307,6 +307,10 @@
} }
} }
&.tb-chips { &.tb-chips {
&.flex {
flex: 1;
width: auto;
}
.mat-mdc-text-field-wrapper { .mat-mdc-text-field-wrapper {
&.mdc-text-field--outlined, &:not(.mdc-text-field--outlined) { &.mdc-text-field--outlined, &:not(.mdc-text-field--outlined) {
.mat-mdc-form-field-infix { .mat-mdc-form-field-infix {
@ -357,7 +361,7 @@
} }
.tb-prompt { .tb-prompt {
height: 38px; height: 40px;
} }
} }
@ -366,11 +370,16 @@
flex-direction: row; flex-direction: row;
gap: 8px; gap: 8px;
padding-left: 8px; padding-left: 8px;
padding-right: 8px;
place-content: center flex-start; place-content: center flex-start;
align-items: center; align-items: center;
&.no-padding-right {
padding-right: 0;
}
@media #{$mat-gt-md} { @media #{$mat-gt-md} {
gap: 12px; gap: 12px;
padding-left: 12px; padding-left: 12px;
padding-right: 12px;
} }
&-cell { &-cell {
font-weight: 400; font-weight: 400;

View File

@ -855,6 +855,9 @@ mat-label {
svg { svg {
vertical-align: inherit; vertical-align: inherit;
} }
&.tb-mat-12 {
@include tb-mat-icon-size(12);
}
&.tb-mat-16 { &.tb-mat-16 {
@include tb-mat-icon-size(16); @include tb-mat-icon-size(16);
} }
@ -1208,4 +1211,7 @@ mat-label {
color: inherit; color: inherit;
} }
.cursor-pointer {
cursor: pointer;
}
} }