thingsboard/ui-ngx/src/app/shared/models/widget.models.ts

924 lines
25 KiB
TypeScript
Raw Normal View History

2019-09-03 19:31:16 +03:00
///
2024-01-09 10:46:16 +02:00
/// Copyright © 2016-2024 The Thingsboard Authors
2019-09-03 19:31:16 +03:00
///
/// 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 { BaseData, ExportableEntity } from '@shared/models/base-data';
2019-09-06 20:17:45 +03:00
import { TenantId } from '@shared/models/id/tenant-id';
import { WidgetTypeId } from '@shared/models/id/widget-type-id';
import { AggregationType, ComparisonDuration, Timewindow } from '@shared/models/time/time.models';
2019-09-10 15:12:10 +03:00
import { EntityType } from '@shared/models/entity-type.models';
import { DataKeyType } from './telemetry/telemetry.models';
import { EntityId } from '@shared/models/id/entity-id';
2020-02-24 17:16:02 +02:00
import * as moment_ from 'moment';
import {
AlarmFilter,
AlarmFilterConfig,
EntityDataPageLink,
EntityFilter,
KeyFilter
} from '@shared/models/query/query.models';
import { PopoverPlacement } from '@shared/components/popover.models';
import { PageComponent } from '@shared/components/page.component';
import { AfterViewInit, Directive, EventEmitter, Inject, OnInit, Type } from '@angular/core';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
2023-02-02 15:55:06 +02:00
import { AbstractControl, UntypedFormGroup } from '@angular/forms';
2022-03-30 11:14:54 +03:00
import { Observable } from 'rxjs';
import { Dashboard } from '@shared/models/dashboard.models';
import { IAliasController } from '@core/api/widget-api.models';
import { isNotEmptyStr } from '@core/utils';
import { WidgetConfigComponentData } from '@home/models/widget-component.models';
import { ComponentStyle, Font, TimewindowStyle } from '@shared/models/widget-settings.models';
2023-08-18 16:14:21 +03:00
import { NULL_UUID } from '@shared/models/id/has-uuid';
import { HasTenantId } from '@shared/models/entity.models';
2019-09-03 19:31:16 +03:00
export enum widgetType {
timeseries = 'timeseries',
latest = 'latest',
rpc = 'rpc',
2019-09-10 15:12:10 +03:00
alarm = 'alarm',
static = 'static'
2019-09-03 19:31:16 +03:00
}
export interface WidgetTypeTemplate {
2023-08-18 16:14:21 +03:00
fullFqn: string;
2019-09-03 19:31:16 +03:00
}
export interface WidgetTypeData {
name: string;
icon: string;
2019-09-25 19:37:29 +03:00
configHelpLinkId: string;
2019-09-03 19:31:16 +03:00
template: WidgetTypeTemplate;
}
export const widgetTypesData = new Map<widgetType, WidgetTypeData>(
[
[
widgetType.timeseries,
{
name: 'widget.timeseries',
icon: 'timeline',
2019-09-25 19:37:29 +03:00
configHelpLinkId: 'widgetsConfigTimeseries',
2019-09-03 19:31:16 +03:00
template: {
2023-08-18 16:14:21 +03:00
fullFqn: 'system.charts.basic_timeseries'
2019-09-03 19:31:16 +03:00
}
}
],
[
widgetType.latest,
{
name: 'widget.latest',
icon: 'track_changes',
2019-09-25 19:37:29 +03:00
configHelpLinkId: 'widgetsConfigLatest',
2019-09-03 19:31:16 +03:00
template: {
2023-08-18 16:14:21 +03:00
fullFqn: 'system.cards.attributes_card'
2019-09-03 19:31:16 +03:00
}
}
],
[
widgetType.rpc,
{
name: 'widget.rpc',
icon: 'mdi:developer-board',
2019-09-25 19:37:29 +03:00
configHelpLinkId: 'widgetsConfigRpc',
2019-09-03 19:31:16 +03:00
template: {
2023-08-18 16:14:21 +03:00
fullFqn: 'system.gpio_widgets.basic_gpio_control'
2019-09-03 19:31:16 +03:00
}
}
],
[
widgetType.alarm,
{
name: 'widget.alarm',
icon: 'error',
2019-09-25 19:37:29 +03:00
configHelpLinkId: 'widgetsConfigAlarm',
2019-09-03 19:31:16 +03:00
template: {
2023-08-18 16:14:21 +03:00
fullFqn: 'system.alarm_widgets.alarms_table'
2019-09-03 19:31:16 +03:00
}
}
2019-09-10 15:12:10 +03:00
],
[
widgetType.static,
{
name: 'widget.static',
icon: 'font_download',
2019-09-25 19:37:29 +03:00
configHelpLinkId: 'widgetsConfigStatic',
2019-09-10 15:12:10 +03:00
template: {
2023-08-18 16:14:21 +03:00
fullFqn: 'system.cards.html_card'
2019-09-10 15:12:10 +03:00
}
}
2019-09-03 19:31:16 +03:00
]
]
);
export interface WidgetResource {
url: string;
isModule?: boolean;
2019-09-03 19:31:16 +03:00
}
export interface WidgetActionSource {
name: string;
value: string;
multiple: boolean;
hasShowCondition?: boolean;
}
2019-10-24 19:52:19 +03:00
export const widgetActionSources: {[acionSourceId: string]: WidgetActionSource} = {
headerButton:
{
name: 'widget-action.header-button',
value: 'headerButton',
multiple: true,
hasShowCondition: true
}
};
2019-09-03 19:31:16 +03:00
export interface WidgetTypeDescriptor {
type: widgetType;
resources: Array<WidgetResource>;
templateHtml: string;
templateCss: string;
controllerScript: string;
2019-09-25 19:37:29 +03:00
settingsSchema?: string | any;
dataKeySettingsSchema?: string | any;
latestDataKeySettingsSchema?: string | any;
settingsDirective?: string;
dataKeySettingsDirective?: string;
2022-03-30 11:14:54 +03:00
latestDataKeySettingsDirective?: string;
hasBasicMode?: boolean;
basicModeDirective?: string;
2019-09-03 19:31:16 +03:00
defaultConfig: string;
sizeX: number;
sizeY: number;
}
export interface WidgetTypeParameters {
useCustomDatasources?: boolean;
maxDatasources?: number;
maxDataKeys?: number;
datasourcesOptional?: boolean;
dataKeysOptional?: boolean;
stateData?: boolean;
2020-06-22 18:55:15 +03:00
hasDataPageLink?: boolean;
singleEntity?: boolean;
hasAdditionalLatestDataKeys?: boolean;
warnOnPageDataOverflow?: boolean;
2021-03-02 13:21:53 +02:00
ignoreDataUpdateOnIntervalTick?: boolean;
processNoDataByWidget?: boolean;
previewWidth?: string;
previewHeight?: string;
embedTitlePanel?: boolean;
hideDataSettings?: boolean;
defaultDataKeysFunction?: (configComponent: any, configData: any) => DataKey[];
defaultLatestDataKeysFunction?: (configComponent: any, configData: any) => DataKey[];
displayRpcMessageToast?: boolean;
}
export interface WidgetControllerDescriptor {
widgetTypeFunction?: any;
2019-09-25 19:37:29 +03:00
settingsSchema?: string | any;
dataKeySettingsSchema?: string | any;
latestDataKeySettingsSchema?: string | any;
typeParameters?: WidgetTypeParameters;
2019-10-24 19:52:19 +03:00
actionSources?: {[actionSourceId: string]: WidgetActionSource};
}
export interface BaseWidgetType extends BaseData<WidgetTypeId>, HasTenantId {
2019-09-03 19:31:16 +03:00
tenantId: TenantId;
2023-08-18 16:14:21 +03:00
fqn: string;
2019-09-03 19:31:16 +03:00
name: string;
2023-08-18 16:14:21 +03:00
deprecated: boolean;
2021-03-05 17:08:46 +02:00
}
2023-08-18 16:14:21 +03:00
export const fullWidgetTypeFqn = (type: BaseWidgetType): string =>
((!type.tenantId || type.tenantId?.id === NULL_UUID) ? 'system' : 'tenant') + '.' + type.fqn;
export const widgetTypeFqn = (fullFqn: string): string => {
if (isNotEmptyStr(fullFqn)) {
const parts = fullFqn.split('.');
if (parts.length > 1) {
const scopeQualifier = parts[0];
if (['system', 'tenant'].includes(scopeQualifier)) {
return fullFqn.substring(scopeQualifier.length + 1);
}
}
}
return fullFqn;
};
export const isValidWidgetFullFqn = (fullFqn: string): boolean => {
if (isNotEmptyStr(fullFqn)) {
const parts = fullFqn.split('.');
if (parts.length > 1) {
const scopeQualifier = parts[0];
return ['system', 'tenant'].includes(scopeQualifier);
}
}
return false;
};
2021-03-05 17:08:46 +02:00
export interface WidgetType extends BaseWidgetType {
descriptor: WidgetTypeDescriptor;
}
export interface WidgetTypeInfo extends BaseWidgetType {
image: string;
description: string;
tags: string[];
2021-03-05 17:08:46 +02:00
widgetType: widgetType;
}
export interface WidgetTypeDetails extends WidgetType, ExportableEntity<WidgetTypeId> {
image: string;
description: string;
tags: string[];
}
export enum DeprecatedFilter {
ALL = 'ALL',
ACTUAL = 'ACTUAL',
DEPRECATED = 'DEPRECATED'
2019-09-03 19:31:16 +03:00
}
export enum LegendDirection {
column = 'column',
row = 'row'
}
export const legendDirectionTranslationMap = new Map<LegendDirection, string>(
[
[ LegendDirection.column, 'direction.column' ],
[ LegendDirection.row, 'direction.row' ]
]
);
export enum LegendPosition {
top = 'top',
bottom = 'bottom',
left = 'left',
right = 'right'
}
2023-11-16 17:42:02 +02:00
export const legendPositions = Object.keys(LegendPosition) as LegendPosition[];
export const legendPositionTranslationMap = new Map<LegendPosition, string>(
[
[ LegendPosition.top, 'position.top' ],
[ LegendPosition.bottom, 'position.bottom' ],
[ LegendPosition.left, 'position.left' ],
[ LegendPosition.right, 'position.right' ]
]
);
export interface LegendConfig {
position: LegendPosition;
direction?: LegendDirection;
sortDataKeys: boolean;
showMin: boolean;
showMax: boolean;
showAvg: boolean;
showTotal: boolean;
2022-06-22 14:53:43 +05:00
showLatest: boolean;
}
2023-05-16 20:00:53 +03:00
export const defaultLegendConfig = (wType: widgetType): LegendConfig => ({
direction: LegendDirection.column,
position: LegendPosition.bottom,
sortDataKeys: false,
showMin: false,
showMax: false,
showAvg: wType === widgetType.timeseries,
showTotal: false,
showLatest: false
});
2019-10-24 19:52:19 +03:00
export enum ComparisonResultType {
PREVIOUS_VALUE = 'PREVIOUS_VALUE',
DELTA_ABSOLUTE = 'DELTA_ABSOLUTE',
DELTA_PERCENT = 'DELTA_PERCENT'
}
export const comparisonResultTypeTranslationMap = new Map<ComparisonResultType, string>(
[
[ComparisonResultType.PREVIOUS_VALUE, 'datakey.delta-calculation-result-previous-value'],
[ComparisonResultType.DELTA_ABSOLUTE, 'datakey.delta-calculation-result-delta-absolute'],
[ComparisonResultType.DELTA_PERCENT, 'datakey.delta-calculation-result-delta-percent']
]
);
2019-09-10 15:12:10 +03:00
export interface KeyInfo {
name: string;
aggregationType?: AggregationType;
comparisonEnabled?: boolean;
timeForComparison?: ComparisonDuration;
comparisonCustomIntervalValue?: number;
comparisonResultType?: ComparisonResultType;
2019-09-10 15:12:10 +03:00
label?: string;
color?: string;
funcBody?: string;
postFuncBody?: string;
units?: string;
decimals?: number;
}
export const dataKeyAggregationTypeHintTranslationMap = new Map<AggregationType, string>(
[
[AggregationType.MIN, 'datakey.aggregation-type-min-hint'],
[AggregationType.MAX, 'datakey.aggregation-type-max-hint'],
[AggregationType.AVG, 'datakey.aggregation-type-avg-hint'],
[AggregationType.SUM, 'datakey.aggregation-type-sum-hint'],
[AggregationType.COUNT, 'datakey.aggregation-type-count-hint'],
[AggregationType.NONE, 'datakey.aggregation-type-none-hint'],
]
);
2019-09-10 15:12:10 +03:00
export interface DataKey extends KeyInfo {
type: DataKeyType;
pattern?: string;
settings?: any;
usePostProcessing?: boolean;
hidden?: boolean;
2020-02-25 12:30:38 +02:00
inLegend?: boolean;
2020-06-24 18:07:47 +03:00
isAdditional?: boolean;
origDataKeyIndex?: number;
2019-09-10 15:12:10 +03:00
_hash?: number;
}
export enum DataKeyConfigMode {
general = 'general',
advanced = 'advanced'
}
2019-09-10 15:12:10 +03:00
export enum DatasourceType {
function = 'function',
device = 'device',
2021-03-02 12:04:45 +02:00
entity = 'entity',
entityCount = 'entityCount',
alarmCount = 'alarmCount'
2019-09-10 15:12:10 +03:00
}
export const datasourceTypeTranslationMap = new Map<DatasourceType, string>(
[
[ DatasourceType.function, 'function.function' ],
[ DatasourceType.device, 'device.device' ],
2021-03-02 12:04:45 +02:00
[ DatasourceType.entity, 'entity.entity' ],
[ DatasourceType.entityCount, 'entity.entities-count' ],
[ DatasourceType.alarmCount, 'entity.alarms-count' ]
]
);
2019-09-10 15:12:10 +03:00
export interface Datasource {
type?: DatasourceType | any;
2019-09-10 15:12:10 +03:00
name?: string;
aliasName?: string;
2019-09-10 15:12:10 +03:00
dataKeys?: Array<DataKey>;
latestDataKeys?: Array<DataKey>;
2019-09-10 15:12:10 +03:00
entityType?: EntityType;
entityId?: string;
entityName?: string;
deviceId?: string;
2019-09-10 15:12:10 +03:00
entityAliasId?: string;
filterId?: string;
unresolvedStateEntity?: boolean;
dataReceived?: boolean;
entity?: BaseData<EntityId>;
entityLabel?: string;
entityDescription?: string;
generated?: boolean;
2020-02-24 17:16:02 +02:00
isAdditional?: boolean;
2020-06-24 18:07:47 +03:00
origDatasourceIndex?: number;
pageLink?: EntityDataPageLink;
keyFilters?: Array<KeyFilter>;
entityFilter?: EntityFilter;
alarmFilterConfig?: AlarmFilterConfig;
alarmFilter?: AlarmFilter;
dataKeyStartIndex?: number;
latestDataKeyStartIndex?: number;
[key: string]: any;
}
export enum TargetDeviceType {
device = 'device',
entity = 'entity'
}
export interface TargetDevice {
type?: TargetDeviceType;
deviceId?: string;
entityAliasId?: string;
}
export const targetDeviceValid = (targetDevice?: TargetDevice): boolean =>
!!targetDevice && !!targetDevice.type &&
((targetDevice.type === TargetDeviceType.device && !!targetDevice.deviceId) ||
(targetDevice.type === TargetDeviceType.entity && !!targetDevice.entityAliasId));
2023-05-16 20:00:53 +03:00
export const datasourcesHasAggregation = (datasources?: Array<Datasource>): boolean => {
if (datasources) {
const foundDatasource = datasources.find(datasource => {
2023-11-07 13:57:27 +02:00
const found = datasource.dataKeys && datasource.dataKeys.find(key => key?.type === DataKeyType.timeseries &&
key?.aggregationType && key.aggregationType !== AggregationType.NONE);
return !!found;
});
if (foundDatasource) {
return true;
}
}
return false;
2023-05-16 20:00:53 +03:00
};
2023-05-16 20:00:53 +03:00
export const datasourcesHasOnlyComparisonAggregation = (datasources?: Array<Datasource>): boolean => {
if (!datasourcesHasAggregation(datasources)) {
return false;
}
if (datasources) {
const foundDatasource = datasources.find(datasource => {
2023-11-09 10:35:06 +02:00
const found = datasource.dataKeys && datasource.dataKeys.find(key => key?.type === DataKeyType.timeseries &&
key?.aggregationType && key.aggregationType !== AggregationType.NONE && !key.comparisonEnabled);
return !!found;
});
if (foundDatasource) {
return false;
}
}
return true;
2023-05-16 20:00:53 +03:00
};
export interface FormattedData {
$datasource: Datasource;
entityName: string;
deviceName: string;
entityId: string;
entityType: EntityType;
entityLabel: string;
entityDescription: string;
aliasName: string;
dsIndex: number;
dsName: string;
deviceType: string;
[key: string]: any;
}
export interface ReplaceInfo {
variable: string;
valDec?: number;
dataKeyName: string;
}
export type DataEntry = [number, any, [number, number]?];
export type DataSet = DataEntry[];
export interface IndexedData {
[id: number]: DataSet;
}
2019-09-10 15:12:10 +03:00
export interface DataSetHolder {
data: DataSet;
}
export interface DatasourceData extends DataSetHolder {
2019-09-10 15:12:10 +03:00
datasource: Datasource;
dataKey: DataKey;
}
export interface LegendKey {
dataKey: DataKey;
dataIndex: number;
}
export interface LegendKeyData {
min: string;
max: string;
avg: string;
total: string;
2022-06-22 14:53:43 +05:00
latest: string;
2019-09-10 15:12:10 +03:00
hidden: boolean;
}
export interface LegendData {
keys: Array<LegendKey>;
data: Array<LegendKeyData>;
}
export enum WidgetActionType {
openDashboardState = 'openDashboardState',
updateDashboardState = 'updateDashboardState',
openDashboard = 'openDashboard',
custom = 'custom',
customPretty = 'customPretty',
mobileAction = 'mobileAction'
}
export enum WidgetMobileActionType {
takePictureFromGallery = 'takePictureFromGallery',
takePhoto = 'takePhoto',
mapDirection = 'mapDirection',
mapLocation = 'mapLocation',
scanQrCode = 'scanQrCode',
makePhoneCall = 'makePhoneCall',
getLocation = 'getLocation',
takeScreenshot = 'takeScreenshot'
}
export const widgetActionTypeTranslationMap = new Map<WidgetActionType, string>(
[
[ WidgetActionType.openDashboardState, 'widget-action.open-dashboard-state' ],
[ WidgetActionType.updateDashboardState, 'widget-action.update-dashboard-state' ],
[ WidgetActionType.openDashboard, 'widget-action.open-dashboard' ],
[ WidgetActionType.custom, 'widget-action.custom' ],
[ WidgetActionType.customPretty, 'widget-action.custom-pretty' ],
[ WidgetActionType.mobileAction, 'widget-action.mobile-action' ]
]
);
export const widgetMobileActionTypeTranslationMap = new Map<WidgetMobileActionType, string>(
[
[ WidgetMobileActionType.takePictureFromGallery, 'widget-action.mobile.take-picture-from-gallery' ],
[ WidgetMobileActionType.takePhoto, 'widget-action.mobile.take-photo' ],
[ WidgetMobileActionType.mapDirection, 'widget-action.mobile.map-direction' ],
[ WidgetMobileActionType.mapLocation, 'widget-action.mobile.map-location' ],
[ WidgetMobileActionType.scanQrCode, 'widget-action.mobile.scan-qr-code' ],
[ WidgetMobileActionType.makePhoneCall, 'widget-action.mobile.make-phone-call' ],
[ WidgetMobileActionType.getLocation, 'widget-action.mobile.get-location' ],
[ WidgetMobileActionType.takeScreenshot, 'widget-action.mobile.take-screenshot' ]
]
);
export interface MobileLaunchResult {
launched: boolean;
}
export interface MobileImageResult {
imageUrl: string;
}
export interface MobileQrCodeResult {
code: string;
format: string;
}
export interface MobileLocationResult {
latitude: number;
longitude: number;
}
export type MobileActionResult = MobileLaunchResult &
MobileImageResult &
MobileQrCodeResult &
MobileLocationResult;
export interface WidgetMobileActionResult<T extends MobileActionResult> {
result?: T;
hasResult: boolean;
error?: string;
hasError: boolean;
}
export interface ProcessImageDescriptor {
processImageFunction: string;
}
export interface ProcessLaunchResultDescriptor {
processLaunchResultFunction?: string;
}
export interface LaunchMapDescriptor extends ProcessLaunchResultDescriptor {
getLocationFunction: string;
}
export interface ScanQrCodeDescriptor {
processQrCodeFunction: string;
}
export interface MakePhoneCallDescriptor extends ProcessLaunchResultDescriptor {
getPhoneNumberFunction: string;
}
export interface GetLocationDescriptor {
processLocationFunction: string;
}
export type WidgetMobileActionDescriptors = ProcessImageDescriptor &
LaunchMapDescriptor &
ScanQrCodeDescriptor &
MakePhoneCallDescriptor &
GetLocationDescriptor;
export interface WidgetMobileActionDescriptor extends WidgetMobileActionDescriptors {
type: WidgetMobileActionType;
handleErrorFunction?: string;
handleEmptyResultFunction?: string;
}
2019-10-24 19:52:19 +03:00
export interface CustomActionDescriptor {
customFunction?: string;
customResources?: Array<WidgetResource>;
customHtml?: string;
customCss?: string;
customModules?: Type<any>[];
2019-10-24 19:52:19 +03:00
}
2024-01-26 17:54:24 +02:00
export interface WidgetAction extends CustomActionDescriptor {
type: WidgetActionType;
targetDashboardId?: string;
targetDashboardStateId?: string;
openRightLayout?: boolean;
openNewBrowserTab?: boolean;
openInPopover?: boolean;
popoverHideDashboardToolbar?: boolean;
popoverPreferredPlacement?: PopoverPlacement;
popoverHideOnClickOutside?: boolean;
popoverWidth?: string;
popoverHeight?: string;
popoverStyle?: { [klass: string]: any };
openInSeparateDialog?: boolean;
dialogTitle?: string;
dialogHideDashboardToolbar?: boolean;
dialogWidth?: number;
dialogHeight?: number;
setEntityId?: boolean;
stateEntityParamName?: string;
mobileAction?: WidgetMobileActionDescriptor;
2024-01-26 17:54:24 +02:00
}
export interface WidgetActionDescriptor extends WidgetAction {
id: string;
name: string;
icon: string;
displayName?: string;
useShowWidgetActionFunction?: boolean;
showWidgetActionFunction?: string;
}
2024-01-26 17:54:24 +02:00
export const actionDescriptorToAction = (descriptor: WidgetActionDescriptor): WidgetAction => {
const result: WidgetActionDescriptor = {...descriptor};
delete result.id;
delete result.name;
delete result.icon;
delete result.displayName;
delete result.useShowWidgetActionFunction;
delete result.showWidgetActionFunction;
return result;
};
2020-02-24 17:16:02 +02:00
export interface WidgetComparisonSettings {
comparisonEnabled?: boolean;
timeForComparison?: moment_.unitOfTime.DurationConstructor;
comparisonCustomIntervalValue?: number;
2020-02-24 17:16:02 +02:00
}
export interface WidgetSettings {
[key: string]: any;
}
export enum WidgetConfigMode {
basic = 'basic',
advanced = 'advanced'
}
2019-09-03 19:31:16 +03:00
export interface WidgetConfig {
configMode?: WidgetConfigMode;
2019-09-03 19:31:16 +03:00
title?: string;
titleFont?: Font;
titleColor?: string;
2019-09-03 19:31:16 +03:00
titleIcon?: string;
showTitle?: boolean;
showTitleIcon?: boolean;
iconColor?: string;
2019-09-25 19:37:29 +03:00
iconSize?: string;
2020-02-25 19:11:25 +02:00
titleTooltip?: string;
2019-09-03 19:31:16 +03:00
dropShadow?: boolean;
enableFullscreen?: boolean;
useDashboardTimewindow?: boolean;
displayTimewindow?: boolean;
timewindow?: Timewindow;
timewindowStyle?: TimewindowStyle;
desktopHide?: boolean;
mobileHide?: boolean;
2019-09-03 19:31:16 +03:00
mobileHeight?: number;
mobileOrder?: number;
color?: string;
backgroundColor?: string;
padding?: string;
margin?: string;
borderRadius?: string;
widgetStyle?: ComponentStyle;
widgetCss?: string;
titleStyle?: ComponentStyle;
units?: string;
decimals?: number;
noDataDisplayMessage?: string;
pageSize?: number;
actions?: {[actionSourceId: string]: Array<WidgetActionDescriptor>};
settings?: WidgetSettings;
2019-09-10 15:12:10 +03:00
alarmSource?: Datasource;
alarmFilterConfig?: AlarmFilterConfig;
2019-09-10 15:12:10 +03:00
datasources?: Array<Datasource>;
targetDevice?: TargetDevice;
2019-09-03 19:31:16 +03:00
[key: string]: any;
}
2023-09-05 12:18:59 +03:00
export interface BaseWidgetInfo {
id?: string;
typeFullFqn: string;
type: widgetType;
}
export interface Widget extends BaseWidgetInfo, ExportableEntity<WidgetTypeId> {
typeId?: WidgetTypeId;
2021-03-03 18:41:01 +02:00
sizeX: number;
sizeY: number;
row: number;
col: number;
config: WidgetConfig;
}
2023-09-05 12:18:59 +03:00
export interface WidgetInfo extends BaseWidgetInfo {
2019-09-03 19:31:16 +03:00
title: string;
2021-03-05 17:08:46 +02:00
image?: string;
description?: string;
deprecated?: boolean;
2019-09-03 19:31:16 +03:00
}
2020-02-24 17:16:02 +02:00
export interface GroupInfo {
formIndex: number;
GroupTitle: string;
}
export interface JsonSchema {
type: string;
title?: string;
properties: {[key: string]: any};
required?: string[];
}
export interface JsonSettingsSchema {
2020-02-24 17:16:02 +02:00
schema?: JsonSchema;
form?: any[];
groupInfoes?: GroupInfo[];
}
export interface WidgetPosition {
row: number;
column: number;
}
export interface WidgetSize {
sizeX: number;
sizeY: number;
}
export interface IWidgetSettingsComponent {
aliasController: IAliasController;
dashboard: Dashboard;
widget: Widget;
widgetConfig: WidgetConfigComponentData;
functionScopeVariables: string[];
settings: WidgetSettings;
settingsChanged: Observable<WidgetSettings>;
validateSettings(): boolean;
[key: string]: any;
}
@Directive()
2023-02-02 16:55:27 +02:00
// eslint-disable-next-line @angular-eslint/directive-class-suffix
export abstract class WidgetSettingsComponent extends PageComponent implements
IWidgetSettingsComponent, OnInit, AfterViewInit {
aliasController: IAliasController;
dashboard: Dashboard;
widget: Widget;
widgetConfigValue: WidgetConfigComponentData;
set widgetConfig(value: WidgetConfigComponentData) {
this.widgetConfigValue = value;
this.onWidgetConfigSet(value);
}
get widgetConfig(): WidgetConfigComponentData {
return this.widgetConfigValue;
}
functionScopeVariables: string[];
settingsValue: WidgetSettings;
private settingsSet = false;
set settings(value: WidgetSettings) {
if (!value) {
this.settingsValue = this.defaultSettings();
} else {
this.settingsValue = {...this.defaultSettings(), ...value};
}
if (!this.settingsSet) {
this.settingsSet = true;
this.setupSettings(this.settingsValue);
} else {
this.updateSettings(this.settingsValue);
}
}
get settings(): WidgetSettings {
return this.settingsValue;
}
settingsChangedEmitter = new EventEmitter<WidgetSettings>();
settingsChanged = this.settingsChangedEmitter.asObservable();
protected constructor(@Inject(Store) protected store: Store<AppState>) {
super(store);
}
ngOnInit() {}
ngAfterViewInit(): void {
if (!this.validateSettings()) {
setTimeout(() => {
this.onSettingsChanged(this.prepareOutputSettings(this.settingsForm().getRawValue()));
}, 0);
}
}
public validateSettings(): boolean {
return this.settingsForm().valid;
}
protected setupSettings(settings: WidgetSettings) {
this.onSettingsSet(this.prepareInputSettings(settings));
this.updateValidators(false);
for (const trigger of this.validatorTriggers()) {
const path = trigger.split('.');
let control: AbstractControl = this.settingsForm();
for (const part of path) {
control = control.get(part);
}
control.valueChanges.subscribe(() => {
this.updateValidators(true, trigger);
});
}
this.settingsForm().valueChanges.subscribe(() => {
this.onSettingsChanged(this.prepareOutputSettings(this.settingsForm().getRawValue()));
});
}
protected updateSettings(settings: WidgetSettings) {
2022-04-28 16:38:58 +03:00
settings = this.prepareInputSettings(settings);
this.settingsForm().reset(settings, {emitEvent: false});
this.doUpdateSettings(this.settingsForm(), settings);
this.updateValidators(false);
}
protected updateValidators(emitEvent: boolean, trigger?: string) {
}
protected validatorTriggers(): string[] {
return [];
}
protected onSettingsChanged(updated: WidgetSettings) {
this.settingsValue = updated;
this.settingsChangedEmitter.emit(this.settingsValue);
}
2023-02-02 15:55:06 +02:00
protected doUpdateSettings(settingsForm: UntypedFormGroup, settings: WidgetSettings) {
2022-04-28 16:38:58 +03:00
}
protected prepareInputSettings(settings: WidgetSettings): WidgetSettings {
return settings;
}
protected prepareOutputSettings(settings: any): WidgetSettings {
return settings;
}
2023-02-02 15:55:06 +02:00
protected abstract settingsForm(): UntypedFormGroup;
protected abstract onSettingsSet(settings: WidgetSettings);
protected defaultSettings(): WidgetSettings {
return {};
}
protected onWidgetConfigSet(widgetConfig: WidgetConfigComponentData) {
}
}