@c8y/ngx-components 1023.14.154 → 1023.14.156

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -140,12 +140,16 @@ class StaticAssetsService {
140
140
  * Gets the static assets app for the given type.
141
141
  */
142
142
  async getAppForTenant(type) {
143
- const appName = this.getContextPath(type, false);
144
- const { data: apps } = await this.appService.listByName(appName);
145
- if (!apps.length) {
146
- throw Error(`No app with name ${appName} found.`);
143
+ const contextPath = this.getContextPath(type);
144
+ const { data: manifest } = await this.appService.getManifestOfContextPath(contextPath);
145
+ if (!manifest.id) {
146
+ throw Error(`No app with path ${contextPath} found.`);
147
147
  }
148
- return apps[0];
148
+ const { data: app } = await this.appService.detail(manifest.id);
149
+ if (app.owner?.tenant.id !== this.appstate.currentTenant.value?.name) {
150
+ throw Error(`No app with path ${contextPath} found that is owned by the current tenant.`);
151
+ }
152
+ return app;
149
153
  }
150
154
  getContentJSONForType(type, appendTenantId) {
151
155
  const path = this.getPathForContent(type, appendTenantId);
@@ -1 +1 @@
1
- {"version":3,"file":"c8y-ngx-components-static-assets-data.mjs","sources":["../../static-assets/data/static-assets.service.ts","../../static-assets/data/c8y-ngx-components-static-assets-data.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\nimport { ApplicationAvailability, ApplicationType, FetchClient, IApplication } from '@c8y/client';\nimport { ApplicationService } from '@c8y/client';\nimport { AppStateService, DroppedFile, FilesService, ZipService } from '@c8y/ngx-components';\nimport { StaticAsset, StaticAssetType } from './static-assets.model';\n\n@Injectable({ providedIn: 'root' })\nexport class StaticAssetsService {\n readonly fileNames = {\n contents: 'contents.json',\n manifest: 'cumulocity.json',\n exportZipName: 'static-assets.zip'\n } as const;\n private listFilesCachePromises: Record<string, Promise<StaticAsset[]>> = {};\n\n constructor(\n private appstate: AppStateService,\n private appService: ApplicationService,\n private fetchClient: FetchClient,\n private fileService: FilesService,\n private zip: ZipService\n ) {}\n\n /**\n * Lists all files of the given type.\n * Forces a cache renewal.\n */\n async listFiles<T extends string = StaticAssetType>(type: T): Promise<StaticAsset[]> {\n return this.listFilesCached(type, true);\n }\n\n /**\n * Lists all files of the given type.\n * Uses a cache.\n */\n async listFilesCached<T extends string = StaticAssetType>(\n type: T,\n forceCacheRenewal = false\n ): Promise<StaticAsset[]> {\n if (!this.listFilesCachePromises[type] || forceCacheRenewal) {\n this.listFilesCachePromises[type] = this._listFiles(type);\n }\n\n return await this.listFilesCachePromises[type];\n }\n\n /**\n * Clones all assets of the given type from parent tenants into the current tenant.\n */\n async cloneAssetsIntoTenant<T extends string = StaticAssetType>(type: T) {\n const { data: apps } = await this.appService.list({\n availability: ApplicationAvailability.SHARED,\n type: ApplicationType.HOSTED,\n pageSize: 2000\n });\n\n const tenantIdsFromOtherAssetApps = apps\n .filter(app => {\n const result =\n app.owner?.tenant?.id &&\n app.owner?.tenant?.id !== this.appstate.currentTenant.value?.name &&\n app.contextPath === this.getContextPath(type, app.owner?.tenant?.id);\n return result;\n })\n .map(app => app.owner?.tenant?.id);\n\n let filesOfCurrentTenant = await this._listFiles(type);\n\n let filesToUpload = new Array<File>();\n\n const oldAssets = new Array<StaticAsset>();\n\n for (const tenantId of tenantIdsFromOtherAssetApps) {\n try {\n const filesOfApp = await this.getContentJSONForType(type, tenantId);\n oldAssets.push(...filesOfApp);\n for (const file of filesOfApp) {\n try {\n const response = await this.fetchClient.fetch(file.path);\n if (response.status !== 200) {\n console.warn(`Failed to get file \"${file.fileName}\" from path ${file.path}`);\n continue;\n }\n const fileContent = await response.blob();\n\n // remove existing file with same name\n filesOfCurrentTenant = filesOfCurrentTenant.filter(\n existingFile => existingFile.fileName !== file.fileName\n );\n filesToUpload = filesToUpload.filter(\n existingFile => existingFile.name !== file.fileName\n );\n\n filesToUpload.push(new File([fileContent], file.fileName, { type: file.type }));\n } catch (e) {\n console.warn(`Failed to add file \"${file.fileName}\" from tenant ${tenantId}`, e);\n }\n }\n } catch (e) {\n console.warn(`Failed to get asset files from tenant ${tenantId}`);\n }\n }\n\n if (!filesToUpload.length) {\n return;\n }\n\n const newAssets = await this.addFilesToStaticAssets(type, filesToUpload, filesOfCurrentTenant);\n\n return { oldAssets, newAssets };\n }\n\n /**\n * Adds the given files to the static assets of the given type.\n */\n async addFilesToStaticAssets<T extends string = StaticAssetType>(\n type: T,\n files: DroppedFile[] | File[],\n existingFiles: StaticAsset[],\n throwErrorOnExistingFiles = true\n ) {\n const app = await this.getAppForTenant(type);\n const filesToAddToContents = new Array<StaticAsset>();\n const addedAt = new Date().toISOString();\n const filesToUpload: {\n path: string;\n contents: Blob;\n }[] = [];\n for (const droppedFile of files) {\n const file = droppedFile instanceof File ? droppedFile : droppedFile.file;\n const fileName = file.name.toLowerCase().replace(/[^a-zA-Z0-9\\-\\_\\.]/g, '');\n const newFile = new File([file], fileName, {\n type: file.type,\n lastModified: file.lastModified\n });\n if (existingFiles.find(existingFile => existingFile.fileName === fileName)) {\n if (throwErrorOnExistingFiles) {\n throw Error(`File with name \"${fileName}\" is already existing.`);\n }\n existingFiles = existingFiles.filter(existingFile => existingFile.fileName !== fileName);\n }\n const fileExtension = file.name.split('.').pop();\n filesToAddToContents.push({\n addedAt,\n lastModified: file.lastModified,\n extension: fileExtension,\n fileName: fileName,\n originalFileName: file.name,\n path: `/apps/public/${app.contextPath}/${fileName}`,\n size: newFile.size,\n type: newFile.type,\n hashSum: await this.fileService.getHashSumOfFile(newFile)\n });\n filesToUpload.push({\n contents: newFile,\n path: fileName\n });\n }\n\n const newFiles: StaticAsset[] = [...existingFiles, ...filesToAddToContents];\n filesToUpload.push({\n contents: new File([JSON.stringify(newFiles)], this.fileNames.contents, {\n type: 'application/json'\n }),\n path: this.fileNames.contents\n });\n await this.appService.binary(app).updateFiles(filesToUpload);\n await this.ensureAddedFilesArePresent(filesToAddToContents);\n return newFiles;\n }\n\n /**\n * Gets the static assets app for the given type.\n */\n async getAppForTenant<T extends string = StaticAssetType>(type: T): Promise<IApplication> {\n const appName = this.getContextPath(type, false);\n const { data: apps } = await this.appService.listByName(appName);\n if (!apps.length) {\n throw Error(`No app with name ${appName} found.`);\n }\n return apps[0];\n }\n\n private getContentJSONForType<T extends string = StaticAssetType>(\n type: T,\n appendTenantId?: string | null | undefined\n ) {\n const path = this.getPathForContent(type, appendTenantId);\n return this.getContentJSONFromPath(path);\n }\n\n private async getContentJSONFromPath(path: string): Promise<StaticAsset[]> {\n const response = await this.fetchClient.fetch(path);\n if (response.status !== 200) {\n throw Error(`Failed to retrieve ${path}`);\n }\n\n const content = await response.json();\n return content;\n }\n\n private async ensureAddedFilesArePresent(files: StaticAsset[]) {\n for (const file of files) {\n await this.ensureFileIsPresent(file);\n }\n }\n\n private async ensureFileIsPresent(file: StaticAsset) {\n for (let i = 0; i < 12; i++) {\n try {\n const currentDate = new Date();\n const response = await this.fetchClient.fetch(\n `${file.path}?nocache=${currentDate.getTime()}`\n );\n if (response.status !== 200) {\n await this.sleep(5_000);\n continue;\n }\n\n const blob = await response.blob();\n const hashSum = await this.fileService.getHashSumOfFile(blob);\n if (hashSum !== file.hashSum) {\n await this.sleep(5_000);\n continue;\n }\n return;\n } catch (e) {\n console.warn(e);\n continue;\n }\n }\n throw Error(`Unable to retrieve file from ${file.path}`);\n }\n\n private sleep(duration: number): Promise<void> {\n return new Promise<void>(resolve => setTimeout(() => resolve(), duration));\n }\n\n private async _listFiles<T extends string = StaticAssetType>(type: T) {\n try {\n const files = await this.getContentJSONForType(type);\n return files;\n } catch (e) {\n console.warn(e);\n }\n\n return await this.createEmptyApp(type);\n }\n\n private getPathForContent<T extends string = StaticAssetType>(\n type: T,\n appendTenantId?: string | null | undefined\n ): string {\n const currentDate = new Date();\n const contextPath = this.getContextPath(type, appendTenantId);\n return `/apps/public/${contextPath}/${\n this.fileNames.contents\n }?nocache=${currentDate.getTime()}`;\n }\n\n private getContextPath<T extends string = StaticAssetType>(\n type: T,\n appendTenantId?: string | false | undefined\n ) {\n let contextPath: string = type + '-assets';\n const tenantId =\n appendTenantId === undefined ? this.appstate.currentTenant.value?.name : appendTenantId;\n if (tenantId) {\n contextPath = `${contextPath}-${tenantId}`;\n }\n return contextPath;\n }\n\n private async createEmptyApp<T extends string = StaticAssetType>(\n type: T,\n appendTenantId?: string | null | undefined\n ): Promise<StaticAsset[]> {\n const name = this.getContextPath(type, false);\n const contextPath = this.getContextPath(type, appendTenantId);\n const key = `${contextPath}-key`;\n const manifest: IApplication = {\n name,\n contextPath,\n key,\n description: `Application containing static assets used for ${type}.`,\n noAppSwitcher: true,\n type: ApplicationType.HOSTED,\n availability: ApplicationAvailability.MARKET\n };\n\n const content = [];\n const zipFile = await this.zip.createZip([\n {\n fileName: `${this.fileNames.manifest}`,\n blob: new Blob([JSON.stringify(manifest)])\n },\n {\n fileName: `${this.fileNames.contents}`,\n blob: new Blob([JSON.stringify(content)])\n }\n ]);\n const { data: app } = await this.appService.create(manifest);\n const { data: appBinary } = await this.appService\n .binary(app)\n .upload(zipFile, `empty-${name}.zip`);\n await this.appService.update({ id: app.id, activeVersionId: appBinary.id as string });\n return [];\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"c8y-ngx-components-static-assets-data.mjs","sources":["../../static-assets/data/static-assets.service.ts","../../static-assets/data/c8y-ngx-components-static-assets-data.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\nimport {\n ApplicationAvailability,\n ApplicationService,\n ApplicationType,\n FetchClient,\n IApplication\n} from '@c8y/client';\nimport { AppStateService, DroppedFile, FilesService, ZipService } from '@c8y/ngx-components';\nimport { StaticAsset, StaticAssetType } from './static-assets.model';\n\n@Injectable({ providedIn: 'root' })\nexport class StaticAssetsService {\n readonly fileNames = {\n contents: 'contents.json',\n manifest: 'cumulocity.json',\n exportZipName: 'static-assets.zip'\n } as const;\n private listFilesCachePromises: Record<string, Promise<StaticAsset[]>> = {};\n\n constructor(\n private appstate: AppStateService,\n private appService: ApplicationService,\n private fetchClient: FetchClient,\n private fileService: FilesService,\n private zip: ZipService\n ) {}\n\n /**\n * Lists all files of the given type.\n * Forces a cache renewal.\n */\n async listFiles<T extends string = StaticAssetType>(type: T): Promise<StaticAsset[]> {\n return this.listFilesCached(type, true);\n }\n\n /**\n * Lists all files of the given type.\n * Uses a cache.\n */\n async listFilesCached<T extends string = StaticAssetType>(\n type: T,\n forceCacheRenewal = false\n ): Promise<StaticAsset[]> {\n if (!this.listFilesCachePromises[type] || forceCacheRenewal) {\n this.listFilesCachePromises[type] = this._listFiles(type);\n }\n\n return await this.listFilesCachePromises[type];\n }\n\n /**\n * Clones all assets of the given type from parent tenants into the current tenant.\n */\n async cloneAssetsIntoTenant<T extends string = StaticAssetType>(type: T) {\n const { data: apps } = await this.appService.list({\n availability: ApplicationAvailability.SHARED,\n type: ApplicationType.HOSTED,\n pageSize: 2000\n });\n\n const tenantIdsFromOtherAssetApps = apps\n .filter(app => {\n const result =\n app.owner?.tenant?.id &&\n app.owner?.tenant?.id !== this.appstate.currentTenant.value?.name &&\n app.contextPath === this.getContextPath(type, app.owner?.tenant?.id);\n return result;\n })\n .map(app => app.owner?.tenant?.id);\n\n let filesOfCurrentTenant = await this._listFiles(type);\n\n let filesToUpload = new Array<File>();\n\n const oldAssets = new Array<StaticAsset>();\n\n for (const tenantId of tenantIdsFromOtherAssetApps) {\n try {\n const filesOfApp = await this.getContentJSONForType(type, tenantId);\n oldAssets.push(...filesOfApp);\n for (const file of filesOfApp) {\n try {\n const response = await this.fetchClient.fetch(file.path);\n if (response.status !== 200) {\n console.warn(`Failed to get file \"${file.fileName}\" from path ${file.path}`);\n continue;\n }\n const fileContent = await response.blob();\n\n // remove existing file with same name\n filesOfCurrentTenant = filesOfCurrentTenant.filter(\n existingFile => existingFile.fileName !== file.fileName\n );\n filesToUpload = filesToUpload.filter(\n existingFile => existingFile.name !== file.fileName\n );\n\n filesToUpload.push(new File([fileContent], file.fileName, { type: file.type }));\n } catch (e) {\n console.warn(`Failed to add file \"${file.fileName}\" from tenant ${tenantId}`, e);\n }\n }\n } catch (e) {\n console.warn(`Failed to get asset files from tenant ${tenantId}`);\n }\n }\n\n if (!filesToUpload.length) {\n return;\n }\n\n const newAssets = await this.addFilesToStaticAssets(type, filesToUpload, filesOfCurrentTenant);\n\n return { oldAssets, newAssets };\n }\n\n /**\n * Adds the given files to the static assets of the given type.\n */\n async addFilesToStaticAssets<T extends string = StaticAssetType>(\n type: T,\n files: DroppedFile[] | File[],\n existingFiles: StaticAsset[],\n throwErrorOnExistingFiles = true\n ) {\n const app = await this.getAppForTenant(type);\n const filesToAddToContents = new Array<StaticAsset>();\n const addedAt = new Date().toISOString();\n const filesToUpload: {\n path: string;\n contents: Blob;\n }[] = [];\n for (const droppedFile of files) {\n const file = droppedFile instanceof File ? droppedFile : droppedFile.file;\n const fileName = file.name.toLowerCase().replace(/[^a-zA-Z0-9\\-\\_\\.]/g, '');\n const newFile = new File([file], fileName, {\n type: file.type,\n lastModified: file.lastModified\n });\n if (existingFiles.find(existingFile => existingFile.fileName === fileName)) {\n if (throwErrorOnExistingFiles) {\n throw Error(`File with name \"${fileName}\" is already existing.`);\n }\n existingFiles = existingFiles.filter(existingFile => existingFile.fileName !== fileName);\n }\n const fileExtension = file.name.split('.').pop();\n filesToAddToContents.push({\n addedAt,\n lastModified: file.lastModified,\n extension: fileExtension,\n fileName: fileName,\n originalFileName: file.name,\n path: `/apps/public/${app.contextPath}/${fileName}`,\n size: newFile.size,\n type: newFile.type,\n hashSum: await this.fileService.getHashSumOfFile(newFile)\n });\n filesToUpload.push({\n contents: newFile,\n path: fileName\n });\n }\n\n const newFiles: StaticAsset[] = [...existingFiles, ...filesToAddToContents];\n filesToUpload.push({\n contents: new File([JSON.stringify(newFiles)], this.fileNames.contents, {\n type: 'application/json'\n }),\n path: this.fileNames.contents\n });\n await this.appService.binary(app).updateFiles(filesToUpload);\n await this.ensureAddedFilesArePresent(filesToAddToContents);\n return newFiles;\n }\n\n /**\n * Gets the static assets app for the given type.\n */\n async getAppForTenant<T extends string = StaticAssetType>(type: T): Promise<IApplication> {\n const contextPath = this.getContextPath(type);\n\n const { data: manifest } = await this.appService.getManifestOfContextPath(contextPath);\n if (!manifest.id) {\n throw Error(`No app with path ${contextPath} found.`);\n }\n const { data: app } = await this.appService.detail(manifest.id);\n if (app.owner?.tenant.id !== this.appstate.currentTenant.value?.name) {\n throw Error(`No app with path ${contextPath} found that is owned by the current tenant.`);\n }\n return app;\n }\n\n private getContentJSONForType<T extends string = StaticAssetType>(\n type: T,\n appendTenantId?: string | null | undefined\n ) {\n const path = this.getPathForContent(type, appendTenantId);\n return this.getContentJSONFromPath(path);\n }\n\n private async getContentJSONFromPath(path: string): Promise<StaticAsset[]> {\n const response = await this.fetchClient.fetch(path);\n if (response.status !== 200) {\n throw Error(`Failed to retrieve ${path}`);\n }\n\n const content = await response.json();\n return content;\n }\n\n private async ensureAddedFilesArePresent(files: StaticAsset[]) {\n for (const file of files) {\n await this.ensureFileIsPresent(file);\n }\n }\n\n private async ensureFileIsPresent(file: StaticAsset) {\n for (let i = 0; i < 12; i++) {\n try {\n const currentDate = new Date();\n const response = await this.fetchClient.fetch(\n `${file.path}?nocache=${currentDate.getTime()}`\n );\n if (response.status !== 200) {\n await this.sleep(5_000);\n continue;\n }\n\n const blob = await response.blob();\n const hashSum = await this.fileService.getHashSumOfFile(blob);\n if (hashSum !== file.hashSum) {\n await this.sleep(5_000);\n continue;\n }\n return;\n } catch (e) {\n console.warn(e);\n continue;\n }\n }\n throw Error(`Unable to retrieve file from ${file.path}`);\n }\n\n private sleep(duration: number): Promise<void> {\n return new Promise<void>(resolve => setTimeout(() => resolve(), duration));\n }\n\n private async _listFiles<T extends string = StaticAssetType>(type: T) {\n try {\n const files = await this.getContentJSONForType(type);\n return files;\n } catch (e) {\n console.warn(e);\n }\n\n return await this.createEmptyApp(type);\n }\n\n private getPathForContent<T extends string = StaticAssetType>(\n type: T,\n appendTenantId?: string | null | undefined\n ): string {\n const currentDate = new Date();\n const contextPath = this.getContextPath(type, appendTenantId);\n return `/apps/public/${contextPath}/${\n this.fileNames.contents\n }?nocache=${currentDate.getTime()}`;\n }\n\n private getContextPath<T extends string = StaticAssetType>(\n type: T,\n appendTenantId?: string | false | undefined\n ) {\n let contextPath: string = type + '-assets';\n const tenantId =\n appendTenantId === undefined ? this.appstate.currentTenant.value?.name : appendTenantId;\n if (tenantId) {\n contextPath = `${contextPath}-${tenantId}`;\n }\n return contextPath;\n }\n\n private async createEmptyApp<T extends string = StaticAssetType>(\n type: T,\n appendTenantId?: string | null | undefined\n ): Promise<StaticAsset[]> {\n const name = this.getContextPath(type, false);\n const contextPath = this.getContextPath(type, appendTenantId);\n const key = `${contextPath}-key`;\n const manifest: IApplication = {\n name,\n contextPath,\n key,\n description: `Application containing static assets used for ${type}.`,\n noAppSwitcher: true,\n type: ApplicationType.HOSTED,\n availability: ApplicationAvailability.MARKET\n };\n\n const content = [];\n const zipFile = await this.zip.createZip([\n {\n fileName: `${this.fileNames.manifest}`,\n blob: new Blob([JSON.stringify(manifest)])\n },\n {\n fileName: `${this.fileNames.contents}`,\n blob: new Blob([JSON.stringify(content)])\n }\n ]);\n const { data: app } = await this.appService.create(manifest);\n const { data: appBinary } = await this.appService\n .binary(app)\n .upload(zipFile, `empty-${name}.zip`);\n await this.appService.update({ id: app.id, activeVersionId: appBinary.id as string });\n return [];\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -275,7 +275,7 @@ class DatapointsGraphWidgetConfigComponent {
275
275
  ChartAlarmsService,
276
276
  ChartHelpersService,
277
277
  WidgetTimeContextDateRangeService
278
- ], viewQueries: [{ propertyName: "previewMapSet", first: true, predicate: ["dataPointsGraphPreview"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<form [formGroup]=\"formGroup\">\n <c8y-datapoint-selection-list\n class=\"bg-component separator-bottom d-block\"\n name=\"datapoints\"\n [minActiveCount]=\"1\"\n [defaultFormOptions]=\"datapointSelectDefaultFormOptions\"\n [config]=\"datapointSelectionConfig\"\n formControlName=\"datapoints\"\n ></c8y-datapoint-selection-list>\n\n <c8y-alarm-event-selection-list\n class=\"bg-component separator-bottom d-block\"\n name=\"alarms\"\n formControlName=\"alarms\"\n [timelineType]=\"'ALARM'\"\n [datapoints]=\"config?.datapoints\"\n ></c8y-alarm-event-selection-list>\n\n <c8y-alarm-event-selection-list\n class=\"bg-inherit\"\n name=\"events\"\n formControlName=\"events\"\n [timelineType]=\"'EVENT'\"\n [datapoints]=\"config?.datapoints\"\n ></c8y-alarm-event-selection-list>\n\n <div class=\"form-group form-group-sm\">\n <label\n [title]=\"'Number of decimal places' | translate\"\n translate\n >\n Number of decimal places\n </label>\n <input\n class=\"form-control\"\n name=\"numberOfDecimalPlaces\"\n type=\"number\"\n formControlName=\"numberOfDecimalPlaces\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 1 }\"\n />\n <c8y-messages\n [show]=\"\n formGroup.controls?.numberOfDecimalPlaces?.touched &&\n formGroup?.controls?.numberOfDecimalPlaces?.errors\n \"\n ></c8y-messages>\n </div>\n <c8y-form-group class=\"form-group-sm\">\n <label>\n {{ 'Data point legend display' | translate }}\n </label>\n <div\n class=\"c8y-select-wrapper\"\n [formGroup]=\"formGroup\"\n >\n <select\n class=\"form-control\"\n [title]=\"'Data point legend display' | translate\"\n id=\"dataPointLegendDisplay\"\n formControlName=\"dataPointLegendDisplay\"\n >\n @for (option of legendDisplayOptions; track option) {\n <option [ngValue]=\"option.value\">\n {{ option.label | translate }}\n </option>\n }\n </select>\n </div>\n </c8y-form-group>\n</form>\n\n<form\n class=\"d-block p-t-8\"\n [formGroup]=\"formGroup\"\n>\n <label>{{ 'Display options' | translate }}</label>\n <fieldset class=\"c8y-fieldset m-b-24 m-t-0\">\n <legend>{{ 'Axis' | translate }}</legend>\n <c8y-form-group class=\"p-b-16 m-b-0 p-t-8 form-group-sm\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Y-axis helper lines' | translate\"\n >\n <input\n name=\"yAxisSplitLines\"\n type=\"checkbox\"\n formControlName=\"yAxisSplitLines\"\n />\n <span></span>\n <span translate>Y-axis helper lines</span>\n </label>\n <label\n class=\"c8y-checkbox\"\n [title]=\"'X-axis helper lines' | translate\"\n >\n <input\n name=\"xAxisSplitLines\"\n type=\"checkbox\"\n formControlName=\"xAxisSplitLines\"\n />\n <span></span>\n <span translate>X-axis helper lines</span>\n </label>\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Merge matching data points into single axis' | translate\"\n >\n <input\n name=\"mergeMatchingDatapoints\"\n type=\"checkbox\"\n formControlName=\"mergeMatchingDatapoints\"\n />\n <span></span>\n <span translate>Merge matching data points into single axis</span>\n <button\n class=\"btn-help\"\n [attr.aria-label]=\"\n 'Data points with the same min and max values will be merged into a single axis. The values must be defined in the data point configuration.'\n | translate\n \"\n [popover]=\"\n 'Data points with the same min and max values will be merged into a single axis. The values must be defined in the data point configuration.'\n | translate\n \"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n (click)=\"$event.stopPropagation()\"\n [adaptivePosition]=\"false\"\n ></button>\n </label>\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Force merge all data points into single axis' | translate\"\n >\n <input\n name=\"forceMergeDatapoints\"\n type=\"checkbox\"\n formControlName=\"forceMergeDatapoints\"\n />\n <span></span>\n <span translate>Force merge all data points into a single axis</span>\n <button\n class=\"btn-help\"\n [attr.aria-label]=\"\n 'All axes will be force merged to a single axis with the scale being set to the max and min value of all axes. It\\'s recommended to use this option for data points with similar values.'\n | translate\n \"\n [popover]=\"\n 'All axes will be force merged to a single axis with the scale being set to the max and min value of all axes. It\\'s recommended to use this option for data points with similar values.'\n | translate\n \"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n (click)=\"$event.stopPropagation()\"\n [adaptivePosition]=\"false\"\n ></button>\n </label>\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Set Y-axis start to 0' | translate\"\n >\n <input\n name=\"setYaxisStartToZero\"\n type=\"checkbox\"\n formControlName=\"setYaxisStartToZero\"\n />\n <span></span>\n <span translate>Set Y-axis start to 0</span>\n <button\n class=\"btn-help\"\n [attr.aria-label]=\"\n 'Sets the Y-axis minimum to 0 for all data points with positive values. If any data point includes negative values, the axis may still extend below 0. Explicitly configured minimum and maximum values take precedence over this option.'\n | translate\n \"\n [popover]=\"\n 'Sets the Y-axis minimum to 0 for all data points with positive values. If any data point includes negative values, the axis may still extend below 0. Explicitly configured minimum and maximum values take precedence over this option.'\n | translate\n \"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n (click)=\"$event.stopPropagation()\"\n [adaptivePosition]=\"false\"\n ></button>\n </label>\n </c8y-form-group>\n </fieldset>\n <fieldset class=\"c8y-fieldset m-b-24 m-t-0\">\n <legend>{{ 'Alarms & events' | translate }}</legend>\n <c8y-form-group class=\"p-b-16 m-b-0 p-t-8 form-group-sm\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show vertical line on every occurrence' | translate\"\n >\n <input\n name=\"displayMarkedLine\"\n type=\"checkbox\"\n formControlName=\"displayMarkedLine\"\n />\n <span></span>\n <span translate>Show vertical line on every occurrence</span>\n </label>\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show icon when triggered' | translate\"\n >\n <input\n name=\"displayMarkedPoint\"\n type=\"checkbox\"\n formControlName=\"displayMarkedPoint\"\n />\n <span></span>\n <span translate>Show icon when triggered</span>\n @if (alarmsOrEventsHaveNoMatchingDps) {\n <button\n class=\"btn-clean m-l-8\"\n [attr.aria-label]=\"\n 'Some alarms or events have no matching data points. No icons will be shown for them.'\n | translate\n \"\n [tooltip]=\"\n 'Some alarms or events have no matching data points. No icons will be shown for them.'\n | translate\n \"\n container=\"body\"\n type=\"button\"\n (click)=\"$event.stopPropagation()\"\n [adaptivePosition]=\"false\"\n >\n <i\n class=\"text-warning\"\n c8yIcon=\"exclamation-triangle\"\n ></i>\n </button>\n }\n </label>\n </c8y-form-group>\n </fieldset>\n <fieldset class=\"c8y-fieldset m-b-24 m-t-0\">\n <legend>{{ 'Chart' | translate }}</legend>\n <c8y-form-group class=\"p-b-16 m-b-0 p-t-8 form-group-sm\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show labels and units' | translate\"\n >\n <input\n name=\"showLabelAndUnit\"\n type=\"checkbox\"\n formControlName=\"showLabelAndUnit\"\n />\n <span></span>\n <span translate>Display labels and units on Y-axis</span>\n </label>\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show slider' | translate\"\n >\n <input\n name=\"showSlider\"\n type=\"checkbox\"\n formControlName=\"showSlider\"\n />\n <span></span>\n <span translate>Show slider</span>\n </label>\n </c8y-form-group>\n </fieldset>\n</form>\n\n<ng-template #dataPointsGraphPreview>\n @if (widgetControls) {\n <c8y-global-context-widget-wrapper\n [config]=\"config\"\n [displayMode]=\"'preview'\"\n [widgetControls]=\"widgetControls\"\n ></c8y-global-context-widget-wrapper>\n }\n\n @if (activeDatapointsExists) {\n <c8y-charts\n class=\"d-block p-relative\"\n [config]=\"config\"\n [alerts]=\"alerts\"\n [chartViewContext]=\"chartViewContext\"\n (timeRangeChangeOnRealtime)=\"updateTimeRangeOnRealtime($event)\"\n (configChangeOnZoomOut)=\"updateDashboardTimeContext($event)\"\n (updateAggregatedSliderDatapoint)=\"updateAggregatedSliderDatapoint($event)\"\n ></c8y-charts>\n }\n\n @if (!activeDatapointsExists) {\n <c8y-ui-empty-state\n class=\"d-block m-t-24\"\n [icon]=\"'search'\"\n [title]=\"'No data points selected' | translate\"\n [subtitle]=\"'Select data point to render content' | translate\"\n data-cy=\"datapoints-graph-list--empty-state-no-data-point-selected\"\n >\n <p c8y-guide-docs>\n <small translate>\n Find out more in the\n <a c8y-guide-href=\"/docs/cockpit/widgets-collection/#data-point-graph\">\n user documentation\n </a>\n .\n </small>\n </p>\n </c8y-ui-empty-state>\n }\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: i2.EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "directive", type: i2.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i2.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "ngmodule", type: CoreModule }, { kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i3.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i3.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "component", type: i2.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "component", type: i2.MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage"] }, { kind: "directive", type: i2.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: i2.GuideHrefDirective, selector: "[c8y-guide-href]", inputs: ["c8y-guide-href"] }, { kind: "component", type: i2.GuideDocsComponent, selector: "[c8y-guide-docs]" }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i4.TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "directive", type: i5.PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "component", type: ChartsComponent, selector: "c8y-charts", inputs: ["config", "alerts", "chartViewContext"], outputs: ["configChangeOnZoomOut", "timeRangeChangeOnRealtime", "datapointOutOfSync", "updateAlarmsAndEvents", "isMarkedAreaEnabled", "finishLoading", "updateActiveDatapoints", "updateAggregatedSliderDatapoint"] }, { kind: "ngmodule", type: DatapointSelectorModule }, { kind: "component", type: i6.DatapointSelectionListComponent, selector: "c8y-datapoint-selection-list", inputs: ["actions", "allowDragAndDrop", "config", "defaultFormOptions", "maxActiveCount", "minActiveCount", "resolveContext", "listTitle"], outputs: ["isValid", "change"] }, { kind: "ngmodule", type: AlarmEventSelectorModule }, { kind: "component", type: i7.AlarmEventSelectionListComponent, selector: "c8y-alarm-event-selection-list", inputs: ["timelineType", "canRemove", "canEdit", "canDragAndDrop", "title", "addButtonLabel", "hideSource", "inline", "activeToggleAsSwitch", "omitProperties", "datapoints", "config"] }, { kind: "component", type: GlobalContextWidgetWrapperComponent, selector: "c8y-global-context-widget-wrapper", inputs: ["isLoading", "displayMode", "widgetControls", "controlLinks", "dashboardChildForLegacy", "config", "disableRefreshEmits"], outputs: ["globalContextChange"] }, { kind: "pipe", type: i2.C8yTranslatePipe, name: "translate" }] }); }
278
+ ], viewQueries: [{ propertyName: "previewMapSet", first: true, predicate: ["dataPointsGraphPreview"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<form [formGroup]=\"formGroup\">\n <c8y-datapoint-selection-list\n class=\"bg-component separator-bottom d-block\"\n name=\"datapoints\"\n [minActiveCount]=\"1\"\n [defaultFormOptions]=\"datapointSelectDefaultFormOptions\"\n [config]=\"datapointSelectionConfig\"\n formControlName=\"datapoints\"\n ></c8y-datapoint-selection-list>\n\n <c8y-alarm-event-selection-list\n class=\"bg-component separator-bottom d-block\"\n name=\"alarms\"\n formControlName=\"alarms\"\n [timelineType]=\"'ALARM'\"\n [datapoints]=\"config?.datapoints\"\n ></c8y-alarm-event-selection-list>\n\n <c8y-alarm-event-selection-list\n class=\"bg-inherit\"\n name=\"events\"\n formControlName=\"events\"\n [timelineType]=\"'EVENT'\"\n [datapoints]=\"config?.datapoints\"\n ></c8y-alarm-event-selection-list>\n\n <div class=\"form-group form-group-sm\">\n <label\n [title]=\"'Number of decimal places' | translate\"\n translate\n >\n Number of decimal places\n </label>\n <input\n class=\"form-control\"\n name=\"numberOfDecimalPlaces\"\n type=\"number\"\n formControlName=\"numberOfDecimalPlaces\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 1 }\"\n />\n <c8y-messages\n [show]=\"\n formGroup.controls?.numberOfDecimalPlaces?.touched &&\n formGroup?.controls?.numberOfDecimalPlaces?.errors\n \"\n ></c8y-messages>\n </div>\n <c8y-form-group class=\"form-group-sm\">\n <label>\n {{ 'Data point legend display' | translate }}\n </label>\n <div\n class=\"c8y-select-wrapper\"\n [formGroup]=\"formGroup\"\n >\n <select\n class=\"form-control\"\n [title]=\"'Data point legend display' | translate\"\n id=\"dataPointLegendDisplay\"\n formControlName=\"dataPointLegendDisplay\"\n >\n @for (option of legendDisplayOptions; track option) {\n <option [ngValue]=\"option.value\">\n {{ option.label | translate }}\n </option>\n }\n </select>\n </div>\n </c8y-form-group>\n</form>\n\n<form\n class=\"d-block p-t-8\"\n [formGroup]=\"formGroup\"\n>\n <label>{{ 'Display options' | translate }}</label>\n <fieldset class=\"c8y-fieldset m-b-24 m-t-0\">\n <legend>{{ 'Axis' | translate }}</legend>\n <c8y-form-group class=\"p-b-16 m-b-0 p-t-8 form-group-sm\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Y-axis helper lines' | translate\"\n >\n <input\n name=\"yAxisSplitLines\"\n type=\"checkbox\"\n formControlName=\"yAxisSplitLines\"\n />\n <span></span>\n <span translate>Y-axis helper lines</span>\n </label>\n <label\n class=\"c8y-checkbox\"\n [title]=\"'X-axis helper lines' | translate\"\n >\n <input\n name=\"xAxisSplitLines\"\n type=\"checkbox\"\n formControlName=\"xAxisSplitLines\"\n />\n <span></span>\n <span translate>X-axis helper lines</span>\n </label>\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Merge matching data points into single axis' | translate\"\n >\n <input\n name=\"mergeMatchingDatapoints\"\n type=\"checkbox\"\n formControlName=\"mergeMatchingDatapoints\"\n />\n <span></span>\n <span translate>Merge matching data points into single axis</span>\n <button\n class=\"btn-help\"\n [attr.aria-label]=\"\n 'Data points with the same min and max values will be merged into a single axis. The values must be defined in the data point configuration.'\n | translate\n \"\n [popover]=\"\n 'Data points with the same min and max values will be merged into a single axis. The values must be defined in the data point configuration.'\n | translate\n \"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n (click)=\"$event.stopPropagation()\"\n [adaptivePosition]=\"false\"\n ></button>\n </label>\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Force merge all data points into single axis' | translate\"\n >\n <input\n name=\"forceMergeDatapoints\"\n type=\"checkbox\"\n formControlName=\"forceMergeDatapoints\"\n />\n <span></span>\n <span translate>Force merge all data points into a single axis</span>\n <button\n class=\"btn-help\"\n [attr.aria-label]=\"\n 'All axes will be force merged to a single axis with the scale being set to the max and min value of all axes. It\\'s recommended to use this option for data points with similar values.'\n | translate\n \"\n [popover]=\"\n 'All axes will be force merged to a single axis with the scale being set to the max and min value of all axes. It\\'s recommended to use this option for data points with similar values.'\n | translate\n \"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n (click)=\"$event.stopPropagation()\"\n [adaptivePosition]=\"false\"\n ></button>\n </label>\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Set Y-axis start to 0' | translate\"\n >\n <input\n name=\"setYaxisStartToZero\"\n type=\"checkbox\"\n formControlName=\"setYaxisStartToZero\"\n />\n <span></span>\n <span translate>Set Y-axis start to 0</span>\n <button\n class=\"btn-help\"\n [attr.aria-label]=\"\n 'Sets the Y-axis minimum to 0 for all data points with positive values. If any data point includes negative values, the axis may still extend below 0. Explicitly configured minimum and maximum values take precedence over this option.'\n | translate\n \"\n [popover]=\"\n 'Sets the Y-axis minimum to 0 for all data points with positive values. If any data point includes negative values, the axis may still extend below 0. Explicitly configured minimum and maximum values take precedence over this option.'\n | translate\n \"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n (click)=\"$event.stopPropagation()\"\n [adaptivePosition]=\"false\"\n ></button>\n </label>\n </c8y-form-group>\n </fieldset>\n <fieldset class=\"c8y-fieldset m-b-24 m-t-0\">\n <legend>{{ 'Alarms & events' | translate }}</legend>\n <c8y-form-group class=\"p-b-16 m-b-0 p-t-8 form-group-sm\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show vertical line on every occurrence' | translate\"\n >\n <input\n name=\"displayMarkedLine\"\n type=\"checkbox\"\n formControlName=\"displayMarkedLine\"\n />\n <span></span>\n <span translate>Show vertical line on every occurrence</span>\n </label>\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show icon when triggered' | translate\"\n >\n <input\n name=\"displayMarkedPoint\"\n type=\"checkbox\"\n formControlName=\"displayMarkedPoint\"\n />\n <span></span>\n <span translate>Show icon when triggered</span>\n @if (alarmsOrEventsHaveNoMatchingDps) {\n <button\n class=\"btn-clean m-l-8\"\n [attr.aria-label]=\"\n 'Some alarms or events have no matching data points. No icons will be shown for them.'\n | translate\n \"\n [tooltip]=\"\n 'Some alarms or events have no matching data points. No icons will be shown for them.'\n | translate\n \"\n container=\"body\"\n type=\"button\"\n (click)=\"$event.stopPropagation()\"\n [adaptivePosition]=\"false\"\n >\n <i\n class=\"text-warning\"\n c8yIcon=\"exclamation-triangle\"\n ></i>\n </button>\n }\n </label>\n </c8y-form-group>\n </fieldset>\n <fieldset class=\"c8y-fieldset m-b-24 m-t-0\">\n <legend>{{ 'Chart' | translate }}</legend>\n <c8y-form-group class=\"p-b-16 m-b-0 p-t-8 form-group-sm\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show labels and units' | translate\"\n >\n <input\n name=\"showLabelAndUnit\"\n type=\"checkbox\"\n formControlName=\"showLabelAndUnit\"\n />\n <span></span>\n <span translate>Display labels and units on Y-axis</span>\n </label>\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show slider' | translate\"\n >\n <input\n name=\"showSlider\"\n type=\"checkbox\"\n formControlName=\"showSlider\"\n />\n <span></span>\n <span translate>Show slider</span>\n </label>\n </c8y-form-group>\n </fieldset>\n</form>\n\n<ng-template #dataPointsGraphPreview>\n @if (widgetControls) {\n <c8y-global-context-widget-wrapper\n [config]=\"config\"\n [displayMode]=\"'preview'\"\n [widgetControls]=\"widgetControls\"\n ></c8y-global-context-widget-wrapper>\n }\n\n @if (activeDatapointsExists) {\n <c8y-charts\n class=\"d-block p-relative\"\n [config]=\"config\"\n [alerts]=\"alerts\"\n [chartViewContext]=\"chartViewContext\"\n (timeRangeChangeOnRealtime)=\"updateTimeRangeOnRealtime($event)\"\n (configChangeOnZoomOut)=\"updateDashboardTimeContext($event)\"\n (updateAggregatedSliderDatapoint)=\"updateAggregatedSliderDatapoint($event)\"\n ></c8y-charts>\n }\n\n @if (!activeDatapointsExists) {\n <c8y-ui-empty-state\n class=\"d-block m-t-24\"\n [icon]=\"'search'\"\n [title]=\"'No data points selected' | translate\"\n [subtitle]=\"'Select data point to render content' | translate\"\n data-cy=\"datapoints-graph-list--empty-state-no-data-point-selected\"\n >\n <p c8y-guide-docs>\n <small translate>\n Find out more in the\n <a c8y-guide-href=\"/docs/cockpit/widgets-collection/#data-point-graph\">\n user documentation\n </a>\n .\n </small>\n </p>\n </c8y-ui-empty-state>\n }\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: i2.EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "directive", type: i2.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i2.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "ngmodule", type: CoreModule }, { kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i3.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i3.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "component", type: i2.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "component", type: i2.MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage"] }, { kind: "directive", type: i2.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: i2.GuideHrefDirective, selector: "[c8y-guide-href]", inputs: ["c8y-guide-href"] }, { kind: "component", type: i2.GuideDocsComponent, selector: "[c8y-guide-docs]" }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i4.TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "directive", type: i5.PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "component", type: ChartsComponent, selector: "c8y-charts", inputs: ["config", "alerts", "chartViewContext"], outputs: ["configChangeOnZoomOut", "timeRangeChangeOnRealtime", "datapointOutOfSync", "datapointBackInSync", "updateAlarmsAndEvents", "isMarkedAreaEnabled", "finishLoading", "updateActiveDatapoints", "updateAggregatedSliderDatapoint"] }, { kind: "ngmodule", type: DatapointSelectorModule }, { kind: "component", type: i6.DatapointSelectionListComponent, selector: "c8y-datapoint-selection-list", inputs: ["actions", "allowDragAndDrop", "config", "defaultFormOptions", "maxActiveCount", "minActiveCount", "resolveContext", "listTitle"], outputs: ["isValid", "change"] }, { kind: "ngmodule", type: AlarmEventSelectorModule }, { kind: "component", type: i7.AlarmEventSelectionListComponent, selector: "c8y-alarm-event-selection-list", inputs: ["timelineType", "canRemove", "canEdit", "canDragAndDrop", "title", "addButtonLabel", "hideSource", "inline", "activeToggleAsSwitch", "omitProperties", "datapoints", "config"] }, { kind: "component", type: GlobalContextWidgetWrapperComponent, selector: "c8y-global-context-widget-wrapper", inputs: ["isLoading", "displayMode", "widgetControls", "controlLinks", "dashboardChildForLegacy", "config", "disableRefreshEmits"], outputs: ["globalContextChange"] }, { kind: "pipe", type: i2.C8yTranslatePipe, name: "translate" }] }); }
279
279
  }
280
280
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatapointsGraphWidgetConfigComponent, decorators: [{
281
281
  type: Component,
@@ -615,6 +615,14 @@ class DatapointsGraphWidgetViewComponent {
615
615
  }
616
616
  this.datapointsOutOfSync.set(dpMatch, true);
617
617
  }
618
+ handleDatapointBackInSync(dpBackInSync) {
619
+ const key = (dp) => dp.__target?.id + dp.fragment + dp.series;
620
+ const dpMatch = this.displayConfig?.datapoints?.find(dp => key(dp) === key(dpBackInSync));
621
+ if (!dpMatch) {
622
+ return;
623
+ }
624
+ this.datapointsOutOfSync.delete(dpMatch);
625
+ }
618
626
  toggleMarkedArea(alarm) {
619
627
  this.enabledMarkedAreaAlarmType = alarm.filters.type;
620
628
  const params = {
@@ -726,7 +734,7 @@ class DatapointsGraphWidgetViewComponent {
726
734
  this.loadedAlarmsOrEvents = this.loadedAlarmsOrEvents.filter(aOrE => aOrE.__active && !aOrE.__hidden);
727
735
  }
728
736
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatapointsGraphWidgetViewComponent, deps: [{ token: i1$1.TranslateService }, { token: i1.ContextDashboardComponent, optional: true }, { token: i2.DynamicComponentService }, { token: i4$1.WidgetConfigMigrationService }, { token: i2.WidgetTimeContextDateRangeService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
729
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: DatapointsGraphWidgetViewComponent, isStandalone: true, selector: "c8y-datapoints-graph-widget-view", inputs: { config: "config" }, providers: [ChartEventsService, ChartAlarmsService, WidgetTimeContextDateRangeService], viewQueries: [{ propertyName: "wrapper", first: true, predicate: GlobalContextWidgetWrapperComponent, descendants: true }, { propertyName: "chartComponent", first: true, predicate: ChartsComponent, descendants: true }], ngImport: i0, template: "@if (widgetControls) {\n <c8y-global-context-widget-wrapper\n [widgetControls]=\"widgetControls\"\n [isLoading]=\"isLoading$ | async\"\n [config]=\"displayConfig\"\n [disableRefreshEmits]=\"true\"\n (globalContextChange)=\"onGlobalContextChange($event)\"\n ></c8y-global-context-widget-wrapper>\n}\n<div\n class=\"p-l-16 p-r-16\"\n style=\"min-height: 34px\"\n>\n <div class=\"d-flex gap-16 a-i-center\">\n @if (hasAtLeastOneAlarmActive) {\n <c8y-alarms-filter\n class=\"d-contents form-group-sm min-width-0\"\n (onFilterApplied)=\"filterSeverity($event)\"\n ></c8y-alarms-filter>\n }\n @if (displayConfig?.datapoints.length > 0) {\n <div class=\"d-flex a-i-center min-width-0\">\n @if (\n displayConfig.dataPointLegendDisplay === 'dropdown' ||\n (displayConfig.dataPointLegendDisplay === 'auto' && totalLegendItems > 5)\n ) {\n <c8y-select\n class=\"min-width-0 c8y-select-v2--sm\"\n aria-label=\"{{ 'Select data points, alarms or events' | translate }}\"\n [placeholder]=\"'Select data points, alarms or events' | translate\"\n [multi]=\"true\"\n [filterItems]=\"true\"\n [(ngModel)]=\"selectedItems\"\n (onSelect)=\"onItemSelected($event)\"\n (onDeselect)=\"onItemDeselected($event)\"\n >\n @for (item of selectableItems; track item.value) {\n <div\n class=\"d-flex a-i-center gap-4\"\n *c8ySelectItem=\"item.value; label: item.label\"\n >\n @if (\n !hasAtLeastOneDatapointActive &&\n item.value.type === 'DATAPOINT' &&\n item.value.original.__active\n ) {\n <i\n class=\"text-warning m-r-4\"\n c8yIcon=\"exclamation-triangle\"\n [tooltip]=\"'At least 1 data point must be active.' | translate\"\n container=\"body\"\n data-cy=\"datapoint-warning-icon\"\n [adaptivePosition]=\"false\"\n ></i>\n }\n @if (item.value.type === 'DATAPOINT') {\n <span\n class=\"circle-icon-wrapper a-s-start circle-icon-wrapper--small\"\n [style.background-color]=\"item.value.original.color\"\n ></span>\n }\n @if (item.value.type === 'ALARM') {\n <span\n class=\"circle-icon-wrapper a-s-start circle-icon-wrapper--small\"\n [style.background-color]=\"item.value.original.color\"\n >\n <i\n class=\"stroked-icon\"\n c8yIcon=\"bell\"\n ></i>\n </span>\n }\n @if (item.value.type === 'EVENT') {\n <span\n class=\"circle-icon-wrapper a-s-start circle-icon-wrapper--small\"\n [ngStyle]=\"{ 'background-color': item.value.original.color }\"\n >\n <i\n class=\"stroked-icon\"\n c8yIcon=\"online1\"\n ></i>\n </span>\n }\n <span class=\"text-truncate d-col\">\n <small class=\"text-truncate\">{{ item.value.original.label }}</small>\n <span class=\"text-muted text-10\">{{ item.value.original.__target?.name }}</span>\n </span>\n </div>\n }\n <span\n class=\"tag tag--info chip\"\n title=\"{{ selectedItem.label }}\"\n *c8ySelectedItems=\"let selectedItem\"\n >\n <button\n class=\"btn btn-xs btn-clean text-10\"\n title=\"{{ selectedItem.label }}\"\n type=\"button\"\n (click)=\"\n $event.preventDefault(); $event.stopPropagation(); onItemDeselected(selectedItem)\n \"\n >\n <i c8yIcon=\"times\"></i>\n </button>\n @if (isLastActiveDatapoint(selectedItem)) {\n <i\n class=\"text-warning a-s-start\"\n c8yIcon=\"exclamation-triangle\"\n [tooltip]=\"'At least 1 data point must be active.' | translate\"\n container=\"body\"\n data-cy=\"datapoint-warning-icon\"\n [adaptivePosition]=\"false\"\n ></i>\n }\n @if (selectedItem.value.type === 'DATAPOINT') {\n <span\n class=\"circle-icon-wrapper circle-icon-wrapper--small\"\n [style.background-color]=\"selectedItem.value?.original.color || ''\"\n ></span>\n }\n @if (selectedItem.value?.type === 'ALARM') {\n <span\n class=\"circle-icon-wrapper circle-icon-wrapper--small\"\n [style.background-color]=\"selectedItem.value?.original.color || ''\"\n >\n <i\n class=\"stroked-icon\"\n c8yIcon=\"bell\"\n ></i>\n </span>\n }\n @if (selectedItem.value?.type === 'EVENT') {\n <span\n class=\"circle-icon-wrapper circle-icon-wrapper--small\"\n [ngStyle]=\"{\n 'background-color': selectedItem.value?.original.color || ''\n }\"\n >\n <i\n class=\"stroked-icon\"\n c8yIcon=\"online1\"\n ></i>\n </span>\n }\n </span>\n </c8y-select>\n } @else {\n <div class=\"inner-scroll\">\n <div class=\"p-t-4 d-flex a-i-start gap-8\">\n @for (datapoint of displayConfig.datapoints; track datapoint) {\n @if (datapoint.retrievalError) {\n <i\n class=\"text-warning m-l-4\"\n c8yIcon=\"exclamation-triangle\"\n [tooltip]=\"'Data point no longer exists.' | translate\"\n container=\"body\"\n [adaptivePosition]=\"false\"\n ></i>\n } @else {\n <div\n class=\"c8y-datapoint-pill pill--sm flex-no-shrink\"\n title=\"{{ datapoint.label }} - {{ datapoint.__target.name }}\"\n [ngClass]=\"{ active: datapoint.__active }\"\n >\n @if (!hasAtLeastOneDatapointActive && datapoint.__active) {\n <i\n class=\"text-warning m-l-4\"\n c8yIcon=\"exclamation-triangle\"\n [tooltip]=\"'At least 1 data point must be active.' | translate\"\n container=\"body\"\n data-cy=\"datapoint-warning-icon\"\n [adaptivePosition]=\"false\"\n ></i>\n }\n <button\n class=\"c8y-datapoint-pill__btn\"\n title=\"{{\n (datapoint.__active ? hideDatapointLabel : showDatapointLabel) | translate\n }} \"\n type=\"button\"\n data-cy=\"datapoint-toggle-visibility-btn\"\n (click)=\"toggleChart(datapoint)\"\n >\n <i\n class=\"icon-14\"\n [c8yIcon]=\"datapoint.__active ? 'eye text-primary' : 'eye-slash text-muted'\"\n ></i>\n </button>\n <div class=\"c8y-datapoint-pill__label c8y-datapoint-pill__btn\">\n <i\n class=\"m-r-4 icon-14\"\n c8yIcon=\"circle\"\n [ngStyle]=\"{\n color: datapoint.color\n }\"\n ></i>\n <span\n class=\"text-truncate\"\n [ngClass]=\"{ 'text-muted': !datapoint.__active }\"\n >\n <span class=\"text-truncate text-bold\">\n {{ datapoint.label }}\n </span>\n <small class=\"text-muted text-10\">\n {{ datapoint.__target.name }}\n </small>\n </span>\n @if (datapointsOutOfSync.get(datapoint)) {\n <i\n class=\"text-warning m-l-4\"\n c8yIcon=\"exclamation-triangle\"\n [tooltip]=\"\n 'Measurements received for this data point may be out of sync.'\n | translate\n \"\n container=\"body\"\n [adaptivePosition]=\"false\"\n ></i>\n }\n </div>\n </div>\n }\n }\n\n @for (alarm of alarms; track alarm) {\n <div\n class=\"c8y-alarm-pill pill--sm flex-no-shrink\"\n title=\"{{ alarm.filters.type }} \"\n >\n @if (displayConfig?.activeAlarmTypesOutOfRange?.includes(alarm.filters.type)) {\n <i\n class=\"text-warning m-l-4\"\n c8yIcon=\"exclamation-triangle\"\n [tooltip]=\"\n 'Alarm of this type is currently active and outside of the selected time range'\n | translate\n \"\n container=\"body\"\n [adaptivePosition]=\"false\"\n ></i>\n }\n <button\n class=\"c8y-alarm-pill__btn\"\n title=\"{{ alarm.filters.type }} \"\n type=\"button\"\n (click)=\"toggleAlarmEventType(alarm)\"\n >\n <i\n class=\"icon-14\"\n [c8yIcon]=\"\n alarm.__hidden || !alarm.__active\n ? 'eye-slash text-muted'\n : 'eye text-primary'\n \"\n ></i>\n </button>\n <button\n class=\"c8y-alarm-pill__label c8y-alarm-pill__btn\"\n (click)=\"toggleMarkedArea(alarm)\"\n [ngClass]=\"{\n active:\n !isMarkedAreaEnabled && alarm.filters.type === enabledMarkedAreaAlarmType\n }\"\n >\n <span\n class=\"circle-icon-wrapper circle-icon-wrapper--small m-r-4\"\n [style.background-color]=\"alarm.color\"\n >\n <i\n class=\"stroked-icon\"\n c8yIcon=\"bell\"\n ></i>\n </span>\n <span\n class=\"text-truncate\"\n [ngClass]=\"{ 'text-muted': alarm.__hidden }\"\n >\n <span class=\"text-truncate text-bold\">\n {{ alarm.label || alarm.filters.type }}\n </span>\n <small class=\"text-muted text-10\">\n {{ alarm.__target.name }}\n </small>\n </span>\n </button>\n </div>\n }\n\n @for (event of events; track event) {\n <div\n class=\"c8y-event-pill pill--sm flex-no-shrink\"\n title=\"{{ event.filters.type }}\"\n >\n <button\n class=\"c8y-event-pill__btn\"\n title=\"{{ event.filters.type }} \"\n type=\"button\"\n (click)=\"toggleAlarmEventType(event)\"\n >\n <i\n class=\"icon-14\"\n [c8yIcon]=\"\n event.__hidden || !event.__active\n ? 'eye-slash text-muted'\n : 'eye text-primary'\n \"\n ></i>\n </button>\n <div class=\"c8y-event-pill__label c8y-event-pill__btn\">\n <span\n class=\"circle-icon-wrapper circle-icon-wrapper--small m-r-4\"\n [ngStyle]=\"{ 'background-color': event.color }\"\n >\n <i\n class=\"stroked-icon\"\n c8yIcon=\"online1\"\n ></i>\n </span>\n <span\n class=\"text-truncate text-bold\"\n [ngClass]=\"{ 'text-muted': event.__hidden }\"\n >\n <span class=\"text-truncate\">\n {{ event.label || event.filters.type }}\n </span>\n <small class=\"text-muted text-10\">\n {{ event.__target.name }}\n </small>\n </span>\n </div>\n </div>\n }\n </div>\n </div>\n <button\n class=\"btn-help btn-help--sm m-r-8\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"legendHelpTemplate\"\n placement=\"left\"\n triggers=\"focus\"\n container=\"body\"\n [adaptivePosition]=\"true\"\n ></button>\n }\n </div>\n }\n <button\n class=\"btn btn-default btn-sm flex-no-shrink m-l-auto\"\n [attr.aria-label]=\"'Save as image' | translate\"\n tooltip=\"{{ 'Save as image' | translate }}\"\n container=\"body\"\n type=\"button\"\n (click)=\"chartComponent.saveAsImage()\"\n [adaptivePosition]=\"false\"\n >\n <i\n class=\"icon-14\"\n c8yIcon=\"image-file-checked\"\n ></i>\n </button>\n </div>\n</div>\n\n@if (displayConfig !== null) {\n <c8y-charts\n #chart\n [config]=\"displayConfig\"\n [alerts]=\"alerts\"\n [chartViewContext]=\"chartViewContext\"\n (updateAlarmsAndEvents)=\"updateAlarmsAndEvents($event)\"\n (configChangeOnZoomOut)=\"updateDashboardTimeContext($event)\"\n (datapointOutOfSync)=\"handleDatapointOutOfSync($event)\"\n (finishLoading)=\"this.isLoading$.next($event)\"\n (isMarkedAreaEnabled)=\"isMarkedAreaEnabled = $event\"\n ></c8y-charts>\n}\n\n<ng-template #legendHelpTemplate>\n <div [innerHTML]=\"legendHelp\"></div>\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: A11yModule }, { kind: "ngmodule", type: CommonModule$1 }, { kind: "directive", type: i5$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i5$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: ChartsComponent, selector: "c8y-charts", inputs: ["config", "alerts", "chartViewContext"], outputs: ["configChangeOnZoomOut", "timeRangeChangeOnRealtime", "datapointOutOfSync", "updateAlarmsAndEvents", "isMarkedAreaEnabled", "finishLoading", "updateActiveDatapoints", "updateAggregatedSliderDatapoint"] }, { kind: "ngmodule", type: CoreModule }, { kind: "directive", type: i2.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "component", type: i2.SelectComponent, selector: "c8y-select", inputs: ["placeholder", "items", "selected", "container", "multi", "canSelectWithSpace", "disabled", "autoClose", "insideClick", "required", "canDeselect", "name", "icon", "filterItems"], outputs: ["onSelect", "onDeselect", "onIconClick"] }, { kind: "directive", type: i2.SelectItemDirective, selector: "[c8ySelectItem]", inputs: ["c8ySelectItem", "c8ySelectItemLabel"] }, { kind: "directive", type: i2.SelectedItemsDirective, selector: "[c8ySelectedItems]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i4.TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "ngmodule", type: BsDropdownModule }, { kind: "ngmodule", type: PopoverModule }, { kind: "directive", type: i5.PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "ngmodule", type: AlarmsModule }, { kind: "component", type: i9.AlarmsFilterComponent, selector: "c8y-alarms-filter", inputs: ["contextSourceId"], outputs: ["onFilterApplied"] }, { kind: "component", type: GlobalContextWidgetWrapperComponent, selector: "c8y-global-context-widget-wrapper", inputs: ["isLoading", "displayMode", "widgetControls", "controlLinks", "dashboardChildForLegacy", "config", "disableRefreshEmits"], outputs: ["globalContextChange"] }, { kind: "pipe", type: i5$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i2.C8yTranslatePipe, name: "translate" }] }); }
737
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: DatapointsGraphWidgetViewComponent, isStandalone: true, selector: "c8y-datapoints-graph-widget-view", inputs: { config: "config" }, providers: [ChartEventsService, ChartAlarmsService, WidgetTimeContextDateRangeService], viewQueries: [{ propertyName: "wrapper", first: true, predicate: GlobalContextWidgetWrapperComponent, descendants: true }, { propertyName: "chartComponent", first: true, predicate: ChartsComponent, descendants: true }], ngImport: i0, template: "@if (widgetControls) {\n <c8y-global-context-widget-wrapper\n [widgetControls]=\"widgetControls\"\n [isLoading]=\"isLoading$ | async\"\n [config]=\"displayConfig\"\n [disableRefreshEmits]=\"true\"\n (globalContextChange)=\"onGlobalContextChange($event)\"\n ></c8y-global-context-widget-wrapper>\n}\n<div\n class=\"p-l-16 p-r-16\"\n style=\"min-height: 34px\"\n>\n <div class=\"d-flex gap-16 a-i-center\">\n @if (hasAtLeastOneAlarmActive) {\n <c8y-alarms-filter\n class=\"d-contents form-group-sm min-width-0\"\n (onFilterApplied)=\"filterSeverity($event)\"\n ></c8y-alarms-filter>\n }\n @if (displayConfig?.datapoints.length > 0) {\n <div class=\"d-flex a-i-center min-width-0\">\n @if (\n displayConfig.dataPointLegendDisplay === 'dropdown' ||\n (displayConfig.dataPointLegendDisplay === 'auto' && totalLegendItems > 5)\n ) {\n <c8y-select\n class=\"min-width-0 c8y-select-v2--sm\"\n aria-label=\"{{ 'Select data points, alarms or events' | translate }}\"\n [placeholder]=\"'Select data points, alarms or events' | translate\"\n [multi]=\"true\"\n [filterItems]=\"true\"\n [(ngModel)]=\"selectedItems\"\n (onSelect)=\"onItemSelected($event)\"\n (onDeselect)=\"onItemDeselected($event)\"\n >\n @for (item of selectableItems; track item.value) {\n <div\n class=\"d-flex a-i-center gap-4\"\n *c8ySelectItem=\"item.value; label: item.label\"\n >\n @if (\n !hasAtLeastOneDatapointActive &&\n item.value.type === 'DATAPOINT' &&\n item.value.original.__active\n ) {\n <i\n class=\"text-warning m-r-4\"\n c8yIcon=\"exclamation-triangle\"\n [tooltip]=\"'At least 1 data point must be active.' | translate\"\n container=\"body\"\n data-cy=\"datapoint-warning-icon\"\n [adaptivePosition]=\"false\"\n ></i>\n }\n @if (item.value.type === 'DATAPOINT') {\n <span\n class=\"circle-icon-wrapper a-s-start circle-icon-wrapper--small\"\n [style.background-color]=\"item.value.original.color\"\n ></span>\n }\n @if (item.value.type === 'ALARM') {\n <span\n class=\"circle-icon-wrapper a-s-start circle-icon-wrapper--small\"\n [style.background-color]=\"item.value.original.color\"\n >\n <i\n class=\"stroked-icon\"\n c8yIcon=\"bell\"\n ></i>\n </span>\n }\n @if (item.value.type === 'EVENT') {\n <span\n class=\"circle-icon-wrapper a-s-start circle-icon-wrapper--small\"\n [ngStyle]=\"{ 'background-color': item.value.original.color }\"\n >\n <i\n class=\"stroked-icon\"\n c8yIcon=\"online1\"\n ></i>\n </span>\n }\n <span class=\"text-truncate d-col\">\n <small class=\"text-truncate\">{{ item.value.original.label }}</small>\n <span class=\"text-muted text-10\">{{ item.value.original.__target?.name }}</span>\n </span>\n </div>\n }\n <span\n class=\"tag tag--info chip\"\n title=\"{{ selectedItem.label }}\"\n *c8ySelectedItems=\"let selectedItem\"\n >\n <button\n class=\"btn btn-xs btn-clean text-10\"\n title=\"{{ selectedItem.label }}\"\n type=\"button\"\n (click)=\"\n $event.preventDefault(); $event.stopPropagation(); onItemDeselected(selectedItem)\n \"\n >\n <i c8yIcon=\"times\"></i>\n </button>\n @if (isLastActiveDatapoint(selectedItem)) {\n <i\n class=\"text-warning a-s-start\"\n c8yIcon=\"exclamation-triangle\"\n [tooltip]=\"'At least 1 data point must be active.' | translate\"\n container=\"body\"\n data-cy=\"datapoint-warning-icon\"\n [adaptivePosition]=\"false\"\n ></i>\n }\n @if (selectedItem.value.type === 'DATAPOINT') {\n <span\n class=\"circle-icon-wrapper circle-icon-wrapper--small\"\n [style.background-color]=\"selectedItem.value?.original.color || ''\"\n ></span>\n }\n @if (selectedItem.value?.type === 'ALARM') {\n <span\n class=\"circle-icon-wrapper circle-icon-wrapper--small\"\n [style.background-color]=\"selectedItem.value?.original.color || ''\"\n >\n <i\n class=\"stroked-icon\"\n c8yIcon=\"bell\"\n ></i>\n </span>\n }\n @if (selectedItem.value?.type === 'EVENT') {\n <span\n class=\"circle-icon-wrapper circle-icon-wrapper--small\"\n [ngStyle]=\"{\n 'background-color': selectedItem.value?.original.color || ''\n }\"\n >\n <i\n class=\"stroked-icon\"\n c8yIcon=\"online1\"\n ></i>\n </span>\n }\n </span>\n </c8y-select>\n } @else {\n <div class=\"inner-scroll\">\n <div class=\"p-t-4 d-flex a-i-start gap-8\">\n @for (datapoint of displayConfig.datapoints; track datapoint) {\n @if (datapoint.retrievalError) {\n <i\n class=\"text-warning m-l-4\"\n c8yIcon=\"exclamation-triangle\"\n [tooltip]=\"'Data point no longer exists.' | translate\"\n container=\"body\"\n [adaptivePosition]=\"false\"\n ></i>\n } @else {\n <div\n class=\"c8y-datapoint-pill pill--sm flex-no-shrink\"\n title=\"{{ datapoint.label }} - {{ datapoint.__target.name }}\"\n [ngClass]=\"{ active: datapoint.__active }\"\n >\n @if (!hasAtLeastOneDatapointActive && datapoint.__active) {\n <i\n class=\"text-warning m-l-4\"\n c8yIcon=\"exclamation-triangle\"\n [tooltip]=\"'At least 1 data point must be active.' | translate\"\n container=\"body\"\n data-cy=\"datapoint-warning-icon\"\n [adaptivePosition]=\"false\"\n ></i>\n }\n <button\n class=\"c8y-datapoint-pill__btn\"\n title=\"{{\n (datapoint.__active ? hideDatapointLabel : showDatapointLabel) | translate\n }} \"\n type=\"button\"\n data-cy=\"datapoint-toggle-visibility-btn\"\n (click)=\"toggleChart(datapoint)\"\n >\n <i\n class=\"icon-14\"\n [c8yIcon]=\"datapoint.__active ? 'eye text-primary' : 'eye-slash text-muted'\"\n ></i>\n </button>\n <div class=\"c8y-datapoint-pill__label c8y-datapoint-pill__btn\">\n <i\n class=\"m-r-4 icon-14\"\n c8yIcon=\"circle\"\n [ngStyle]=\"{\n color: datapoint.color\n }\"\n ></i>\n <span\n class=\"text-truncate\"\n [ngClass]=\"{ 'text-muted': !datapoint.__active }\"\n >\n <span class=\"text-truncate text-bold\">\n {{ datapoint.label }}\n </span>\n <small class=\"text-muted text-10\">\n {{ datapoint.__target.name }}\n </small>\n </span>\n @if (datapointsOutOfSync.get(datapoint)) {\n <i\n class=\"text-warning m-l-4\"\n c8yIcon=\"exclamation-triangle\"\n [tooltip]=\"\n 'Measurements received for this data point may be out of sync.'\n | translate\n \"\n container=\"body\"\n [adaptivePosition]=\"false\"\n ></i>\n }\n </div>\n </div>\n }\n }\n\n @for (alarm of alarms; track alarm) {\n <div\n class=\"c8y-alarm-pill pill--sm flex-no-shrink\"\n title=\"{{ alarm.filters.type }} \"\n >\n @if (displayConfig?.activeAlarmTypesOutOfRange?.includes(alarm.filters.type)) {\n <i\n class=\"text-warning m-l-4\"\n c8yIcon=\"exclamation-triangle\"\n [tooltip]=\"\n 'Alarm of this type is currently active and outside of the selected time range'\n | translate\n \"\n container=\"body\"\n [adaptivePosition]=\"false\"\n ></i>\n }\n <button\n class=\"c8y-alarm-pill__btn\"\n title=\"{{ alarm.filters.type }} \"\n type=\"button\"\n (click)=\"toggleAlarmEventType(alarm)\"\n >\n <i\n class=\"icon-14\"\n [c8yIcon]=\"\n alarm.__hidden || !alarm.__active\n ? 'eye-slash text-muted'\n : 'eye text-primary'\n \"\n ></i>\n </button>\n <button\n class=\"c8y-alarm-pill__label c8y-alarm-pill__btn\"\n (click)=\"toggleMarkedArea(alarm)\"\n [ngClass]=\"{\n active:\n !isMarkedAreaEnabled && alarm.filters.type === enabledMarkedAreaAlarmType\n }\"\n >\n <span\n class=\"circle-icon-wrapper circle-icon-wrapper--small m-r-4\"\n [style.background-color]=\"alarm.color\"\n >\n <i\n class=\"stroked-icon\"\n c8yIcon=\"bell\"\n ></i>\n </span>\n <span\n class=\"text-truncate\"\n [ngClass]=\"{ 'text-muted': alarm.__hidden }\"\n >\n <span class=\"text-truncate text-bold\">\n {{ alarm.label || alarm.filters.type }}\n </span>\n <small class=\"text-muted text-10\">\n {{ alarm.__target.name }}\n </small>\n </span>\n </button>\n </div>\n }\n\n @for (event of events; track event) {\n <div\n class=\"c8y-event-pill pill--sm flex-no-shrink\"\n title=\"{{ event.filters.type }}\"\n >\n <button\n class=\"c8y-event-pill__btn\"\n title=\"{{ event.filters.type }} \"\n type=\"button\"\n (click)=\"toggleAlarmEventType(event)\"\n >\n <i\n class=\"icon-14\"\n [c8yIcon]=\"\n event.__hidden || !event.__active\n ? 'eye-slash text-muted'\n : 'eye text-primary'\n \"\n ></i>\n </button>\n <div class=\"c8y-event-pill__label c8y-event-pill__btn\">\n <span\n class=\"circle-icon-wrapper circle-icon-wrapper--small m-r-4\"\n [ngStyle]=\"{ 'background-color': event.color }\"\n >\n <i\n class=\"stroked-icon\"\n c8yIcon=\"online1\"\n ></i>\n </span>\n <span\n class=\"text-truncate text-bold\"\n [ngClass]=\"{ 'text-muted': event.__hidden }\"\n >\n <span class=\"text-truncate\">\n {{ event.label || event.filters.type }}\n </span>\n <small class=\"text-muted text-10\">\n {{ event.__target.name }}\n </small>\n </span>\n </div>\n </div>\n }\n </div>\n </div>\n <button\n class=\"btn-help btn-help--sm m-r-8\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"legendHelpTemplate\"\n placement=\"left\"\n triggers=\"focus\"\n container=\"body\"\n [adaptivePosition]=\"true\"\n ></button>\n }\n </div>\n }\n <button\n class=\"btn btn-default btn-sm flex-no-shrink m-l-auto\"\n [attr.aria-label]=\"'Save as image' | translate\"\n tooltip=\"{{ 'Save as image' | translate }}\"\n container=\"body\"\n type=\"button\"\n (click)=\"chartComponent.saveAsImage()\"\n [adaptivePosition]=\"false\"\n >\n <i\n class=\"icon-14\"\n c8yIcon=\"image-file-checked\"\n ></i>\n </button>\n </div>\n</div>\n\n@if (displayConfig !== null) {\n <c8y-charts\n #chart\n [config]=\"displayConfig\"\n [alerts]=\"alerts\"\n [chartViewContext]=\"chartViewContext\"\n (updateAlarmsAndEvents)=\"updateAlarmsAndEvents($event)\"\n (configChangeOnZoomOut)=\"updateDashboardTimeContext($event)\"\n (datapointOutOfSync)=\"handleDatapointOutOfSync($event)\"\n (datapointBackInSync)=\"handleDatapointBackInSync($event)\"\n (finishLoading)=\"this.isLoading$.next($event)\"\n (isMarkedAreaEnabled)=\"isMarkedAreaEnabled = $event\"\n ></c8y-charts>\n}\n\n<ng-template #legendHelpTemplate>\n <div [innerHTML]=\"legendHelp\"></div>\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: A11yModule }, { kind: "ngmodule", type: CommonModule$1 }, { kind: "directive", type: i5$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i5$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: ChartsComponent, selector: "c8y-charts", inputs: ["config", "alerts", "chartViewContext"], outputs: ["configChangeOnZoomOut", "timeRangeChangeOnRealtime", "datapointOutOfSync", "datapointBackInSync", "updateAlarmsAndEvents", "isMarkedAreaEnabled", "finishLoading", "updateActiveDatapoints", "updateAggregatedSliderDatapoint"] }, { kind: "ngmodule", type: CoreModule }, { kind: "directive", type: i2.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "component", type: i2.SelectComponent, selector: "c8y-select", inputs: ["placeholder", "items", "selected", "container", "multi", "canSelectWithSpace", "disabled", "autoClose", "insideClick", "required", "canDeselect", "name", "icon", "filterItems"], outputs: ["onSelect", "onDeselect", "onIconClick"] }, { kind: "directive", type: i2.SelectItemDirective, selector: "[c8ySelectItem]", inputs: ["c8ySelectItem", "c8ySelectItemLabel"] }, { kind: "directive", type: i2.SelectedItemsDirective, selector: "[c8ySelectedItems]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i4.TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "ngmodule", type: BsDropdownModule }, { kind: "ngmodule", type: PopoverModule }, { kind: "directive", type: i5.PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "ngmodule", type: AlarmsModule }, { kind: "component", type: i9.AlarmsFilterComponent, selector: "c8y-alarms-filter", inputs: ["contextSourceId"], outputs: ["onFilterApplied"] }, { kind: "component", type: GlobalContextWidgetWrapperComponent, selector: "c8y-global-context-widget-wrapper", inputs: ["isLoading", "displayMode", "widgetControls", "controlLinks", "dashboardChildForLegacy", "config", "disableRefreshEmits"], outputs: ["globalContextChange"] }, { kind: "pipe", type: i5$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i2.C8yTranslatePipe, name: "translate" }] }); }
730
738
  }
731
739
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatapointsGraphWidgetViewComponent, decorators: [{
732
740
  type: Component,
@@ -743,7 +751,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
743
751
  SelectComponent,
744
752
  SelectItemDirective,
745
753
  SelectedItemsDirective
746
- ], providers: [ChartEventsService, ChartAlarmsService, WidgetTimeContextDateRangeService], template: "@if (widgetControls) {\n <c8y-global-context-widget-wrapper\n [widgetControls]=\"widgetControls\"\n [isLoading]=\"isLoading$ | async\"\n [config]=\"displayConfig\"\n [disableRefreshEmits]=\"true\"\n (globalContextChange)=\"onGlobalContextChange($event)\"\n ></c8y-global-context-widget-wrapper>\n}\n<div\n class=\"p-l-16 p-r-16\"\n style=\"min-height: 34px\"\n>\n <div class=\"d-flex gap-16 a-i-center\">\n @if (hasAtLeastOneAlarmActive) {\n <c8y-alarms-filter\n class=\"d-contents form-group-sm min-width-0\"\n (onFilterApplied)=\"filterSeverity($event)\"\n ></c8y-alarms-filter>\n }\n @if (displayConfig?.datapoints.length > 0) {\n <div class=\"d-flex a-i-center min-width-0\">\n @if (\n displayConfig.dataPointLegendDisplay === 'dropdown' ||\n (displayConfig.dataPointLegendDisplay === 'auto' && totalLegendItems > 5)\n ) {\n <c8y-select\n class=\"min-width-0 c8y-select-v2--sm\"\n aria-label=\"{{ 'Select data points, alarms or events' | translate }}\"\n [placeholder]=\"'Select data points, alarms or events' | translate\"\n [multi]=\"true\"\n [filterItems]=\"true\"\n [(ngModel)]=\"selectedItems\"\n (onSelect)=\"onItemSelected($event)\"\n (onDeselect)=\"onItemDeselected($event)\"\n >\n @for (item of selectableItems; track item.value) {\n <div\n class=\"d-flex a-i-center gap-4\"\n *c8ySelectItem=\"item.value; label: item.label\"\n >\n @if (\n !hasAtLeastOneDatapointActive &&\n item.value.type === 'DATAPOINT' &&\n item.value.original.__active\n ) {\n <i\n class=\"text-warning m-r-4\"\n c8yIcon=\"exclamation-triangle\"\n [tooltip]=\"'At least 1 data point must be active.' | translate\"\n container=\"body\"\n data-cy=\"datapoint-warning-icon\"\n [adaptivePosition]=\"false\"\n ></i>\n }\n @if (item.value.type === 'DATAPOINT') {\n <span\n class=\"circle-icon-wrapper a-s-start circle-icon-wrapper--small\"\n [style.background-color]=\"item.value.original.color\"\n ></span>\n }\n @if (item.value.type === 'ALARM') {\n <span\n class=\"circle-icon-wrapper a-s-start circle-icon-wrapper--small\"\n [style.background-color]=\"item.value.original.color\"\n >\n <i\n class=\"stroked-icon\"\n c8yIcon=\"bell\"\n ></i>\n </span>\n }\n @if (item.value.type === 'EVENT') {\n <span\n class=\"circle-icon-wrapper a-s-start circle-icon-wrapper--small\"\n [ngStyle]=\"{ 'background-color': item.value.original.color }\"\n >\n <i\n class=\"stroked-icon\"\n c8yIcon=\"online1\"\n ></i>\n </span>\n }\n <span class=\"text-truncate d-col\">\n <small class=\"text-truncate\">{{ item.value.original.label }}</small>\n <span class=\"text-muted text-10\">{{ item.value.original.__target?.name }}</span>\n </span>\n </div>\n }\n <span\n class=\"tag tag--info chip\"\n title=\"{{ selectedItem.label }}\"\n *c8ySelectedItems=\"let selectedItem\"\n >\n <button\n class=\"btn btn-xs btn-clean text-10\"\n title=\"{{ selectedItem.label }}\"\n type=\"button\"\n (click)=\"\n $event.preventDefault(); $event.stopPropagation(); onItemDeselected(selectedItem)\n \"\n >\n <i c8yIcon=\"times\"></i>\n </button>\n @if (isLastActiveDatapoint(selectedItem)) {\n <i\n class=\"text-warning a-s-start\"\n c8yIcon=\"exclamation-triangle\"\n [tooltip]=\"'At least 1 data point must be active.' | translate\"\n container=\"body\"\n data-cy=\"datapoint-warning-icon\"\n [adaptivePosition]=\"false\"\n ></i>\n }\n @if (selectedItem.value.type === 'DATAPOINT') {\n <span\n class=\"circle-icon-wrapper circle-icon-wrapper--small\"\n [style.background-color]=\"selectedItem.value?.original.color || ''\"\n ></span>\n }\n @if (selectedItem.value?.type === 'ALARM') {\n <span\n class=\"circle-icon-wrapper circle-icon-wrapper--small\"\n [style.background-color]=\"selectedItem.value?.original.color || ''\"\n >\n <i\n class=\"stroked-icon\"\n c8yIcon=\"bell\"\n ></i>\n </span>\n }\n @if (selectedItem.value?.type === 'EVENT') {\n <span\n class=\"circle-icon-wrapper circle-icon-wrapper--small\"\n [ngStyle]=\"{\n 'background-color': selectedItem.value?.original.color || ''\n }\"\n >\n <i\n class=\"stroked-icon\"\n c8yIcon=\"online1\"\n ></i>\n </span>\n }\n </span>\n </c8y-select>\n } @else {\n <div class=\"inner-scroll\">\n <div class=\"p-t-4 d-flex a-i-start gap-8\">\n @for (datapoint of displayConfig.datapoints; track datapoint) {\n @if (datapoint.retrievalError) {\n <i\n class=\"text-warning m-l-4\"\n c8yIcon=\"exclamation-triangle\"\n [tooltip]=\"'Data point no longer exists.' | translate\"\n container=\"body\"\n [adaptivePosition]=\"false\"\n ></i>\n } @else {\n <div\n class=\"c8y-datapoint-pill pill--sm flex-no-shrink\"\n title=\"{{ datapoint.label }} - {{ datapoint.__target.name }}\"\n [ngClass]=\"{ active: datapoint.__active }\"\n >\n @if (!hasAtLeastOneDatapointActive && datapoint.__active) {\n <i\n class=\"text-warning m-l-4\"\n c8yIcon=\"exclamation-triangle\"\n [tooltip]=\"'At least 1 data point must be active.' | translate\"\n container=\"body\"\n data-cy=\"datapoint-warning-icon\"\n [adaptivePosition]=\"false\"\n ></i>\n }\n <button\n class=\"c8y-datapoint-pill__btn\"\n title=\"{{\n (datapoint.__active ? hideDatapointLabel : showDatapointLabel) | translate\n }} \"\n type=\"button\"\n data-cy=\"datapoint-toggle-visibility-btn\"\n (click)=\"toggleChart(datapoint)\"\n >\n <i\n class=\"icon-14\"\n [c8yIcon]=\"datapoint.__active ? 'eye text-primary' : 'eye-slash text-muted'\"\n ></i>\n </button>\n <div class=\"c8y-datapoint-pill__label c8y-datapoint-pill__btn\">\n <i\n class=\"m-r-4 icon-14\"\n c8yIcon=\"circle\"\n [ngStyle]=\"{\n color: datapoint.color\n }\"\n ></i>\n <span\n class=\"text-truncate\"\n [ngClass]=\"{ 'text-muted': !datapoint.__active }\"\n >\n <span class=\"text-truncate text-bold\">\n {{ datapoint.label }}\n </span>\n <small class=\"text-muted text-10\">\n {{ datapoint.__target.name }}\n </small>\n </span>\n @if (datapointsOutOfSync.get(datapoint)) {\n <i\n class=\"text-warning m-l-4\"\n c8yIcon=\"exclamation-triangle\"\n [tooltip]=\"\n 'Measurements received for this data point may be out of sync.'\n | translate\n \"\n container=\"body\"\n [adaptivePosition]=\"false\"\n ></i>\n }\n </div>\n </div>\n }\n }\n\n @for (alarm of alarms; track alarm) {\n <div\n class=\"c8y-alarm-pill pill--sm flex-no-shrink\"\n title=\"{{ alarm.filters.type }} \"\n >\n @if (displayConfig?.activeAlarmTypesOutOfRange?.includes(alarm.filters.type)) {\n <i\n class=\"text-warning m-l-4\"\n c8yIcon=\"exclamation-triangle\"\n [tooltip]=\"\n 'Alarm of this type is currently active and outside of the selected time range'\n | translate\n \"\n container=\"body\"\n [adaptivePosition]=\"false\"\n ></i>\n }\n <button\n class=\"c8y-alarm-pill__btn\"\n title=\"{{ alarm.filters.type }} \"\n type=\"button\"\n (click)=\"toggleAlarmEventType(alarm)\"\n >\n <i\n class=\"icon-14\"\n [c8yIcon]=\"\n alarm.__hidden || !alarm.__active\n ? 'eye-slash text-muted'\n : 'eye text-primary'\n \"\n ></i>\n </button>\n <button\n class=\"c8y-alarm-pill__label c8y-alarm-pill__btn\"\n (click)=\"toggleMarkedArea(alarm)\"\n [ngClass]=\"{\n active:\n !isMarkedAreaEnabled && alarm.filters.type === enabledMarkedAreaAlarmType\n }\"\n >\n <span\n class=\"circle-icon-wrapper circle-icon-wrapper--small m-r-4\"\n [style.background-color]=\"alarm.color\"\n >\n <i\n class=\"stroked-icon\"\n c8yIcon=\"bell\"\n ></i>\n </span>\n <span\n class=\"text-truncate\"\n [ngClass]=\"{ 'text-muted': alarm.__hidden }\"\n >\n <span class=\"text-truncate text-bold\">\n {{ alarm.label || alarm.filters.type }}\n </span>\n <small class=\"text-muted text-10\">\n {{ alarm.__target.name }}\n </small>\n </span>\n </button>\n </div>\n }\n\n @for (event of events; track event) {\n <div\n class=\"c8y-event-pill pill--sm flex-no-shrink\"\n title=\"{{ event.filters.type }}\"\n >\n <button\n class=\"c8y-event-pill__btn\"\n title=\"{{ event.filters.type }} \"\n type=\"button\"\n (click)=\"toggleAlarmEventType(event)\"\n >\n <i\n class=\"icon-14\"\n [c8yIcon]=\"\n event.__hidden || !event.__active\n ? 'eye-slash text-muted'\n : 'eye text-primary'\n \"\n ></i>\n </button>\n <div class=\"c8y-event-pill__label c8y-event-pill__btn\">\n <span\n class=\"circle-icon-wrapper circle-icon-wrapper--small m-r-4\"\n [ngStyle]=\"{ 'background-color': event.color }\"\n >\n <i\n class=\"stroked-icon\"\n c8yIcon=\"online1\"\n ></i>\n </span>\n <span\n class=\"text-truncate text-bold\"\n [ngClass]=\"{ 'text-muted': event.__hidden }\"\n >\n <span class=\"text-truncate\">\n {{ event.label || event.filters.type }}\n </span>\n <small class=\"text-muted text-10\">\n {{ event.__target.name }}\n </small>\n </span>\n </div>\n </div>\n }\n </div>\n </div>\n <button\n class=\"btn-help btn-help--sm m-r-8\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"legendHelpTemplate\"\n placement=\"left\"\n triggers=\"focus\"\n container=\"body\"\n [adaptivePosition]=\"true\"\n ></button>\n }\n </div>\n }\n <button\n class=\"btn btn-default btn-sm flex-no-shrink m-l-auto\"\n [attr.aria-label]=\"'Save as image' | translate\"\n tooltip=\"{{ 'Save as image' | translate }}\"\n container=\"body\"\n type=\"button\"\n (click)=\"chartComponent.saveAsImage()\"\n [adaptivePosition]=\"false\"\n >\n <i\n class=\"icon-14\"\n c8yIcon=\"image-file-checked\"\n ></i>\n </button>\n </div>\n</div>\n\n@if (displayConfig !== null) {\n <c8y-charts\n #chart\n [config]=\"displayConfig\"\n [alerts]=\"alerts\"\n [chartViewContext]=\"chartViewContext\"\n (updateAlarmsAndEvents)=\"updateAlarmsAndEvents($event)\"\n (configChangeOnZoomOut)=\"updateDashboardTimeContext($event)\"\n (datapointOutOfSync)=\"handleDatapointOutOfSync($event)\"\n (finishLoading)=\"this.isLoading$.next($event)\"\n (isMarkedAreaEnabled)=\"isMarkedAreaEnabled = $event\"\n ></c8y-charts>\n}\n\n<ng-template #legendHelpTemplate>\n <div [innerHTML]=\"legendHelp\"></div>\n</ng-template>\n" }]
754
+ ], providers: [ChartEventsService, ChartAlarmsService, WidgetTimeContextDateRangeService], template: "@if (widgetControls) {\n <c8y-global-context-widget-wrapper\n [widgetControls]=\"widgetControls\"\n [isLoading]=\"isLoading$ | async\"\n [config]=\"displayConfig\"\n [disableRefreshEmits]=\"true\"\n (globalContextChange)=\"onGlobalContextChange($event)\"\n ></c8y-global-context-widget-wrapper>\n}\n<div\n class=\"p-l-16 p-r-16\"\n style=\"min-height: 34px\"\n>\n <div class=\"d-flex gap-16 a-i-center\">\n @if (hasAtLeastOneAlarmActive) {\n <c8y-alarms-filter\n class=\"d-contents form-group-sm min-width-0\"\n (onFilterApplied)=\"filterSeverity($event)\"\n ></c8y-alarms-filter>\n }\n @if (displayConfig?.datapoints.length > 0) {\n <div class=\"d-flex a-i-center min-width-0\">\n @if (\n displayConfig.dataPointLegendDisplay === 'dropdown' ||\n (displayConfig.dataPointLegendDisplay === 'auto' && totalLegendItems > 5)\n ) {\n <c8y-select\n class=\"min-width-0 c8y-select-v2--sm\"\n aria-label=\"{{ 'Select data points, alarms or events' | translate }}\"\n [placeholder]=\"'Select data points, alarms or events' | translate\"\n [multi]=\"true\"\n [filterItems]=\"true\"\n [(ngModel)]=\"selectedItems\"\n (onSelect)=\"onItemSelected($event)\"\n (onDeselect)=\"onItemDeselected($event)\"\n >\n @for (item of selectableItems; track item.value) {\n <div\n class=\"d-flex a-i-center gap-4\"\n *c8ySelectItem=\"item.value; label: item.label\"\n >\n @if (\n !hasAtLeastOneDatapointActive &&\n item.value.type === 'DATAPOINT' &&\n item.value.original.__active\n ) {\n <i\n class=\"text-warning m-r-4\"\n c8yIcon=\"exclamation-triangle\"\n [tooltip]=\"'At least 1 data point must be active.' | translate\"\n container=\"body\"\n data-cy=\"datapoint-warning-icon\"\n [adaptivePosition]=\"false\"\n ></i>\n }\n @if (item.value.type === 'DATAPOINT') {\n <span\n class=\"circle-icon-wrapper a-s-start circle-icon-wrapper--small\"\n [style.background-color]=\"item.value.original.color\"\n ></span>\n }\n @if (item.value.type === 'ALARM') {\n <span\n class=\"circle-icon-wrapper a-s-start circle-icon-wrapper--small\"\n [style.background-color]=\"item.value.original.color\"\n >\n <i\n class=\"stroked-icon\"\n c8yIcon=\"bell\"\n ></i>\n </span>\n }\n @if (item.value.type === 'EVENT') {\n <span\n class=\"circle-icon-wrapper a-s-start circle-icon-wrapper--small\"\n [ngStyle]=\"{ 'background-color': item.value.original.color }\"\n >\n <i\n class=\"stroked-icon\"\n c8yIcon=\"online1\"\n ></i>\n </span>\n }\n <span class=\"text-truncate d-col\">\n <small class=\"text-truncate\">{{ item.value.original.label }}</small>\n <span class=\"text-muted text-10\">{{ item.value.original.__target?.name }}</span>\n </span>\n </div>\n }\n <span\n class=\"tag tag--info chip\"\n title=\"{{ selectedItem.label }}\"\n *c8ySelectedItems=\"let selectedItem\"\n >\n <button\n class=\"btn btn-xs btn-clean text-10\"\n title=\"{{ selectedItem.label }}\"\n type=\"button\"\n (click)=\"\n $event.preventDefault(); $event.stopPropagation(); onItemDeselected(selectedItem)\n \"\n >\n <i c8yIcon=\"times\"></i>\n </button>\n @if (isLastActiveDatapoint(selectedItem)) {\n <i\n class=\"text-warning a-s-start\"\n c8yIcon=\"exclamation-triangle\"\n [tooltip]=\"'At least 1 data point must be active.' | translate\"\n container=\"body\"\n data-cy=\"datapoint-warning-icon\"\n [adaptivePosition]=\"false\"\n ></i>\n }\n @if (selectedItem.value.type === 'DATAPOINT') {\n <span\n class=\"circle-icon-wrapper circle-icon-wrapper--small\"\n [style.background-color]=\"selectedItem.value?.original.color || ''\"\n ></span>\n }\n @if (selectedItem.value?.type === 'ALARM') {\n <span\n class=\"circle-icon-wrapper circle-icon-wrapper--small\"\n [style.background-color]=\"selectedItem.value?.original.color || ''\"\n >\n <i\n class=\"stroked-icon\"\n c8yIcon=\"bell\"\n ></i>\n </span>\n }\n @if (selectedItem.value?.type === 'EVENT') {\n <span\n class=\"circle-icon-wrapper circle-icon-wrapper--small\"\n [ngStyle]=\"{\n 'background-color': selectedItem.value?.original.color || ''\n }\"\n >\n <i\n class=\"stroked-icon\"\n c8yIcon=\"online1\"\n ></i>\n </span>\n }\n </span>\n </c8y-select>\n } @else {\n <div class=\"inner-scroll\">\n <div class=\"p-t-4 d-flex a-i-start gap-8\">\n @for (datapoint of displayConfig.datapoints; track datapoint) {\n @if (datapoint.retrievalError) {\n <i\n class=\"text-warning m-l-4\"\n c8yIcon=\"exclamation-triangle\"\n [tooltip]=\"'Data point no longer exists.' | translate\"\n container=\"body\"\n [adaptivePosition]=\"false\"\n ></i>\n } @else {\n <div\n class=\"c8y-datapoint-pill pill--sm flex-no-shrink\"\n title=\"{{ datapoint.label }} - {{ datapoint.__target.name }}\"\n [ngClass]=\"{ active: datapoint.__active }\"\n >\n @if (!hasAtLeastOneDatapointActive && datapoint.__active) {\n <i\n class=\"text-warning m-l-4\"\n c8yIcon=\"exclamation-triangle\"\n [tooltip]=\"'At least 1 data point must be active.' | translate\"\n container=\"body\"\n data-cy=\"datapoint-warning-icon\"\n [adaptivePosition]=\"false\"\n ></i>\n }\n <button\n class=\"c8y-datapoint-pill__btn\"\n title=\"{{\n (datapoint.__active ? hideDatapointLabel : showDatapointLabel) | translate\n }} \"\n type=\"button\"\n data-cy=\"datapoint-toggle-visibility-btn\"\n (click)=\"toggleChart(datapoint)\"\n >\n <i\n class=\"icon-14\"\n [c8yIcon]=\"datapoint.__active ? 'eye text-primary' : 'eye-slash text-muted'\"\n ></i>\n </button>\n <div class=\"c8y-datapoint-pill__label c8y-datapoint-pill__btn\">\n <i\n class=\"m-r-4 icon-14\"\n c8yIcon=\"circle\"\n [ngStyle]=\"{\n color: datapoint.color\n }\"\n ></i>\n <span\n class=\"text-truncate\"\n [ngClass]=\"{ 'text-muted': !datapoint.__active }\"\n >\n <span class=\"text-truncate text-bold\">\n {{ datapoint.label }}\n </span>\n <small class=\"text-muted text-10\">\n {{ datapoint.__target.name }}\n </small>\n </span>\n @if (datapointsOutOfSync.get(datapoint)) {\n <i\n class=\"text-warning m-l-4\"\n c8yIcon=\"exclamation-triangle\"\n [tooltip]=\"\n 'Measurements received for this data point may be out of sync.'\n | translate\n \"\n container=\"body\"\n [adaptivePosition]=\"false\"\n ></i>\n }\n </div>\n </div>\n }\n }\n\n @for (alarm of alarms; track alarm) {\n <div\n class=\"c8y-alarm-pill pill--sm flex-no-shrink\"\n title=\"{{ alarm.filters.type }} \"\n >\n @if (displayConfig?.activeAlarmTypesOutOfRange?.includes(alarm.filters.type)) {\n <i\n class=\"text-warning m-l-4\"\n c8yIcon=\"exclamation-triangle\"\n [tooltip]=\"\n 'Alarm of this type is currently active and outside of the selected time range'\n | translate\n \"\n container=\"body\"\n [adaptivePosition]=\"false\"\n ></i>\n }\n <button\n class=\"c8y-alarm-pill__btn\"\n title=\"{{ alarm.filters.type }} \"\n type=\"button\"\n (click)=\"toggleAlarmEventType(alarm)\"\n >\n <i\n class=\"icon-14\"\n [c8yIcon]=\"\n alarm.__hidden || !alarm.__active\n ? 'eye-slash text-muted'\n : 'eye text-primary'\n \"\n ></i>\n </button>\n <button\n class=\"c8y-alarm-pill__label c8y-alarm-pill__btn\"\n (click)=\"toggleMarkedArea(alarm)\"\n [ngClass]=\"{\n active:\n !isMarkedAreaEnabled && alarm.filters.type === enabledMarkedAreaAlarmType\n }\"\n >\n <span\n class=\"circle-icon-wrapper circle-icon-wrapper--small m-r-4\"\n [style.background-color]=\"alarm.color\"\n >\n <i\n class=\"stroked-icon\"\n c8yIcon=\"bell\"\n ></i>\n </span>\n <span\n class=\"text-truncate\"\n [ngClass]=\"{ 'text-muted': alarm.__hidden }\"\n >\n <span class=\"text-truncate text-bold\">\n {{ alarm.label || alarm.filters.type }}\n </span>\n <small class=\"text-muted text-10\">\n {{ alarm.__target.name }}\n </small>\n </span>\n </button>\n </div>\n }\n\n @for (event of events; track event) {\n <div\n class=\"c8y-event-pill pill--sm flex-no-shrink\"\n title=\"{{ event.filters.type }}\"\n >\n <button\n class=\"c8y-event-pill__btn\"\n title=\"{{ event.filters.type }} \"\n type=\"button\"\n (click)=\"toggleAlarmEventType(event)\"\n >\n <i\n class=\"icon-14\"\n [c8yIcon]=\"\n event.__hidden || !event.__active\n ? 'eye-slash text-muted'\n : 'eye text-primary'\n \"\n ></i>\n </button>\n <div class=\"c8y-event-pill__label c8y-event-pill__btn\">\n <span\n class=\"circle-icon-wrapper circle-icon-wrapper--small m-r-4\"\n [ngStyle]=\"{ 'background-color': event.color }\"\n >\n <i\n class=\"stroked-icon\"\n c8yIcon=\"online1\"\n ></i>\n </span>\n <span\n class=\"text-truncate text-bold\"\n [ngClass]=\"{ 'text-muted': event.__hidden }\"\n >\n <span class=\"text-truncate\">\n {{ event.label || event.filters.type }}\n </span>\n <small class=\"text-muted text-10\">\n {{ event.__target.name }}\n </small>\n </span>\n </div>\n </div>\n }\n </div>\n </div>\n <button\n class=\"btn-help btn-help--sm m-r-8\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"legendHelpTemplate\"\n placement=\"left\"\n triggers=\"focus\"\n container=\"body\"\n [adaptivePosition]=\"true\"\n ></button>\n }\n </div>\n }\n <button\n class=\"btn btn-default btn-sm flex-no-shrink m-l-auto\"\n [attr.aria-label]=\"'Save as image' | translate\"\n tooltip=\"{{ 'Save as image' | translate }}\"\n container=\"body\"\n type=\"button\"\n (click)=\"chartComponent.saveAsImage()\"\n [adaptivePosition]=\"false\"\n >\n <i\n class=\"icon-14\"\n c8yIcon=\"image-file-checked\"\n ></i>\n </button>\n </div>\n</div>\n\n@if (displayConfig !== null) {\n <c8y-charts\n #chart\n [config]=\"displayConfig\"\n [alerts]=\"alerts\"\n [chartViewContext]=\"chartViewContext\"\n (updateAlarmsAndEvents)=\"updateAlarmsAndEvents($event)\"\n (configChangeOnZoomOut)=\"updateDashboardTimeContext($event)\"\n (datapointOutOfSync)=\"handleDatapointOutOfSync($event)\"\n (datapointBackInSync)=\"handleDatapointBackInSync($event)\"\n (finishLoading)=\"this.isLoading$.next($event)\"\n (isMarkedAreaEnabled)=\"isMarkedAreaEnabled = $event\"\n ></c8y-charts>\n}\n\n<ng-template #legendHelpTemplate>\n <div [innerHTML]=\"legendHelp\"></div>\n</ng-template>\n" }]
747
755
  }], ctorParameters: () => [{ type: i1$1.TranslateService }, { type: i1.ContextDashboardComponent, decorators: [{
748
756
  type: Optional
749
757
  }] }, { type: i2.DynamicComponentService }, { type: i4$1.WidgetConfigMigrationService }, { type: i2.WidgetTimeContextDateRangeService }, { type: i0.ChangeDetectorRef }], propDecorators: { config: [{