UI: Improve multiple attributes input widget.

This commit is contained in:
Igor Kulikov 2023-09-07 18:05:08 +03:00
parent ac6717b99f
commit 244f8239ba
11 changed files with 370 additions and 279 deletions

View File

@ -21,7 +21,18 @@ export class TbMissingTranslationHandler implements MissingTranslationHandler {
handle(params: MissingTranslationHandlerParams) {
if (params.key && !params.key.startsWith(customTranslationsPrefix)) {
console.warn('Translation for \'' + params.key + '\' doesn\'t exist');
params.translateService.set(params.key, params.key);
let translations: any;
const parts = params.key.split('.');
for (let i=parts.length-1; i>=0; i--) {
const newTranslations = {};
if (i === parts.length-1) {
newTranslations[parts[i]] = params.key;
} else {
newTranslations[parts[i]] = translations;
}
translations = newTranslations;
}
params.translateService.setTranslation(params.translateService.currentLang, translations, true);
}
}
}

View File

@ -23,12 +23,11 @@
<fieldset *ngFor="let source of sources" [ngClass]="{'fields-group': settings.showGroupTitle}">
<legend class="group-title" *ngIf="settings.showGroupTitle">{{ getGroupTitle(source.datasource) }}
</legend>
<div class="tb-multiple-input-layout layout-wrap"
[ngClass]="{'vertical-alignment': isVerticalAlignment || changeAlignment}">
<div *ngFor="let key of visibleKeys(source)"
[ngStyle]="{width: (isVerticalAlignment || changeAlignment) ? '100%' : inputWidthSettings}">
<div class="input-field" *ngIf="key.settings.dataKeyValueType === 'string'">
<mat-form-field class="mat-block" appearance="outline" subscriptSizing="dynamic">
<div class="tb-multiple-input-layout"
[style]="{'grid-template-columns': 'repeat(' + columns + ', 1fr)', 'column-gap': settings.columnGap + 'px', 'row-gap': settings.rowGap + 'px'}">
<ng-container *ngFor="let key of visibleKeys(source)">
<mat-form-field *ngIf="key.settings.dataKeyValueType === 'string'"
[appearance]="key.settings.appearance" [subscriptSizing]="key.settings.subscriptSizing">
<mat-label>{{key.label}}</mat-label>
<input matInput
formControlName="{{key.formId}}"
@ -37,20 +36,15 @@
type="text"
(focus)="key.isFocused = true; focusInputElement($event)"
(blur)="key.isFocused = false; inputChanged(source, key)">
<ng-container *ngIf="key.settings.icon || key.settings.safeCustomIcon" matPrefix>
<tb-icon *ngIf="!key.settings.safeCustomIcon; else customToggleIcon">{{key.settings.icon}}</tb-icon>
<ng-template #customToggleIcon>
<img class="mat-icon" [src]="key.settings.safeCustomIcon" alt="icon">
</ng-template>
<ng-container *ngIf="key.settings.icon || key.settings.safeCustomIcon" matIconPrefix>
<ng-container *ngTemplateOutlet="iconPrefix; context: {key: key}"></ng-container>
</ng-container>
<mat-error *ngIf="multipleInputFormGroup.get(key.formId).hasError('required')">
{{ getErrorMessageText(key.settings, 'required') }}
</mat-error>
</mat-form-field>
</div>
<div class="input-field" *ngIf="key.settings.dataKeyValueType === 'double' ||
key.settings.dataKeyValueType === 'integer'">
<mat-form-field class="mat-block" appearance="outline" subscriptSizing="dynamic">
<mat-form-field *ngIf="['double', 'integer'].includes(key.settings.dataKeyValueType)"
[appearance]="key.settings.appearance" [subscriptSizing]="key.settings.subscriptSizing">
<mat-label>{{key.label}}</mat-label>
<input matInput
formControlName="{{key.formId}}"
@ -62,11 +56,8 @@
max="{{key.settings.maxValue}}"
(focus)="key.isFocused = true; focusInputElement($event)"
(blur)="key.isFocused = false; inputChanged(source, key)">
<ng-container *ngIf="key.settings.icon || key.settings.safeCustomIcon" matPrefix>
<tb-icon *ngIf="!key.settings.safeCustomIcon; else customToggleIcon">{{key.settings.icon}}</tb-icon>
<ng-template #customToggleIcon>
<img class="mat-icon" [src]="key.settings.safeCustomIcon" alt="icon">
</ng-template>
<ng-container *ngIf="key.settings.icon || key.settings.safeCustomIcon" matIconPrefix>
<ng-container *ngTemplateOutlet="iconPrefix; context: {key: key}"></ng-container>
</ng-container>
<mat-error *ngIf="multipleInputFormGroup.get(key.formId).hasError('required')">
{{ getErrorMessageText(key.settings,'required') }}
@ -78,15 +69,8 @@
{{ getErrorMessageText(key.settings,'max') }}
</mat-error>
</mat-form-field>
</div>
<div class="input-field mat-block" *ngIf="key.settings.dataKeyValueType === 'booleanCheckbox'">
<mat-checkbox formControlName="{{key.formId}}"
(change)="inputChanged(source, key)">
<span class="label-wrapper">{{key.label}}</span>
</mat-checkbox>
</div>
<div class="input-field" *ngIf="key.settings.dataKeyValueType === 'JSON'">
<mat-form-field class="mat-block" appearance="outline" subscriptSizing="dynamic">
<mat-form-field *ngIf="key.settings.dataKeyValueType === 'JSON'"
[appearance]="key.settings.appearance" [subscriptSizing]="key.settings.subscriptSizing">
<mat-label>{{key.label}}</mat-label>
<input
matInput
@ -98,11 +82,8 @@
(focus)="key.isFocused = true; focusInputElement($event)"
(blur)="key.isFocused = false; inputChanged(source, key)"
/>
<ng-container *ngIf="key.settings.icon || key.settings.safeCustomIcon" matPrefix>
<tb-icon *ngIf="!key.settings.safeCustomIcon; else customToggleIcon">{{key.settings.icon}}</tb-icon>
<ng-template #customToggleIcon>
<img class="mat-icon" [src]="key.settings.safeCustomIcon" alt="icon">
</ng-template>
<ng-container *ngIf="key.settings.icon || key.settings.safeCustomIcon" matIconPrefix>
<ng-container *ngTemplateOutlet="iconPrefix; context: {key: key}"></ng-container>
</ng-container>
<button [disabled]="key.settings.isEditable === 'disabled' || key.settings.isEditable === 'readonly'"
type="button"
@ -117,24 +98,8 @@
{{ getErrorMessageText(key.settings,'invalidJSON') | translate }}
</mat-error>
</mat-form-field>
</div>
<div class="input-field mat-block" *ngIf="key.settings.dataKeyValueType === 'booleanSwitch'">
<mat-slide-toggle formControlName="{{key.formId}}"
[labelPosition]="key.settings.slideToggleLabelPosition"
(change)="inputChanged(source, key)">
<ng-container *ngIf="key.settings.icon || key.settings.safeCustomIcon">
<tb-icon *ngIf="!key.settings.safeCustomIcon; else customToggleIcon">{{key.settings.icon}}</tb-icon>
<ng-template #customToggleIcon>
<img class="mat-icon" [src]="key.settings.safeCustomIcon" alt="icon">
</ng-template>
</ng-container>
<span class="label-wrapper">{{key.label}}</span>
</mat-slide-toggle>
</div>
<div class="input-field mat-block date-time-input" *ngIf="(key.settings.dataKeyValueType === 'dateTime') ||
(key.settings.dataKeyValueType === 'date') ||
(key.settings.dataKeyValueType === 'time')" fxLayout="column">
<mat-form-field appearance="outline" subscriptSizing="dynamic">
<mat-form-field *ngIf="['dateTime','date', 'time'].includes(key.settings.dataKeyValueType)"
[appearance]="key.settings.appearance" [subscriptSizing]="key.settings.subscriptSizing">
<mat-label>{{key.label}}</mat-label>
<mat-datetimepicker-toggle [for]="datePicker" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #datePicker type="{{datePickerType(key.settings.dataKeyValueType)}}"
@ -153,9 +118,8 @@
{{ getErrorMessageText(key.settings, 'invalidDate') }}
</mat-error>
</mat-form-field>
</div>
<div class="input-field" *ngIf="key.settings.dataKeyValueType === 'select'">
<mat-form-field class="mat-block" appearance="outline" subscriptSizing="dynamic">
<mat-form-field *ngIf="key.settings.dataKeyValueType === 'select'"
[appearance]="key.settings.appearance" [subscriptSizing]="key.settings.subscriptSizing">
<mat-label>{{key.label}}</mat-label>
<mat-select formControlName="{{key.formId}}"
[required]="key.settings.required"
@ -167,30 +131,56 @@
{{ getCustomTranslationText(option.label ? option.label : option.value) }}
</mat-option>
</mat-select>
<ng-container *ngIf="key.settings.icon || key.settings.safeCustomIcon" matIconPrefix>
<ng-container *ngTemplateOutlet="iconPrefix; context: {key: key}"></ng-container>
</ng-container>
<mat-error *ngIf="multipleInputFormGroup.get(key.formId).hasError('required')">
{{ getErrorMessageText(key.settings, 'required') }}
</mat-error>
</mat-form-field>
</div>
<div class="tb-form-row space-between color-picker-input" *ngIf="key.settings.dataKeyValueType === 'color'">
<div class="label-container">
<ng-container *ngIf="key.settings.icon || key.settings.safeCustomIcon">
<tb-icon *ngIf="!key.settings.safeCustomIcon; else customToggleIcon">{{key.settings.icon}}</tb-icon>
<ng-template #customToggleIcon>
<img class="mat-icon" [src]="key.settings.safeCustomIcon" alt="icon">
</ng-template>
</ng-container>
{{key.label}}
</div>
<tb-color-input asBoxInput
<mat-form-field *ngIf="key.settings.dataKeyValueType === 'color'"
class="color-input" [appearance]="key.settings.appearance" [subscriptSizing]="key.settings.subscriptSizing"
(click)="colorInput.openColorPickerPopup($event)">
<mat-label>{{key.label}}</mat-label>
<input matInput
formControlName="{{key.formId}}"
[required]="key.settings.required"
[requiredText]="getErrorMessageText(key.settings, 'required')"
openOnInput
formControlName="{{key.formId}}">
[readonly]="key.settings.isEditable === 'readonly'"
type="text"
(keydown)="$event.preventDefault();">
<ng-container *ngIf="key.settings.icon || key.settings.safeCustomIcon" matIconPrefix>
<ng-container *ngTemplateOutlet="iconPrefix; context: {key: key}"></ng-container>
</ng-container>
<tb-color-input #colorInput asBoxInput matSuffix
colorClearButton
[disabled]="multipleInputFormGroup.get(key.formId).disabled"
[readonly]="key.settings.isEditable === 'readonly'"
[ngModel]="multipleInputFormGroup.get(key.formId).value"
(ngModelChange)="colorChanged(source, key, $event)"
[ngModelOptions]="{ standalone: true }">
</tb-color-input>
</div>
</div>
<mat-error *ngIf="multipleInputFormGroup.get(key.formId).hasError('required')">
{{ getErrorMessageText(key.settings, 'required') }}
</mat-error>
</mat-form-field>
<mat-checkbox *ngIf="key.settings.dataKeyValueType === 'booleanCheckbox'"
formControlName="{{key.formId}}"
(change)="inputChanged(source, key)">
<ng-container *ngIf="key.settings.icon || key.settings.safeCustomIcon">
<ng-container *ngTemplateOutlet="iconPrefix; context: {key: key}"></ng-container>
</ng-container>
<span class="label-wrapper">{{key.label}}</span>
</mat-checkbox>
<mat-slide-toggle *ngIf="key.settings.dataKeyValueType === 'booleanSwitch'"
formControlName="{{key.formId}}"
[labelPosition]="key.settings.slideToggleLabelPosition"
(change)="inputChanged(source, key)">
<ng-container *ngIf="key.settings.icon || key.settings.safeCustomIcon">
<ng-container *ngTemplateOutlet="iconPrefix; context: {key: key}"></ng-container>
</ng-container>
<span class="label-wrapper">{{key.label}}</span>
</mat-slide-toggle>
</ng-container>
</div>
</fieldset>
</div>
@ -216,3 +206,9 @@
</div>
</div>
</form>
<ng-template #iconPrefix let-key="key">
<tb-icon *ngIf="!key.settings.safeCustomIcon; else customToggleIcon">{{key.settings.icon}}</tb-icon>
<ng-template #customToggleIcon>
<img class="mat-icon" [src]="key.settings.safeCustomIcon" alt="icon">
</ng-template>
</ng-template>

View File

@ -38,34 +38,7 @@
}
.tb-multiple-input-layout {
display: flex;
flex-direction: row;
align-items: start;
gap: 8px;
}
.color-picker-input {
padding: 7px 16px 7px 12px;
margin: 0 10px 22px 0;
border-color: rgba(0, 0, 0, 0.4);
.label-container {
display: flex;
flex-direction: row;
align-items: center;
}
.mat-icon, img {
margin-right: 5px;
}
}
.input-field {
padding-right: 10px;
mat-form-field {
margin-bottom: 5px;
}
display: grid;
}
.mat-mdc-slide-toggle {
@ -74,17 +47,6 @@
margin-bottom: 8px;
}
.date-time-input {
.mat-mdc-form-field {
width: 100%;
margin: 2px 0;
}
}
.vertical-alignment {
flex-direction: column;
}
&--buttons-container {
display: flex;
flex-direction: row;
@ -113,7 +75,8 @@
:host ::ng-deep {
.tb-multiple-input {
.mat-mdc-slide-toggle .mdc-form-field {
.mat-mdc-slide-toggle, .mat-mdc-checkbox {
.mdc-form-field {
width: 100%;
& > label {
display: flex;
@ -121,10 +84,24 @@
width: 100%;
margin: 0;
.mat-icon {
margin-left: 8px;
margin-right: 8px;
}
}
}
}
.mat-mdc-slide-toggle {
.mdc-form-field {
& > label {
.mat-icon {
margin-left: 8px;
}
}
}
}
.color-input {
.mat-mdc-form-field-icon-suffix {
padding: 0 8px 0 4px;
}
}
}
}

View File

@ -48,6 +48,7 @@ import {
} from '@shared/components/dialog/json-object-edit-dialog.component';
import { MatDialog } from '@angular/material/dialog';
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
import { MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form-field';
type FieldAlignment = 'row' | 'column';
@ -55,7 +56,7 @@ type MultipleInputWidgetDataKeyType = 'server' | 'shared' | 'timeseries';
export type MultipleInputWidgetDataKeyValueType = 'string' | 'double' | 'integer' |
'JSON' | 'booleanCheckbox' | 'booleanSwitch' |
'dateTime' | 'date' | 'time' | 'select' | 'color';
type MultipleInputWidgetDataKeyEditableType = 'editable' | 'disabled' | 'readonly';
export type MultipleInputWidgetDataKeyEditableType = 'editable' | 'disabled' | 'readonly';
type ConvertGetValueFunction = (value: any, ctx: WidgetContext) => any;
type ConvertSetValueFunction = (value: any, originValue: any, ctx: WidgetContext) => any;
@ -71,6 +72,8 @@ interface MultipleInputWidgetSettings {
groupTitle: string;
fieldsAlignment: FieldAlignment;
fieldsInRow: number;
columnGap: number;
rowGap: number;
attributesShared?: boolean;
}
@ -88,6 +91,8 @@ interface MultipleInputWidgetDataKeySettings {
isEditable: MultipleInputWidgetDataKeyEditableType;
disabledOnDataKey: string;
dataKeyHidden: boolean;
appearance: MatFormFieldAppearance;
subscriptSizing: SubscriptSizing;
step?: number;
minValue?: number;
maxValue?: number;
@ -148,8 +153,7 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
private isSavingInProgress = false;
isVerticalAlignment: boolean;
inputWidthSettings: string;
changeAlignment: boolean;
columns: number;
saveButtonLabel: string;
resetButtonLabel: string;
@ -228,15 +232,15 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
if (isUndefined(this.settings.fieldsInRow)) {
this.settings.fieldsInRow = 2;
}
// For backward compatibility
this.isVerticalAlignment = !(this.settings.fieldsAlignment === 'row');
if (!this.isVerticalAlignment && this.settings.fieldsInRow) {
this.inputWidthSettings = 100 / this.settings.fieldsInRow + '%';
if (isUndefined(this.settings.columnGap)) {
this.settings.columnGap = 10;
}
if (isUndefined(this.settings.rowGap)) {
this.settings.rowGap = 5;
}
this.updateWidgetDisplaying();
this.updateColumns();
}
private updateDatasources() {
@ -284,6 +288,15 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
dataKey.settings.isEditable = 'editable';
}
}
if (isUndefined(dataKey.settings.appearance)) {
dataKey.settings.appearance = 'outline';
}
if (isUndefined(dataKey.settings.subscriptSizing)) {
dataKey.settings.subscriptSizing = 'fixed';
}
// For backward compatibility
if (dataKey.settings.dataKeyValueType === 'select') {
@ -387,12 +400,6 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
}
});
}
} else if (key.settings.dataKeyValueType === 'color') {
formControl.valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe(() => {
this.inputChanged(source, key);
});
}
this.multipleInputFormGroup.addControl(key.formId, formControl);
}
@ -452,9 +459,8 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
}
if (key.settings.isEditable === 'editable' && key.settings.disabledOnDataKey) {
const conditions = data.filter((item) => {
return source.datasource === item.datasource && item.dataKey.name === key.settings.disabledOnDataKey;
});
const conditions = data.filter((item) =>
source.datasource === item.datasource && item.dataKey.name === key.settings.disabledOnDataKey);
if (conditions && conditions.length) {
if (conditions[0].data.length) {
if (conditions[0].data[0][1] === 'false') {
@ -494,8 +500,17 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
return data;
}
private updateWidgetDisplaying() {
this.changeAlignment = (this.ctx.$container && this.ctx.$container[0].offsetWidth < 620);
private updateColumns() {
const changeAlignment = (this.ctx.$container && this.ctx.$container[0].offsetWidth < 620);
if (changeAlignment) {
this.columns = 1;
} else {
if (!this.isVerticalAlignment && this.settings.fieldsInRow) {
this.columns = this.settings.fieldsInRow;
} else {
this.columns = 1;
}
}
}
public onDataUpdated() {
@ -504,7 +519,7 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
}
private resize() {
this.updateWidgetDisplaying();
this.updateColumns();
this.ctx.detectChanges();
}
@ -599,6 +614,13 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
}
}
public colorChanged(source: MultipleInputWidgetSource, key: MultipleInputWidgetDataKey, color: string) {
this.multipleInputFormGroup.get(key.formId).setValue(color);
this.multipleInputFormGroup.get(key.formId).markAsDirty();
this.multipleInputFormGroup.get(key.formId).markAsTouched();
this.inputChanged(source, key);
}
public saveForm() {
if (this.settings.showActionButtons) {
this.save();
@ -737,7 +759,7 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
this.multipleInputFormGroup.reset(undefined, {emitEvent: false});
this.sources.forEach((source) => {
for (const key of this.visibleKeys(source)) {
this.multipleInputFormGroup.get(key.formId).patchValue(key.value, {emitEvent: false});
this.multipleInputFormGroup.get(key.formId).patchValue(key.value);
}
});
this.multipleInputFormGroup.markAsPristine();

View File

@ -93,11 +93,36 @@
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field [fxShow]="updateMultipleAttributesKeySettingsForm.get('isEditable').value !== 'disabled'" fxFlex="50" class="mat-block">
<mat-form-field [fxShow]="updateMultipleAttributesKeySettingsForm.get('isEditable').value === 'editable'" fxFlex="50" class="mat-block">
<mat-label translate>widgets.input-widgets.disable-on-datakey-name</mat-label>
<input matInput formControlName="disabledOnDataKey">
</mat-form-field>
</section>
<section *ngIf="!['booleanSwitch', 'booleanCheckbox'].includes(updateMultipleAttributesKeySettingsForm.get('dataKeyValueType').value)"
fxLayout="column" fxLayout.gt-xs="row" fxLayoutGap.gt-xs="8px" fxLayoutAlign.gt-xs="start center">
<mat-form-field fxFlex="50" class="mat-block">
<mat-label translate>widgets.input-widgets.field-appearance</mat-label>
<mat-select formControlName="appearance">
<mat-option [value]="'fill'">
{{ 'widgets.input-widgets.appearance-fill' | translate }}
</mat-option>
<mat-option [value]="'outline'">
{{ 'widgets.input-widgets.appearance-outline' | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field fxFlex="50" class="mat-block">
<mat-label translate>widgets.input-widgets.subscript-sizing</mat-label>
<mat-select formControlName="subscriptSizing">
<mat-option [value]="'fixed'">
{{ 'widgets.input-widgets.subscript-sizing-fixed' | translate }}
</mat-option>
<mat-option [value]="'dynamic'">
{{ 'widgets.input-widgets.subscript-sizing-dynamic' | translate }}
</mat-option>
</mat-select>
</mat-form-field>
</section>
</fieldset>
<fieldset [fxShow]="!updateMultipleAttributesKeySettingsForm.get('dataKeyHidden').value &&
updateMultipleAttributesKeySettingsForm.get('dataKeyValueType').value === 'booleanSwitch'" class="fields-group">
@ -208,7 +233,9 @@
</mat-form-field>
</section>
</fieldset>
<fieldset [fxShow]="!updateMultipleAttributesKeySettingsForm.get('dataKeyHidden').value" class="fields-group">
<fieldset [fxShow]="!updateMultipleAttributesKeySettingsForm.get('dataKeyHidden').value &&
!['date', 'dateTime', 'time'].includes(updateMultipleAttributesKeySettingsForm.get('dataKeyValueType').value)"
class="fields-group">
<legend class="group-title" translate>widgets.input-widgets.icon-settings</legend>
<mat-slide-toggle formControlName="useCustomIcon" class="slide-block">
{{ 'widgets.input-widgets.use-custom-icon' | translate }}

View File

@ -24,7 +24,10 @@ import {
dataKeySelectOptionValidator
} from '@home/components/widget/lib/settings/input/datakey-select-option.component';
import { CdkDragDrop } from '@angular/cdk/drag-drop';
import { MultipleInputWidgetDataKeyValueType } from '@home/components/widget/lib/multiple-input-widget.component';
import {
MultipleInputWidgetDataKeyEditableType,
MultipleInputWidgetDataKeyValueType
} from '@home/components/widget/lib/multiple-input-widget.component';
@Component({
selector: 'tb-update-multiple-attributes-key-settings',
@ -52,6 +55,8 @@ export class UpdateMultipleAttributesKeySettingsComponent extends WidgetSettings
required: false,
isEditable: 'editable',
disabledOnDataKey: '',
appearance: 'outline',
subscriptSizing: 'fixed',
slideToggleLabelPosition: 'after',
selectOptions: [],
@ -92,6 +97,8 @@ export class UpdateMultipleAttributesKeySettingsComponent extends WidgetSettings
required: [settings.required, []],
isEditable: [settings.isEditable, []],
disabledOnDataKey: [settings.disabledOnDataKey, []],
appearance: [settings.appearance, []],
subscriptSizing: [settings.subscriptSizing, []],
// Slide toggle settings
@ -146,7 +153,7 @@ export class UpdateMultipleAttributesKeySettingsComponent extends WidgetSettings
const dataKeyValueType: MultipleInputWidgetDataKeyValueType =
this.updateMultipleAttributesKeySettingsForm.get('dataKeyValueType').value;
const required: boolean = this.updateMultipleAttributesKeySettingsForm.get('required').value;
const isEditable: string = this.updateMultipleAttributesKeySettingsForm.get('isEditable').value;
const isEditable: MultipleInputWidgetDataKeyEditableType = this.updateMultipleAttributesKeySettingsForm.get('isEditable').value;
const useCustomIcon: boolean = this.updateMultipleAttributesKeySettingsForm.get('useCustomIcon').value;
const useGetValueFunction: boolean = this.updateMultipleAttributesKeySettingsForm.get('useGetValueFunction').value;
const useSetValueFunction: boolean = this.updateMultipleAttributesKeySettingsForm.get('useSetValueFunction').value;
@ -163,10 +170,15 @@ export class UpdateMultipleAttributesKeySettingsComponent extends WidgetSettings
this.updateMultipleAttributesKeySettingsForm.get('useGetValueFunction').enable({emitEvent: false});
this.updateMultipleAttributesKeySettingsForm.get('useSetValueFunction').enable({emitEvent: false});
if (isEditable !== 'disabled') {
if (isEditable === 'editable') {
this.updateMultipleAttributesKeySettingsForm.get('disabledOnDataKey').enable({emitEvent: false});
}
if (!['booleanSwitch', 'booleanCheckbox'].includes(dataKeyValueType)) {
this.updateMultipleAttributesKeySettingsForm.get('appearance').enable({emitEvent: false});
this.updateMultipleAttributesKeySettingsForm.get('subscriptSizing').enable({emitEvent: false});
}
if (dataKeyValueType === 'booleanSwitch') {
this.updateMultipleAttributesKeySettingsForm.get('slideToggleLabelPosition').enable({emitEvent: false});
} else if (dataKeyValueType === 'select') {

View File

@ -72,7 +72,7 @@
</section>
</fieldset>
<fieldset class="fields-group">
<legend class="group-title" translate>widgets.input-widgets.fields-alignment</legend>
<legend class="group-title" translate>widgets.input-widgets.layout</legend>
<section fxLayout="column" fxLayout.gt-xs="row" fxLayoutGap.gt-xs="8px" fxLayoutAlign.gt-xs="start center">
<mat-form-field fxFlex="50" class="mat-block">
<mat-label translate>widgets.input-widgets.fields-alignment</mat-label>
@ -90,5 +90,15 @@
<input matInput type="number" min="1" step="1" formControlName="fieldsInRow">
</mat-form-field>
</section>
<section fxLayout="column" fxLayout.gt-xs="row" fxLayoutGap.gt-xs="8px" fxLayoutAlign.gt-xs="start center">
<mat-form-field fxFlex="50" class="mat-block">
<mat-label translate>widgets.input-widgets.row-gap</mat-label>
<input matInput type="number" min="0" step="1" formControlName="rowGap">
</mat-form-field>
<mat-form-field fxFlex="50" [fxShow]="updateMultipleAttributesWidgetSettingsForm.get('fieldsAlignment').value === 'row'" class="mat-block">
<mat-label translate>widgets.input-widgets.column-gap</mat-label>
<input matInput type="number" min="0" step="1" formControlName="columnGap">
</mat-form-field>
</section>
</fieldset>
</section>

View File

@ -49,7 +49,9 @@ export class UpdateMultipleAttributesWidgetSettingsComponent extends WidgetSetti
showGroupTitle: false,
groupTitle: '',
fieldsAlignment: 'row',
fieldsInRow: 2
fieldsInRow: 2,
rowGap: 5,
columnGap: 10
};
}
@ -77,6 +79,11 @@ export class UpdateMultipleAttributesWidgetSettingsComponent extends WidgetSetti
fieldsAlignment: [settings.fieldsAlignment, []],
fieldsInRow: [settings.fieldsInRow, [Validators.min(1)]],
// Layout gap
rowGap: [settings.rowGap, [Validators.min(0)]],
columnGap: [settings.columnGap, [Validators.min(0)]]
});
}
@ -105,13 +112,16 @@ export class UpdateMultipleAttributesWidgetSettingsComponent extends WidgetSetti
}
if (fieldsAlignment === 'row') {
this.updateMultipleAttributesWidgetSettingsForm.get('fieldsInRow').enable();
this.updateMultipleAttributesWidgetSettingsForm.get('columnGap').enable();
} else {
this.updateMultipleAttributesWidgetSettingsForm.get('fieldsInRow').disable();
this.updateMultipleAttributesWidgetSettingsForm.get('columnGap').disable();
}
this.updateMultipleAttributesWidgetSettingsForm.get('updateAllValues').updateValueAndValidity({emitEvent});
this.updateMultipleAttributesWidgetSettingsForm.get('saveButtonLabel').updateValueAndValidity({emitEvent});
this.updateMultipleAttributesWidgetSettingsForm.get('resetButtonLabel').updateValueAndValidity({emitEvent});
this.updateMultipleAttributesWidgetSettingsForm.get('groupTitle').updateValueAndValidity({emitEvent});
this.updateMultipleAttributesWidgetSettingsForm.get('fieldsInRow').updateValueAndValidity({emitEvent});
this.updateMultipleAttributesWidgetSettingsForm.get('columnGap').updateValueAndValidity({emitEvent});
}
}

View File

@ -40,7 +40,7 @@
class="tb-box-button"
[disabled]="disabled"
#matButton
(click)="openColorPickerPopup($event, matButton)">
(click)="openColorPickerPopup($event, matButton._elementRef)">
<div class="tb-color-preview no-margin box" [ngClass]="{'disabled': disabled}">
<div class="tb-color-result" [style]="!disabled ? {background: colorFormGroup.get('color').value} : {}"></div>
</div>

View File

@ -14,7 +14,16 @@
/// limitations under the License.
///
import { ChangeDetectorRef, Component, forwardRef, Input, OnInit, Renderer2, ViewContainerRef } from '@angular/core';
import {
ChangeDetectorRef,
Component,
ElementRef,
forwardRef,
Input,
OnInit,
Renderer2,
ViewContainerRef
} from '@angular/core';
import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
@ -84,6 +93,10 @@ export class ColorInputComponent extends PageComponent implements OnInit, Contro
@Input()
disabled: boolean;
@Input()
@coerceBoolean()
readonly = false;
private modelValue: string;
private propagateChange = null;
@ -151,6 +164,7 @@ export class ColorInputComponent extends PageComponent implements OnInit, Contro
showColorPicker($event: MouseEvent) {
$event.stopPropagation();
if (!this.disabled && !this.readonly) {
this.dialogs.colorPicker(this.colorFormGroup.get('color').value,
this.colorClearButton).subscribe(
(result) => {
@ -163,12 +177,14 @@ export class ColorInputComponent extends PageComponent implements OnInit, Contro
}
);
}
}
openColorPickerPopup($event: Event, matButton: MatButton) {
openColorPickerPopup($event: Event, element?: ElementRef) {
if ($event) {
$event.stopPropagation();
}
const trigger = matButton._elementRef.nativeElement;
if (!this.disabled && !this.readonly) {
const trigger = element ? element.nativeElement : $event.target;
if (this.popoverService.hasPopover(trigger)) {
this.popoverService.hidePopover(trigger);
} else {
@ -190,6 +206,7 @@ export class ColorInputComponent extends PageComponent implements OnInit, Contro
});
}
}
}
clear() {
this.colorFormGroup.get('color').patchValue(null, {emitEvent: true});

View File

@ -5329,6 +5329,9 @@
"input-fields-alignment": "Input fields alignment",
"input-fields-alignment-column": "Column (default)",
"input-fields-alignment-row": "Row",
"layout": "Layout",
"row-gap": "Gap between rows in pixels",
"column-gap": "Gap between columns in pixels",
"latitude-field-required": "Latitude field required",
"longitude-field-required": "Longitude field required",
"attribute-settings": "Attribute settings",
@ -5384,6 +5387,12 @@
"ability-to-edit-attribute-disabled": "Disabled",
"ability-to-edit-attribute-readonly": "Read-only",
"disable-on-datakey-name": "Disable on false value of another datakey (specify datakey name)",
"field-appearance": "Field appearance",
"appearance-fill": "Fill",
"appearance-outline": "Outline",
"subscript-sizing": "Subscript sizing",
"subscript-sizing-fixed": "Fixed",
"subscript-sizing-dynamic": "Dynamic",
"slide-toggle-settings": "Slide toggle settings",
"slide-toggle-label-position": "Slide toggle label position",
"slide-toggle-label-position-after": "After",