@c8y/ngx-components 1024.18.1 → 1024.18.10

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.
Files changed (87) hide show
  1. package/fesm2022/c8y-ngx-components-actility-device-registration.mjs +1 -1
  2. package/fesm2022/c8y-ngx-components-actility-device-registration.mjs.map +1 -1
  3. package/fesm2022/c8y-ngx-components-asset-properties.mjs +67 -20
  4. package/fesm2022/c8y-ngx-components-asset-properties.mjs.map +1 -1
  5. package/fesm2022/c8y-ngx-components-assets-navigator.mjs +75 -13
  6. package/fesm2022/c8y-ngx-components-assets-navigator.mjs.map +1 -1
  7. package/fesm2022/c8y-ngx-components-branding-shared-lazy-add-branding-modal.mjs +2 -4
  8. package/fesm2022/c8y-ngx-components-branding-shared-lazy-add-branding-modal.mjs.map +1 -1
  9. package/fesm2022/c8y-ngx-components-branding-shared-lazy.mjs +6 -8
  10. package/fesm2022/c8y-ngx-components-branding-shared-lazy.mjs.map +1 -1
  11. package/fesm2022/c8y-ngx-components-context-dashboard.mjs +98 -19
  12. package/fesm2022/c8y-ngx-components-context-dashboard.mjs.map +1 -1
  13. package/fesm2022/c8y-ngx-components-dashboard-manager.mjs +4 -4
  14. package/fesm2022/c8y-ngx-components-dashboard-manager.mjs.map +1 -1
  15. package/fesm2022/c8y-ngx-components-datapoint-explorer-view.mjs +7 -7
  16. package/fesm2022/c8y-ngx-components-datapoint-explorer-view.mjs.map +1 -1
  17. package/fesm2022/c8y-ngx-components-datapoints-export-selector.mjs +5 -3
  18. package/fesm2022/c8y-ngx-components-datapoints-export-selector.mjs.map +1 -1
  19. package/fesm2022/c8y-ngx-components-echart-models.mjs.map +1 -1
  20. package/fesm2022/c8y-ngx-components-echart.mjs +52 -7
  21. package/fesm2022/c8y-ngx-components-echart.mjs.map +1 -1
  22. package/fesm2022/c8y-ngx-components-ecosystem.mjs +1 -1
  23. package/fesm2022/c8y-ngx-components-ecosystem.mjs.map +1 -1
  24. package/fesm2022/c8y-ngx-components-file-preview.mjs +2 -2
  25. package/fesm2022/c8y-ngx-components-file-preview.mjs.map +1 -1
  26. package/fesm2022/c8y-ngx-components-icon-selector.mjs +6 -6
  27. package/fesm2022/c8y-ngx-components-icon-selector.mjs.map +1 -1
  28. package/fesm2022/c8y-ngx-components-loriot-device-registration.mjs +1 -1
  29. package/fesm2022/c8y-ngx-components-loriot-device-registration.mjs.map +1 -1
  30. package/fesm2022/c8y-ngx-components-operation-picker.mjs +2 -2
  31. package/fesm2022/c8y-ngx-components-operation-picker.mjs.map +1 -1
  32. package/fesm2022/c8y-ngx-components-register-device.mjs +4 -4
  33. package/fesm2022/c8y-ngx-components-register-device.mjs.map +1 -1
  34. package/fesm2022/c8y-ngx-components-remote-access-configurations.mjs +2 -3
  35. package/fesm2022/c8y-ngx-components-remote-access-configurations.mjs.map +1 -1
  36. package/fesm2022/c8y-ngx-components-remote-access-shared.mjs +2 -2
  37. package/fesm2022/c8y-ngx-components-remote-access-shared.mjs.map +1 -1
  38. package/fesm2022/c8y-ngx-components-remote-access-ssh-remote-access-ssh-endpoint-modal.mjs +2 -2
  39. package/fesm2022/c8y-ngx-components-remote-access-ssh-remote-access-ssh-endpoint-modal.mjs.map +1 -1
  40. package/fesm2022/c8y-ngx-components-remote-access-vnc-remote-access-vnc-endpoint-modal.mjs +2 -4
  41. package/fesm2022/c8y-ngx-components-remote-access-vnc-remote-access-vnc-endpoint-modal.mjs.map +1 -1
  42. package/fesm2022/c8y-ngx-components-remote-access-vnc-vnc-viewer.mjs +2 -3
  43. package/fesm2022/c8y-ngx-components-remote-access-vnc-vnc-viewer.mjs.map +1 -1
  44. package/fesm2022/c8y-ngx-components-sigfox-device-registration.mjs +1 -1
  45. package/fesm2022/c8y-ngx-components-sigfox-device-registration.mjs.map +1 -1
  46. package/fesm2022/c8y-ngx-components-static-assets-modal.mjs +2 -2
  47. package/fesm2022/c8y-ngx-components-static-assets-modal.mjs.map +1 -1
  48. package/fesm2022/c8y-ngx-components-translation-editor-lazy.mjs +3 -4
  49. package/fesm2022/c8y-ngx-components-translation-editor-lazy.mjs.map +1 -1
  50. package/fesm2022/c8y-ngx-components-widgets-definitions-scada.mjs +10 -1
  51. package/fesm2022/c8y-ngx-components-widgets-definitions-scada.mjs.map +1 -1
  52. package/fesm2022/c8y-ngx-components-widgets-implementations-asset-table.mjs +4 -4
  53. package/fesm2022/c8y-ngx-components-widgets-implementations-asset-table.mjs.map +1 -1
  54. package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-graph.mjs +18 -3
  55. package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-graph.mjs.map +1 -1
  56. package/fesm2022/c8y-ngx-components-widgets-implementations-scada.mjs +1 -1
  57. package/fesm2022/c8y-ngx-components-widgets-implementations-scada.mjs.map +1 -1
  58. package/fesm2022/c8y-ngx-components.mjs +59 -19
  59. package/fesm2022/c8y-ngx-components.mjs.map +1 -1
  60. package/locales/de.po +3 -2
  61. package/locales/es.po +3 -3
  62. package/locales/fr.po +3 -3
  63. package/locales/ja_JP.po +2 -2
  64. package/locales/ko.po +3 -3
  65. package/locales/locales.pot +1 -1
  66. package/locales/nl.po +3 -3
  67. package/locales/pl.po +2 -3
  68. package/locales/pt_BR.po +2 -3
  69. package/locales/zh_CN.po +2 -2
  70. package/locales/zh_TW.po +3 -3
  71. package/package.json +1 -1
  72. package/types/c8y-ngx-components-asset-properties.d.ts +9 -1
  73. package/types/c8y-ngx-components-asset-properties.d.ts.map +1 -1
  74. package/types/c8y-ngx-components-assets-navigator.d.ts +30 -3
  75. package/types/c8y-ngx-components-assets-navigator.d.ts.map +1 -1
  76. package/types/c8y-ngx-components-branding-shared-lazy-add-branding-modal.d.ts.map +1 -1
  77. package/types/c8y-ngx-components-context-dashboard.d.ts +44 -1
  78. package/types/c8y-ngx-components-context-dashboard.d.ts.map +1 -1
  79. package/types/c8y-ngx-components-echart-models.d.ts +9 -0
  80. package/types/c8y-ngx-components-echart-models.d.ts.map +1 -1
  81. package/types/c8y-ngx-components-echart.d.ts +32 -5
  82. package/types/c8y-ngx-components-echart.d.ts.map +1 -1
  83. package/types/c8y-ngx-components-remote-access-vnc-remote-access-vnc-endpoint-modal.d.ts.map +1 -1
  84. package/types/c8y-ngx-components-widgets-implementations-datapoints-graph.d.ts +6 -0
  85. package/types/c8y-ngx-components-widgets-implementations-datapoints-graph.d.ts.map +1 -1
  86. package/types/c8y-ngx-components.d.ts +50 -2
  87. package/types/c8y-ngx-components.d.ts.map +1 -1
@@ -150,11 +150,11 @@ class FilePreviewComponent {
150
150
  return 'unsupported';
151
151
  }
152
152
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: FilePreviewComponent, deps: [{ token: DOWNLOAD_EMITTER }, { token: i1.BsModalRef }, { token: i1.BsModalService }, { token: i2.DomSanitizer }, { token: i3.FilesService }, { token: i3.AlertService }, { token: i3.GainsightService }], target: i0.ɵɵFactoryTarget.Component }); }
153
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.19", type: FilePreviewComponent, isStandalone: true, selector: "c8y-file-preview", inputs: { mo: { classPropertyName: "mo", publicName: "mo", isSignal: true, isRequired: true, transformFunction: null }, downloadFn: { classPropertyName: "downloadFn", publicName: "downloadFn", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (contentType() !== 'unsupported') {\n <div\n class=\"d-inline-block\"\n (click)=\"openModal(modalTemplate)\"\n >\n <div #customButtonRef>\n <ng-content select=\"[customButton]\"></ng-content>\n </div>\n\n @if (!customButtonRef.children.length) {\n <button\n class=\"btn btn-default btn-icon\"\n [title]=\"'Preview file' | translate\"\n type=\"button\"\n >\n <i c8yIcon=\"search\"></i>\n </button>\n }\n </div>\n}\n\n<ng-template #modalTemplate>\n <c8y-modal\n class=\"text-break-word\"\n [title]=\"mo().name\"\n [customFooter]=\"false\"\n (onClose)=\"modalRef.hide()\"\n (onDismiss)=\"modalRef.hide()\"\n [labels]=\"{ cancel: '', ok: 'Close' | translate }\"\n >\n @if (!dataUrl) {\n <div class=\"text-center d-block\">\n <c8y-loading\n layout=\"application\"\n [progress]=\"progress.percentage\"\n ></c8y-loading>\n {{\n BUFFERING_STATUS_TEXT\n | translate\n : {\n totalBytes: progress.totalBytes | bytes,\n bufferedBytes: progress.bufferedBytes | bytes,\n percentage: progress.percentage,\n speed: progress.bytesPerSecond | bytes\n }\n }}\n </div>\n }\n\n @if (dataUrl) {\n <div id=\"modal-body\">\n @switch (contentType()) {\n @case ('image') {\n <img\n class=\"fit-w\"\n alt=\"safeDataUrl\"\n [src]=\"safeDataUrl\"\n />\n }\n @case ('video') {\n <video\n class=\"fit-w\"\n controls\n autoplay\n >\n <source [src]=\"safeDataUrl\" />\n {{ 'Your browser does not support the video tag.' | translate }}\n </video>\n }\n @case ('text') {\n <!-- @TODO: Implement text viewer-->\n text\n }\n @case ('json') {\n <!-- @TODO: Implement json viewer-->\n json\n }\n }\n </div>\n }\n </c8y-modal>\n</ng-template>\n", dependencies: [{ kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "component", type: ModalComponent, selector: "c8y-modal", inputs: ["disabled", "close", "dismiss", "title", "body", "customFooter", "headerClasses", "labels"], outputs: ["onDismiss", "onClose"] }, { kind: "component", type: LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "progressLabel", "message"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: BytesPipe, name: "bytes" }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
153
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.19", type: FilePreviewComponent, isStandalone: true, selector: "c8y-file-preview", inputs: { mo: { classPropertyName: "mo", publicName: "mo", isSignal: true, isRequired: true, transformFunction: null }, downloadFn: { classPropertyName: "downloadFn", publicName: "downloadFn", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (contentType() !== 'unsupported') {\n <div\n class=\"d-inline-block\"\n (click)=\"openModal(modalTemplate)\"\n >\n <div #customButtonRef>\n <ng-content select=\"[customButton]\"></ng-content>\n </div>\n\n @if (!customButtonRef.children.length) {\n <button\n class=\"btn btn-default btn-icon\"\n [title]=\"'Preview file' | translate\"\n type=\"button\"\n >\n <i c8yIcon=\"search\"></i>\n </button>\n }\n </div>\n}\n\n<ng-template #modalTemplate>\n <c8y-modal\n class=\"text-break-word\"\n [title]=\"mo().name\"\n [customFooter]=\"false\"\n [flushBody]=\"true\"\n (onClose)=\"modalRef.hide()\"\n (onDismiss)=\"modalRef.hide()\"\n [labels]=\"{ cancel: '', ok: 'Close' | translate }\"\n >\n @if (!dataUrl) {\n <div class=\"text-center d-block\">\n <c8y-loading\n layout=\"application\"\n [progress]=\"progress.percentage\"\n ></c8y-loading>\n {{\n BUFFERING_STATUS_TEXT\n | translate\n : {\n totalBytes: progress.totalBytes | bytes,\n bufferedBytes: progress.bufferedBytes | bytes,\n percentage: progress.percentage,\n speed: progress.bytesPerSecond | bytes\n }\n }}\n </div>\n }\n\n @if (dataUrl) {\n <div id=\"modal-body\">\n @switch (contentType()) {\n @case ('image') {\n <img\n class=\"fit-w\"\n alt=\"safeDataUrl\"\n [src]=\"safeDataUrl\"\n />\n }\n @case ('video') {\n <video\n class=\"fit-w\"\n controls\n autoplay\n >\n <source [src]=\"safeDataUrl\" />\n {{ 'Your browser does not support the video tag.' | translate }}\n </video>\n }\n @case ('text') {\n <!-- @TODO: Implement text viewer-->\n text\n }\n @case ('json') {\n <!-- @TODO: Implement json viewer-->\n json\n }\n }\n </div>\n }\n </c8y-modal>\n</ng-template>\n", dependencies: [{ kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "component", type: ModalComponent, selector: "c8y-modal", inputs: ["disabled", "close", "dismiss", "title", "body", "customFooter", "headerClasses", "flushBody", "labels"], outputs: ["onDismiss", "onClose"] }, { kind: "component", type: LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "progressLabel", "message"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: BytesPipe, name: "bytes" }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
154
154
  }
155
155
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: FilePreviewComponent, decorators: [{
156
156
  type: Component,
157
- args: [{ selector: 'c8y-file-preview', imports: [IconDirective, ModalComponent, LoadingComponent, C8yTranslatePipe, BytesPipe], changeDetection: ChangeDetectionStrategy.Eager, template: "@if (contentType() !== 'unsupported') {\n <div\n class=\"d-inline-block\"\n (click)=\"openModal(modalTemplate)\"\n >\n <div #customButtonRef>\n <ng-content select=\"[customButton]\"></ng-content>\n </div>\n\n @if (!customButtonRef.children.length) {\n <button\n class=\"btn btn-default btn-icon\"\n [title]=\"'Preview file' | translate\"\n type=\"button\"\n >\n <i c8yIcon=\"search\"></i>\n </button>\n }\n </div>\n}\n\n<ng-template #modalTemplate>\n <c8y-modal\n class=\"text-break-word\"\n [title]=\"mo().name\"\n [customFooter]=\"false\"\n (onClose)=\"modalRef.hide()\"\n (onDismiss)=\"modalRef.hide()\"\n [labels]=\"{ cancel: '', ok: 'Close' | translate }\"\n >\n @if (!dataUrl) {\n <div class=\"text-center d-block\">\n <c8y-loading\n layout=\"application\"\n [progress]=\"progress.percentage\"\n ></c8y-loading>\n {{\n BUFFERING_STATUS_TEXT\n | translate\n : {\n totalBytes: progress.totalBytes | bytes,\n bufferedBytes: progress.bufferedBytes | bytes,\n percentage: progress.percentage,\n speed: progress.bytesPerSecond | bytes\n }\n }}\n </div>\n }\n\n @if (dataUrl) {\n <div id=\"modal-body\">\n @switch (contentType()) {\n @case ('image') {\n <img\n class=\"fit-w\"\n alt=\"safeDataUrl\"\n [src]=\"safeDataUrl\"\n />\n }\n @case ('video') {\n <video\n class=\"fit-w\"\n controls\n autoplay\n >\n <source [src]=\"safeDataUrl\" />\n {{ 'Your browser does not support the video tag.' | translate }}\n </video>\n }\n @case ('text') {\n <!-- @TODO: Implement text viewer-->\n text\n }\n @case ('json') {\n <!-- @TODO: Implement json viewer-->\n json\n }\n }\n </div>\n }\n </c8y-modal>\n</ng-template>\n" }]
157
+ args: [{ selector: 'c8y-file-preview', imports: [IconDirective, ModalComponent, LoadingComponent, C8yTranslatePipe, BytesPipe], changeDetection: ChangeDetectionStrategy.Eager, template: "@if (contentType() !== 'unsupported') {\n <div\n class=\"d-inline-block\"\n (click)=\"openModal(modalTemplate)\"\n >\n <div #customButtonRef>\n <ng-content select=\"[customButton]\"></ng-content>\n </div>\n\n @if (!customButtonRef.children.length) {\n <button\n class=\"btn btn-default btn-icon\"\n [title]=\"'Preview file' | translate\"\n type=\"button\"\n >\n <i c8yIcon=\"search\"></i>\n </button>\n }\n </div>\n}\n\n<ng-template #modalTemplate>\n <c8y-modal\n class=\"text-break-word\"\n [title]=\"mo().name\"\n [customFooter]=\"false\"\n [flushBody]=\"true\"\n (onClose)=\"modalRef.hide()\"\n (onDismiss)=\"modalRef.hide()\"\n [labels]=\"{ cancel: '', ok: 'Close' | translate }\"\n >\n @if (!dataUrl) {\n <div class=\"text-center d-block\">\n <c8y-loading\n layout=\"application\"\n [progress]=\"progress.percentage\"\n ></c8y-loading>\n {{\n BUFFERING_STATUS_TEXT\n | translate\n : {\n totalBytes: progress.totalBytes | bytes,\n bufferedBytes: progress.bufferedBytes | bytes,\n percentage: progress.percentage,\n speed: progress.bytesPerSecond | bytes\n }\n }}\n </div>\n }\n\n @if (dataUrl) {\n <div id=\"modal-body\">\n @switch (contentType()) {\n @case ('image') {\n <img\n class=\"fit-w\"\n alt=\"safeDataUrl\"\n [src]=\"safeDataUrl\"\n />\n }\n @case ('video') {\n <video\n class=\"fit-w\"\n controls\n autoplay\n >\n <source [src]=\"safeDataUrl\" />\n {{ 'Your browser does not support the video tag.' | translate }}\n </video>\n }\n @case ('text') {\n <!-- @TODO: Implement text viewer-->\n text\n }\n @case ('json') {\n <!-- @TODO: Implement json viewer-->\n json\n }\n }\n </div>\n }\n </c8y-modal>\n</ng-template>\n" }]
158
158
  }], ctorParameters: () => [{ type: i0.EventEmitter, decorators: [{
159
159
  type: Inject,
160
160
  args: [DOWNLOAD_EMITTER]
@@ -1 +1 @@
1
- {"version":3,"file":"c8y-ngx-components-file-preview.mjs","sources":["../../file-preview/file-preview-product-experience.constants.ts","../../file-preview/download-emitter.token.ts","../../file-preview/file-preview.component.ts","../../file-preview/file-preview.component.html","../../file-preview/file-preview.module.ts","../../file-preview/c8y-ngx-components-file-preview.ts"],"sourcesContent":["export const PRODUCT_EXPERIENCE_FILE_PREVIEW = {\n EVENTS: {\n FILE_PREVIEW: 'filePreview'\n },\n COMPONENTS: {\n FILE_PREVIEW_COMPONENT: 'file-preview'\n },\n ACTIONS: {\n OPEN_PREVIEW: 'openPreview'\n }\n} as const;\n","import { EventEmitter, InjectionToken } from '@angular/core';\n\n/**\n * Provides InjectionToken used in FilePreviewModule. This token is used to inject EventEmitter instance, that is\n * shared among all instances of FilePreviewComponent. These components communicate between each other using this\n * emitter, which enables them to clear downloaded files from memory, if other instance of FilePreviewComponent\n * started downloading different file. Value emitted by this EventEmitter is ID of downloaded file.\n */\nexport const DOWNLOAD_EMITTER = new InjectionToken<EventEmitter<string>>('downloadEmitter');\n","import {\n Component,\n computed,\n EventEmitter,\n Inject,\n input,\n OnDestroy,\n TemplateRef,\n ChangeDetectionStrategy\n} from '@angular/core';\nimport { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';\nimport { IFetchResponse, IManagedObjectBinary } from '@c8y/client';\nimport { DomSanitizer, SafeUrl } from '@angular/platform-browser';\nimport { first, takeUntil } from 'rxjs/operators';\nimport { Subject } from 'rxjs';\nimport { gettext } from '@c8y/ngx-components/gettext';\nimport {\n AlertService,\n FilesService,\n IFetchWithProgress,\n GainsightService,\n IconDirective,\n ModalComponent,\n LoadingComponent,\n C8yTranslatePipe,\n BytesPipe\n} from '@c8y/ngx-components';\nimport { PRODUCT_EXPERIENCE_FILE_PREVIEW } from './file-preview-product-experience.constants';\nimport { DOWNLOAD_EMITTER } from './download-emitter.token';\n\n/**\n * A component which shows a button that opens a modal with the preview of a binary managed object.\n * This component requires CSP 'blob:' rule for img-src and media-src to be set.\n *\n * ```html\n * <c8y-file-preview [mo]=\"managedObject\">\n * <button customButton>Preview</button>\n * </c8y-file-preview>\n * ```\n * If no custom button provided, the component will use the default search icon button instead.\n *\n */\n@Component({\n selector: 'c8y-file-preview',\n templateUrl: './file-preview.component.html',\n imports: [IconDirective, ModalComponent, LoadingComponent, C8yTranslatePipe, BytesPipe],\n changeDetection: ChangeDetectionStrategy.Eager\n})\nexport class FilePreviewComponent implements OnDestroy {\n mo = input.required<IManagedObjectBinary>();\n downloadFn = input<() => Promise<IFetchResponse>>();\n\n contentType = computed(() => this.resolveContentType(this.mo()));\n progress: IFetchWithProgress = {\n totalBytes: 0,\n bufferedBytes: 0,\n percentage: 0,\n bytesPerSecond: 0\n };\n\n dataUrl: string;\n safeDataUrl: SafeUrl;\n BUFFERING_STATUS_TEXT = gettext(\n '{{speed}}/s - {{bufferedBytes}} of {{totalBytes}} buffered ({{percentage}}%)'\n );\n\n private destroy$: Subject<any> = new Subject();\n\n constructor(\n @Inject(DOWNLOAD_EMITTER) private downloadEmitter: EventEmitter<string>,\n public modalRef: BsModalRef,\n private modalService: BsModalService,\n private sanitizer: DomSanitizer,\n private filesService: FilesService,\n private alertService: AlertService,\n private gainsightService: GainsightService\n ) {\n downloadEmitter.pipe(takeUntil(this.destroy$)).subscribe(id => {\n if (this.dataUrl && this.mo().id !== id) {\n this.removeDownloadedFile();\n }\n });\n }\n\n ngOnDestroy(): void {\n this.removeDownloadedFile();\n this.destroy$.next(true);\n this.destroy$.complete();\n }\n\n async openModal(template: TemplateRef<any>) {\n this.modalRef = this.modalService.show(template, {\n ariaDescribedby: 'modal-body',\n ariaLabelledBy: 'modal-title',\n ignoreBackdropClick: true\n });\n\n this.gainsightService.triggerEvent(PRODUCT_EXPERIENCE_FILE_PREVIEW.EVENTS.FILE_PREVIEW, {\n component: PRODUCT_EXPERIENCE_FILE_PREVIEW.COMPONENTS.FILE_PREVIEW_COMPONENT,\n action: PRODUCT_EXPERIENCE_FILE_PREVIEW.ACTIONS.OPEN_PREVIEW\n });\n\n if (this.dataUrl) {\n return;\n }\n\n this.downloadEmitter.emit(this.mo().id);\n\n if (this.downloadFn()) {\n await this.fetchWithDownloadFn();\n return;\n }\n\n const subscription = this.filesService\n .fetchFileWithProgress$(this.mo())\n .pipe(takeUntil(this.destroy$))\n .subscribe(\n progress => {\n this.progress = progress;\n if (this.progress.blob) {\n this.dataUrl = URL.createObjectURL(progress.blob);\n this.safeDataUrl = this.sanitizer.bypassSecurityTrustUrl(this.dataUrl);\n }\n },\n e => {\n this.modalRef.hide();\n this.alertService.addServerFailure(e);\n }\n );\n\n this.modalRef.onHide.pipe(first(), takeUntil(this.destroy$)).subscribe(() => {\n subscription.unsubscribe();\n });\n }\n\n private async fetchWithDownloadFn(): Promise<void> {\n try {\n const fn = this.downloadFn();\n if (!fn) {\n return;\n }\n const response = await fn();\n const blob = await response.blob();\n this.dataUrl = URL.createObjectURL(blob);\n this.safeDataUrl = this.sanitizer.bypassSecurityTrustUrl(this.dataUrl);\n } catch (e) {\n this.modalRef.hide();\n this.alertService.addServerFailure(e);\n }\n }\n\n private removeDownloadedFile(): void {\n URL.revokeObjectURL(this.dataUrl);\n delete this.dataUrl;\n delete this.safeDataUrl;\n }\n\n private resolveContentType(\n mo: IManagedObjectBinary\n ): 'image' | 'video' | 'text' | 'json' | 'unsupported' {\n if (!mo || !mo.hasOwnProperty('c8y_IsBinary')) {\n // eslint-disable-next-line no-console\n console.warn('Provided Managed object is not binary');\n return 'unsupported';\n }\n\n if (mo.contentType.startsWith('image/')) {\n return 'image';\n } else if (mo.contentType.startsWith('video/')) {\n return 'video';\n } else if (mo.contentType.startsWith('text/')) {\n // @TODO: Implement in future\n return 'unsupported';\n } else if (mo.contentType.startsWith('application/json')) {\n // @TODO: Implement in future\n return 'unsupported';\n }\n return 'unsupported';\n }\n}\n","@if (contentType() !== 'unsupported') {\n <div\n class=\"d-inline-block\"\n (click)=\"openModal(modalTemplate)\"\n >\n <div #customButtonRef>\n <ng-content select=\"[customButton]\"></ng-content>\n </div>\n\n @if (!customButtonRef.children.length) {\n <button\n class=\"btn btn-default btn-icon\"\n [title]=\"'Preview file' | translate\"\n type=\"button\"\n >\n <i c8yIcon=\"search\"></i>\n </button>\n }\n </div>\n}\n\n<ng-template #modalTemplate>\n <c8y-modal\n class=\"text-break-word\"\n [title]=\"mo().name\"\n [customFooter]=\"false\"\n (onClose)=\"modalRef.hide()\"\n (onDismiss)=\"modalRef.hide()\"\n [labels]=\"{ cancel: '', ok: 'Close' | translate }\"\n >\n @if (!dataUrl) {\n <div class=\"text-center d-block\">\n <c8y-loading\n layout=\"application\"\n [progress]=\"progress.percentage\"\n ></c8y-loading>\n {{\n BUFFERING_STATUS_TEXT\n | translate\n : {\n totalBytes: progress.totalBytes | bytes,\n bufferedBytes: progress.bufferedBytes | bytes,\n percentage: progress.percentage,\n speed: progress.bytesPerSecond | bytes\n }\n }}\n </div>\n }\n\n @if (dataUrl) {\n <div id=\"modal-body\">\n @switch (contentType()) {\n @case ('image') {\n <img\n class=\"fit-w\"\n alt=\"safeDataUrl\"\n [src]=\"safeDataUrl\"\n />\n }\n @case ('video') {\n <video\n class=\"fit-w\"\n controls\n autoplay\n >\n <source [src]=\"safeDataUrl\" />\n {{ 'Your browser does not support the video tag.' | translate }}\n </video>\n }\n @case ('text') {\n <!-- @TODO: Implement text viewer-->\n text\n }\n @case ('json') {\n <!-- @TODO: Implement json viewer-->\n json\n }\n }\n </div>\n }\n </c8y-modal>\n</ng-template>\n","import { EventEmitter, NgModule } from '@angular/core';\nimport { CoreModule, CommonModule } from '@c8y/ngx-components';\nimport { FilePreviewComponent } from './file-preview.component';\nimport { DOWNLOAD_EMITTER } from './download-emitter.token';\n\nconst downloadEmitter = new EventEmitter<string>();\n\n@NgModule({\n imports: [CoreModule, CommonModule, FilePreviewComponent],\n exports: [FilePreviewComponent],\n providers: [\n {\n provide: DOWNLOAD_EMITTER,\n useValue: downloadEmitter\n }\n ]\n})\nexport class FilePreviewModule {}\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-file-preview.mjs","sources":["../../file-preview/file-preview-product-experience.constants.ts","../../file-preview/download-emitter.token.ts","../../file-preview/file-preview.component.ts","../../file-preview/file-preview.component.html","../../file-preview/file-preview.module.ts","../../file-preview/c8y-ngx-components-file-preview.ts"],"sourcesContent":["export const PRODUCT_EXPERIENCE_FILE_PREVIEW = {\n EVENTS: {\n FILE_PREVIEW: 'filePreview'\n },\n COMPONENTS: {\n FILE_PREVIEW_COMPONENT: 'file-preview'\n },\n ACTIONS: {\n OPEN_PREVIEW: 'openPreview'\n }\n} as const;\n","import { EventEmitter, InjectionToken } from '@angular/core';\n\n/**\n * Provides InjectionToken used in FilePreviewModule. This token is used to inject EventEmitter instance, that is\n * shared among all instances of FilePreviewComponent. These components communicate between each other using this\n * emitter, which enables them to clear downloaded files from memory, if other instance of FilePreviewComponent\n * started downloading different file. Value emitted by this EventEmitter is ID of downloaded file.\n */\nexport const DOWNLOAD_EMITTER = new InjectionToken<EventEmitter<string>>('downloadEmitter');\n","import {\n Component,\n computed,\n EventEmitter,\n Inject,\n input,\n OnDestroy,\n TemplateRef,\n ChangeDetectionStrategy\n} from '@angular/core';\nimport { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';\nimport { IFetchResponse, IManagedObjectBinary } from '@c8y/client';\nimport { DomSanitizer, SafeUrl } from '@angular/platform-browser';\nimport { first, takeUntil } from 'rxjs/operators';\nimport { Subject } from 'rxjs';\nimport { gettext } from '@c8y/ngx-components/gettext';\nimport {\n AlertService,\n FilesService,\n IFetchWithProgress,\n GainsightService,\n IconDirective,\n ModalComponent,\n LoadingComponent,\n C8yTranslatePipe,\n BytesPipe\n} from '@c8y/ngx-components';\nimport { PRODUCT_EXPERIENCE_FILE_PREVIEW } from './file-preview-product-experience.constants';\nimport { DOWNLOAD_EMITTER } from './download-emitter.token';\n\n/**\n * A component which shows a button that opens a modal with the preview of a binary managed object.\n * This component requires CSP 'blob:' rule for img-src and media-src to be set.\n *\n * ```html\n * <c8y-file-preview [mo]=\"managedObject\">\n * <button customButton>Preview</button>\n * </c8y-file-preview>\n * ```\n * If no custom button provided, the component will use the default search icon button instead.\n *\n */\n@Component({\n selector: 'c8y-file-preview',\n templateUrl: './file-preview.component.html',\n imports: [IconDirective, ModalComponent, LoadingComponent, C8yTranslatePipe, BytesPipe],\n changeDetection: ChangeDetectionStrategy.Eager\n})\nexport class FilePreviewComponent implements OnDestroy {\n mo = input.required<IManagedObjectBinary>();\n downloadFn = input<() => Promise<IFetchResponse>>();\n\n contentType = computed(() => this.resolveContentType(this.mo()));\n progress: IFetchWithProgress = {\n totalBytes: 0,\n bufferedBytes: 0,\n percentage: 0,\n bytesPerSecond: 0\n };\n\n dataUrl: string;\n safeDataUrl: SafeUrl;\n BUFFERING_STATUS_TEXT = gettext(\n '{{speed}}/s - {{bufferedBytes}} of {{totalBytes}} buffered ({{percentage}}%)'\n );\n\n private destroy$: Subject<any> = new Subject();\n\n constructor(\n @Inject(DOWNLOAD_EMITTER) private downloadEmitter: EventEmitter<string>,\n public modalRef: BsModalRef,\n private modalService: BsModalService,\n private sanitizer: DomSanitizer,\n private filesService: FilesService,\n private alertService: AlertService,\n private gainsightService: GainsightService\n ) {\n downloadEmitter.pipe(takeUntil(this.destroy$)).subscribe(id => {\n if (this.dataUrl && this.mo().id !== id) {\n this.removeDownloadedFile();\n }\n });\n }\n\n ngOnDestroy(): void {\n this.removeDownloadedFile();\n this.destroy$.next(true);\n this.destroy$.complete();\n }\n\n async openModal(template: TemplateRef<any>) {\n this.modalRef = this.modalService.show(template, {\n ariaDescribedby: 'modal-body',\n ariaLabelledBy: 'modal-title',\n ignoreBackdropClick: true\n });\n\n this.gainsightService.triggerEvent(PRODUCT_EXPERIENCE_FILE_PREVIEW.EVENTS.FILE_PREVIEW, {\n component: PRODUCT_EXPERIENCE_FILE_PREVIEW.COMPONENTS.FILE_PREVIEW_COMPONENT,\n action: PRODUCT_EXPERIENCE_FILE_PREVIEW.ACTIONS.OPEN_PREVIEW\n });\n\n if (this.dataUrl) {\n return;\n }\n\n this.downloadEmitter.emit(this.mo().id);\n\n if (this.downloadFn()) {\n await this.fetchWithDownloadFn();\n return;\n }\n\n const subscription = this.filesService\n .fetchFileWithProgress$(this.mo())\n .pipe(takeUntil(this.destroy$))\n .subscribe(\n progress => {\n this.progress = progress;\n if (this.progress.blob) {\n this.dataUrl = URL.createObjectURL(progress.blob);\n this.safeDataUrl = this.sanitizer.bypassSecurityTrustUrl(this.dataUrl);\n }\n },\n e => {\n this.modalRef.hide();\n this.alertService.addServerFailure(e);\n }\n );\n\n this.modalRef.onHide.pipe(first(), takeUntil(this.destroy$)).subscribe(() => {\n subscription.unsubscribe();\n });\n }\n\n private async fetchWithDownloadFn(): Promise<void> {\n try {\n const fn = this.downloadFn();\n if (!fn) {\n return;\n }\n const response = await fn();\n const blob = await response.blob();\n this.dataUrl = URL.createObjectURL(blob);\n this.safeDataUrl = this.sanitizer.bypassSecurityTrustUrl(this.dataUrl);\n } catch (e) {\n this.modalRef.hide();\n this.alertService.addServerFailure(e);\n }\n }\n\n private removeDownloadedFile(): void {\n URL.revokeObjectURL(this.dataUrl);\n delete this.dataUrl;\n delete this.safeDataUrl;\n }\n\n private resolveContentType(\n mo: IManagedObjectBinary\n ): 'image' | 'video' | 'text' | 'json' | 'unsupported' {\n if (!mo || !mo.hasOwnProperty('c8y_IsBinary')) {\n // eslint-disable-next-line no-console\n console.warn('Provided Managed object is not binary');\n return 'unsupported';\n }\n\n if (mo.contentType.startsWith('image/')) {\n return 'image';\n } else if (mo.contentType.startsWith('video/')) {\n return 'video';\n } else if (mo.contentType.startsWith('text/')) {\n // @TODO: Implement in future\n return 'unsupported';\n } else if (mo.contentType.startsWith('application/json')) {\n // @TODO: Implement in future\n return 'unsupported';\n }\n return 'unsupported';\n }\n}\n","@if (contentType() !== 'unsupported') {\n <div\n class=\"d-inline-block\"\n (click)=\"openModal(modalTemplate)\"\n >\n <div #customButtonRef>\n <ng-content select=\"[customButton]\"></ng-content>\n </div>\n\n @if (!customButtonRef.children.length) {\n <button\n class=\"btn btn-default btn-icon\"\n [title]=\"'Preview file' | translate\"\n type=\"button\"\n >\n <i c8yIcon=\"search\"></i>\n </button>\n }\n </div>\n}\n\n<ng-template #modalTemplate>\n <c8y-modal\n class=\"text-break-word\"\n [title]=\"mo().name\"\n [customFooter]=\"false\"\n [flushBody]=\"true\"\n (onClose)=\"modalRef.hide()\"\n (onDismiss)=\"modalRef.hide()\"\n [labels]=\"{ cancel: '', ok: 'Close' | translate }\"\n >\n @if (!dataUrl) {\n <div class=\"text-center d-block\">\n <c8y-loading\n layout=\"application\"\n [progress]=\"progress.percentage\"\n ></c8y-loading>\n {{\n BUFFERING_STATUS_TEXT\n | translate\n : {\n totalBytes: progress.totalBytes | bytes,\n bufferedBytes: progress.bufferedBytes | bytes,\n percentage: progress.percentage,\n speed: progress.bytesPerSecond | bytes\n }\n }}\n </div>\n }\n\n @if (dataUrl) {\n <div id=\"modal-body\">\n @switch (contentType()) {\n @case ('image') {\n <img\n class=\"fit-w\"\n alt=\"safeDataUrl\"\n [src]=\"safeDataUrl\"\n />\n }\n @case ('video') {\n <video\n class=\"fit-w\"\n controls\n autoplay\n >\n <source [src]=\"safeDataUrl\" />\n {{ 'Your browser does not support the video tag.' | translate }}\n </video>\n }\n @case ('text') {\n <!-- @TODO: Implement text viewer-->\n text\n }\n @case ('json') {\n <!-- @TODO: Implement json viewer-->\n json\n }\n }\n </div>\n }\n </c8y-modal>\n</ng-template>\n","import { EventEmitter, NgModule } from '@angular/core';\nimport { CoreModule, CommonModule } from '@c8y/ngx-components';\nimport { FilePreviewComponent } from './file-preview.component';\nimport { DOWNLOAD_EMITTER } from './download-emitter.token';\n\nconst downloadEmitter = new EventEmitter<string>();\n\n@NgModule({\n imports: [CoreModule, CommonModule, FilePreviewComponent],\n exports: [FilePreviewComponent],\n providers: [\n {\n provide: DOWNLOAD_EMITTER,\n useValue: downloadEmitter\n }\n ]\n})\nexport class FilePreviewModule {}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -930,7 +930,7 @@ class AddCustomIconModalComponent {
930
930
  };
931
931
  }
932
932
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: AddCustomIconModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
933
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.19", type: AddCustomIconModalComponent, isStandalone: true, selector: "c8y-add-custom-icon-modal", viewQueries: [{ propertyName: "categoryTypeahead", first: true, predicate: ["categoryTypeahead"], descendants: true, isSignal: true }, { propertyName: "modalBody", first: true, predicate: ["modalBody"], descendants: true, isSignal: true }], ngImport: i0, template: "<c8y-modal\n [title]=\"modalTitle() | translate\"\n [headerClasses]=\"'dialog-header'\"\n (onDismiss)=\"cancel()\"\n (onClose)=\"save()\"\n [close]=\"keepOpenOnOk\"\n [labels]=\"{ cancel: 'Cancel', ok: okLabel() | translate }\"\n>\n <ng-container c8y-modal-title>\n <span\n [c8yIcon]=\"isOverrideMode() ? 'replace' : isEditMode() ? 'pencil' : 'plus-circle-o'\"\n aria-hidden=\"true\"\n ></span>\n </ng-container>\n\n @if (!isReady()) {\n <!-- min-height matches the approximate form height so Bootstrap animates\n the modal at its full size before the form is rendered. -->\n <div\n class=\"p-32 d-flex a-i-center j-c-center\"\n style=\"min-height: 380px\"\n [attr.aria-label]=\"'Loading\u2026' | translate\"\n role=\"status\"\n >\n <c8y-loading></c8y-loading>\n </div>\n }\n\n @if (isReady()) {\n <div\n class=\"p-t-16 p-b-24 p-l-32 p-r-32\"\n #modalBody\n >\n @if (isOverrideMode()) {\n <p class=\"text-center text-16 text-bold text-balance m-b-16\">\n {{ 'Upload a replacement SVG for {{iconName}}' | translate: { iconName:\n editingIcon?.overriddenIconName } }}\n </p>\n }\n\n <!-- Name, collection and icon type (hidden in override mode) -->\n @if (!isOverrideMode()) {\n @if (!isEditMode()) {\n <p\n class=\"text-muted m-b-16\"\n translate\n >\n Upload a custom SVG icon and assign it to a collection to keep your icons organized.\n </p>\n }\n <div\n class=\"m-b-16\"\n [formGroup]=\"form\"\n >\n <c8y-form-group class=\"p-b-16\">\n <label\n for=\"iconName\"\n translate\n >Icon name</label\n >\n <input\n class=\"form-control\"\n id=\"iconName\"\n aria-required=\"true\"\n aria-describedby=\"iconName-messages\"\n type=\"text\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 'acme-logo' }\"\n formControlName=\"name\"\n />\n <c8y-messages\n id=\"iconName-messages\"\n role=\"status\"\n [helpMessage]=\"'Lowercase letters, numbers and hyphens only' | translate\"\n >\n @for (message of nameMessages; track message.name) {\n <c8y-message\n [name]=\"message.name\"\n [text]=\"message.text | translate\"\n ></c8y-message>\n }\n </c8y-messages>\n </c8y-form-group>\n\n <c8y-form-group [hasError]=\"hasCategoryError()\">\n <label\n for=\"iconCategory\"\n translate\n >Collection</label\n >\n <div class=\"p-relative\">\n <c8y-typeahead\n [attr.aria-label]=\"'Icon collection' | translate\"\n id=\"iconCategory\"\n #categoryTypeahead\n [allowFreeEntries]=\"true\"\n [hideNew]=\"true\"\n [placeholder]=\"'Select or enter a collection name' | translate\"\n (onSearch)=\"onCategorySearch($event)\"\n >\n @for (cat of filteredCategories(); track cat) {\n <c8y-li\n class=\"p-l-8 p-r-8 c8y-list__item--link\"\n (click)=\"selectCategory(cat)\"\n [active]=\"form.value.category === cat\"\n >{{ cat | translate }}</c8y-li\n >\n }\n @if (showAddNew()) {\n <c8y-li class=\"bg-level-2 p-8\">\n @if (hasNoMatch()) {\n <span\n class=\"m-r-8\"\n translate\n >No existing collection found.</span\n >\n }\n <button\n class=\"btn btn-default btn-xs\"\n type=\"button\"\n (click)=\"selectCategory(categorySearch())\"\n >\n {{ 'Add new collection`collection`' | translate }}\n </button>\n </c8y-li>\n }\n </c8y-typeahead>\n @if (categorySearch()) {\n <button\n class=\"btn btn-dot p-absolute\"\n style=\"right: 36px; z-index: 10\"\n [attr.aria-label]=\"'Clear collection' | translate\"\n type=\"button\"\n (click)=\"clearCategory()\"\n >\n <i\n [c8yIcon]=\"'times'\"\n aria-hidden=\"true\"\n ></i>\n </button>\n }\n </div>\n <c8y-messages\n role=\"alert\"\n [show]=\"categoryErrors()\"\n >\n <c8y-message\n [name]=\"'required'\"\n [text]=\"'Collection is required.' | translate\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n\n @if (!isEditMode()) {\n <fieldset class=\"c8y-fieldset m-b-24\">\n <legend translate>Icon type</legend>\n <div class=\"d-flex gap-16 p-b-16\">\n <label class=\"c8y-radio a-i-start\">\n <input\n name=\"iconFamily\"\n type=\"radio\"\n [checked]=\"selectedFamily() === 'dlt'\"\n (change)=\"onFamilyChange('dlt')\"\n />\n <span></span>\n <span\n >{{ 'Single color' | translate }}<br />\n <small\n class=\"text-muted d-block\"\n translate\n >\n Single SVG, inherits text color via currentColor.\n </small>\n </span>\n </label>\n\n <label class=\"c8y-radio a-i-start\">\n <input\n name=\"iconFamily\"\n type=\"radio\"\n [checked]=\"selectedFamily() === 'c8y'\"\n (change)=\"onFamilyChange('c8y')\"\n />\n <span></span>\n <span>\n {{ 'Duocolor' | translate }}<br />\n <small\n class=\"text-muted d-block\"\n translate\n >\n Two SVGs required: a dark layer and a light accent layer.\n </small>\n </span>\n </label>\n </div>\n </fieldset>\n }\n </div>\n }\n\n <!-- SVG uploads -->\n <div\n class=\"m-b-16\"\n [class.row]=\"selectedFamily() === 'c8y'\"\n >\n @if (selectedFamily() === 'dlt') {\n <c8y-form-group [hasError]=\"hasPrimarySvgError()\">\n <label translate>Icon SVG</label>\n @if (!primarySvg()) {\n <c8y-drop-area\n class=\"drop-area-sm\"\n [icon]=\"'upload'\"\n [title]=\"'Upload SVG icon' | translate\"\n [attr.aria-label]=\"'Upload SVG icon' | translate\"\n [accept]=\"'.svg'\"\n [maxAllowedFiles]=\"1\"\n [maxFileSizeInMegaBytes]=\"5\"\n [message]=\"'Drop SVG file here or click to browse' | translate\"\n (dropped)=\"onPrimaryDropped($event)\"\n ></c8y-drop-area>\n <c8y-messages\n class=\"m-t-8\"\n role=\"status\"\n [show]=\"primarySvgErrors()\"\n [helpMessage]=\"\n hasPrimarySvgError()\n ? ''\n : ('Square viewBox (e.g. 24\u00D724) \u00B7 currentColor fills \u00B7 no external refs or scripts'\n | translate)\n \"\n >\n <c8y-message\n [name]=\"'required'\"\n [text]=\"'Upload an SVG file.' | translate\"\n ></c8y-message>\n @if (primaryValidation(); as pv) {\n @for (error of pv.errors; track error) {\n <div class=\"d-block text-danger\">{{ error }}</div>\n }\n @for (warning of pv.warnings; track warning) {\n <div class=\"d-block text-warning\">{{ warning }}</div>\n }\n }\n </c8y-messages>\n } @else {\n <fieldset class=\"c8y-fieldset m-b-8\">\n <legend class=\"d-flex a-i-center\">\n <span translate>Preview</span>\n <button\n class=\"btn btn-dot btn-dot--danger m-l-8\"\n [attr.aria-label]=\"'Remove SVG preview' | translate\"\n [tooltip]=\"'Remove SVG preview' | translate\"\n placement=\"top\"\n container=\"body\"\n [delay]=\"500\"\n (click)=\"clearPrimary()\"\n >\n <i\n [c8yIcon]=\"'minus-circle'\"\n aria-hidden=\"true\"\n ></i>\n </button>\n </legend>\n <div class=\"row d-flex a-i-center gap-16 p-b-16\">\n <div\n class=\"col-sm-6 d-flex a-i-end gap-8 j-c-around p-16 c8y-light-theme\"\n style=\"\n background: var(--c8y-root-component-background-default);\n border: 1px solid var(--c8y-root-component-border-color);\n border-radius: var(--c8y-component-border-radius-base, 4px);\n \"\n >\n @for (size of previewSizes; track size) {\n <div class=\"d-flex d-col a-i-center\">\n <div\n class=\"d-flex a-i-center j-c-center text-default\"\n [style.width.px]=\"size\"\n [style.height.px]=\"size\"\n aria-hidden=\"true\"\n [innerHTML]=\"primaryPreviewHtml()\"\n ></div>\n <small class=\"text-muted\">{{ size }}</small>\n </div>\n }\n </div>\n <div\n class=\"col-sm-6 d-flex a-i-end gap-16 j-c-around p-16 c8y-dark-theme\"\n style=\"\n background: var(--c8y-root-component-background-default);\n border: 1px solid var(--c8y-root-component-border-color);\n border-radius: var(--c8y-component-border-radius-base, 4px);\n \"\n >\n @for (size of previewSizes; track size) {\n <div class=\"d-flex d-col a-i-center\">\n <div\n class=\"d-flex a-i-center j-c-center text-default\"\n [style.width.px]=\"size\"\n [style.height.px]=\"size\"\n aria-hidden=\"true\"\n [innerHTML]=\"primaryPreviewHtml()\"\n ></div>\n <small class=\"text-muted\">{{ size }}</small>\n </div>\n }\n </div>\n </div>\n @if (primaryValidation(); as pv) {\n @if (pv.errors.length || pv.warnings.length) {\n <small\n class=\"form-control-feedback-message\"\n role=\"status\"\n >\n @for (error of pv.errors; track error) {\n <div class=\"d-block text-danger\">{{ error }}</div>\n }\n @for (warning of pv.warnings; track warning) {\n <div class=\"d-block text-warning\">{{ warning }}</div>\n }\n </small>\n }\n }\n </fieldset>\n }\n </c8y-form-group>\n } @else {\n <!-- C8Y duocolor: dark + light uploads side by side -->\n <div class=\"row d-flex-md gap-16\">\n <div class=\"col-sm-6\">\n <c8y-form-group [hasError]=\"hasPrimarySvgError()\">\n <label translate>Dark layer SVG</label>\n @if (!primarySvg()) {\n <c8y-drop-area\n class=\"drop-area-sm\"\n [icon]=\"'upload'\"\n [attr.aria-label]=\"'Upload dark layer SVG' | translate\"\n [accept]=\"'.svg'\"\n [maxAllowedFiles]=\"1\"\n [maxFileSizeInMegaBytes]=\"5\"\n [message]=\"'Drop dark layer SVG' | translate\"\n (dropped)=\"onPrimaryDropped($event)\"\n ></c8y-drop-area>\n <c8y-messages\n class=\"m-t-8\"\n role=\"status\"\n [show]=\"primarySvgErrors()\"\n [helpMessage]=\"\n hasPrimarySvgError()\n ? ''\n : ('Square viewBox \u00B7 currentColor fills \u00B7 main icon shape' | translate)\n \"\n >\n <c8y-message\n [name]=\"'required'\"\n [text]=\"'Upload an SVG file.' | translate\"\n ></c8y-message>\n @if (primaryValidation(); as pv) {\n @for (error of pv.errors; track error) {\n <div class=\"d-block text-danger\">{{ error }}</div>\n }\n @for (warning of pv.warnings; track warning) {\n <div class=\"d-block text-warning\">{{ warning }}</div>\n }\n }\n </c8y-messages>\n } @else {\n <div class=\"d-flex a-i-center gap-8 p-8 separator m-b-4\">\n <div\n class=\"d-flex a-i-center j-c-center\"\n [style.width.px]=\"32\"\n [style.height.px]=\"32\"\n aria-hidden=\"true\"\n [innerHTML]=\"primaryPreviewHtml()\"\n ></div>\n <span\n class=\"text-muted text-12 flex-grow\"\n translate\n >Dark layer</span\n >\n <button\n class=\"btn btn-dot btn-dot--danger\"\n [attr.aria-label]=\"'Remove dark layer SVG' | translate\"\n [tooltip]=\"'Remove dark layer SVG' | translate\"\n placement=\"top\"\n container=\"body\"\n [delay]=\"500\"\n (click)=\"clearPrimary()\"\n >\n <i\n [c8yIcon]=\"'minus-circle'\"\n aria-hidden=\"true\"\n ></i>\n </button>\n </div>\n @if (primaryValidation(); as pv) {\n @if (pv.errors.length || pv.warnings.length) {\n <small\n class=\"form-control-feedback-message\"\n role=\"status\"\n >\n @for (error of pv.errors; track error) {\n <div class=\"d-block text-danger\">{{ error }}</div>\n }\n @for (warning of pv.warnings; track warning) {\n <div class=\"d-block text-warning\">{{ warning }}</div>\n }\n </small>\n }\n }\n }\n </c8y-form-group>\n </div>\n <div class=\"col-sm-6\">\n <c8y-form-group [hasError]=\"hasLightSvgError()\">\n <label translate>Light layer SVG</label>\n @if (!lightSvg()) {\n <c8y-drop-area\n class=\"drop-area-sm\"\n [icon]=\"'upload'\"\n [attr.aria-label]=\"'Upload light layer SVG' | translate\"\n [accept]=\"'.svg'\"\n [maxAllowedFiles]=\"1\"\n [maxFileSizeInMegaBytes]=\"5\"\n [message]=\"'Drop light layer SVG' | translate\"\n (dropped)=\"onLightDropped($event)\"\n ></c8y-drop-area>\n <c8y-messages\n class=\"m-t-8\"\n role=\"status\"\n [show]=\"lightSvgErrors()\"\n [helpMessage]=\"\n hasLightSvgError()\n ? ''\n : ('Same viewBox as dark layer \u00B7 accent shape for the light tint'\n | translate)\n \"\n >\n <c8y-message\n [name]=\"'required'\"\n [text]=\"'Upload an SVG file.' | translate\"\n ></c8y-message>\n @if (lightValidation(); as lv) {\n @for (error of lv.errors; track error) {\n <div class=\"d-block text-danger\">{{ error }}</div>\n }\n @for (warning of lv.warnings; track warning) {\n <div class=\"d-block text-warning\">{{ warning }}</div>\n }\n }\n </c8y-messages>\n } @else {\n <div class=\"d-flex a-i-center gap-8 p-8 separator m-b-4\">\n <div\n class=\"d-flex a-i-center j-c-center\"\n [style.width.px]=\"32\"\n [style.height.px]=\"32\"\n aria-hidden=\"true\"\n [innerHTML]=\"lightPreviewHtml()\"\n ></div>\n <span\n class=\"text-muted text-12 flex-grow\"\n translate\n >Light layer</span\n >\n <button\n class=\"btn btn-dot btn-dot--danger\"\n [attr.aria-label]=\"'Remove light layer SVG' | translate\"\n [tooltip]=\"'Remove light layer SVG' | translate\"\n placement=\"top\"\n container=\"body\"\n [delay]=\"500\"\n (click)=\"clearLight()\"\n >\n <i\n [c8yIcon]=\"'minus-circle'\"\n aria-hidden=\"true\"\n ></i>\n </button>\n </div>\n @if (lightValidation(); as lv) {\n @if (lv.errors.length || lv.warnings.length) {\n <small\n class=\"form-control-feedback-message\"\n role=\"status\"\n >\n @for (error of lv.errors; track error) {\n <div class=\"d-block text-danger\">{{ error }}</div>\n }\n @for (warning of lv.warnings; track warning) {\n <div class=\"d-block text-warning\">{{ warning }}</div>\n }\n </small>\n }\n }\n }\n </c8y-form-group>\n </div>\n </div>\n\n @if (showCompositePreview()) {\n <fieldset\n class=\"c8y-fieldset m-t-16\"\n aria-hidden=\"true\"\n >\n <legend translate>Composited preview</legend>\n <div class=\"row d-flex-md gap-16 p-b-16\">\n <!-- Light background -->\n <div\n class=\"col-sm-6 d-flex a-i-end gap-8 j-c-around p-16 c8y-light-theme\"\n style=\"\n background: var(--c8y-root-component-background-default);\n border: 1px solid var(--c8y-root-component-border-color);\n border-radius: var(--c8y-component-border-radius-base, 4px);\n \"\n >\n @for (size of previewSizes; track size) {\n <div class=\"d-flex d-col a-i-center\">\n <div\n class=\"p-relative\"\n [style.width.px]=\"size\"\n [style.height.px]=\"size\"\n >\n <div\n class=\"d-flex a-i-center j-c-center fit-w fit-h\"\n style=\"color: var(--c8y-root-component-icon-dark-color-dark)\"\n [innerHTML]=\"primaryPreviewHtml()\"\n ></div>\n <div\n class=\"d-flex a-i-center j-c-center fit-w fit-h p-absolute\"\n style=\"\n top: 0;\n left: 0;\n color: var(--c8y-root-component-icon-dark-color-light);\n \"\n [innerHTML]=\"lightPreviewHtml()\"\n ></div>\n </div>\n <small class=\"text-muted\">{{ size }}</small>\n </div>\n }\n </div>\n <!-- Dark background -->\n <div\n class=\"col-sm-6 d-flex a-i-end gap-8 j-c-around p-16 c8y-dark-theme\"\n style=\"\n background: var(--c8y-root-component-background-default);\n border-radius: var(--c8y-component-border-radius-base, 4px);\n \"\n >\n @for (size of previewSizes; track size) {\n <div class=\"d-flex d-col a-i-center\">\n <div\n class=\"p-relative\"\n [style.width.px]=\"size\"\n [style.height.px]=\"size\"\n >\n <div\n class=\"d-flex a-i-center j-c-center fit-w fit-h\"\n style=\"color: var(--c8y-root-component-icon-dark-color-dark)\"\n [innerHTML]=\"primaryPreviewHtml()\"\n ></div>\n <div\n class=\"d-flex a-i-center j-c-center fit-w fit-h p-absolute\"\n style=\"\n top: 0;\n left: 0;\n color: var(\n --c8y-component-icon-dark-color-light,\n var(--c8y-root-component-icon-dark-color-light)\n );\n \"\n [innerHTML]=\"lightPreviewHtml()\"\n ></div>\n </div>\n <small class=\"text-muted\">{{ size }}</small>\n </div>\n }\n </div>\n </div>\n </fieldset>\n }\n }\n </div>\n </div>\n }\n</c8y-modal>\n", dependencies: [{ kind: "component", type: ModalComponent, selector: "c8y-modal", inputs: ["disabled", "close", "dismiss", "title", "body", "customFooter", "headerClasses", "labels"], outputs: ["onDismiss", "onClose"] }, { kind: "component", type: DropAreaComponent, selector: "c8y-drop-area", inputs: ["formControl", "title", "message", "icon", "loadingMessage", "forceHideList", "alwaysShow", "clickToOpen", "loading", "progress", "maxAllowedFiles", "files", "maxFileSizeInMegaBytes", "accept"], outputs: ["dropped"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "component", type: MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage", "additionalMessages"] }, { kind: "directive", type: MessageDirective, selector: "c8y-message", inputs: ["name", "text"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.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: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: TypeaheadComponent, selector: "c8y-typeahead", inputs: ["required", "maxlength", "disabled", "allowFreeEntries", "placeholder", "displayProperty", "icon", "name", "autoClose", "hideNew", "container", "selected", "title", "highlightFirstItem"], outputs: ["onSearch", "onIconClick"] }, { kind: "component", type: ListItemComponent, selector: "c8y-list-item, c8y-li", inputs: ["active", "highlighted", "emptyActions", "dense", "collapsed", "selectable"], outputs: ["collapsedChange"] }, { kind: "component", type: LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "progressLabel", "message"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i2.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: "pipe", type: C8yTranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
933
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.19", type: AddCustomIconModalComponent, isStandalone: true, selector: "c8y-add-custom-icon-modal", viewQueries: [{ propertyName: "categoryTypeahead", first: true, predicate: ["categoryTypeahead"], descendants: true, isSignal: true }, { propertyName: "modalBody", first: true, predicate: ["modalBody"], descendants: true, isSignal: true }], ngImport: i0, template: "<c8y-modal\n [title]=\"modalTitle() | translate\"\n [headerClasses]=\"'dialog-header'\"\n (onDismiss)=\"cancel()\"\n (onClose)=\"save()\"\n [close]=\"keepOpenOnOk\"\n [labels]=\"{ cancel: 'Cancel', ok: okLabel() | translate }\"\n [flushBody]=\"true\"\n>\n <ng-container c8y-modal-title>\n <span\n [c8yIcon]=\"isOverrideMode() ? 'replace' : isEditMode() ? 'pencil' : 'plus-circle-o'\"\n aria-hidden=\"true\"\n ></span>\n </ng-container>\n\n @if (!isReady()) {\n <!-- min-height matches the approximate form height so Bootstrap animates\n the modal at its full size before the form is rendered. -->\n <div\n class=\"p-32 d-flex a-i-center j-c-center\"\n style=\"min-height: 380px\"\n [attr.aria-label]=\"'Loading\u2026' | translate\"\n role=\"status\"\n >\n <c8y-loading></c8y-loading>\n </div>\n }\n\n @if (isReady()) {\n <div\n class=\"p-t-16 p-b-24 p-l-32 p-r-32\"\n #modalBody\n >\n @if (isOverrideMode()) {\n <p class=\"text-center text-16 text-bold text-balance m-b-16\">\n {{ 'Upload a replacement SVG for {{iconName}}' | translate: { iconName:\n editingIcon?.overriddenIconName } }}\n </p>\n }\n\n <!-- Name, collection and icon type (hidden in override mode) -->\n @if (!isOverrideMode()) {\n @if (!isEditMode()) {\n <p\n class=\"text-muted m-b-16\"\n translate\n >\n Upload a custom SVG icon and assign it to a collection to keep your icons organized.\n </p>\n }\n <div\n class=\"m-b-16\"\n [formGroup]=\"form\"\n >\n <c8y-form-group class=\"p-b-16\">\n <label\n for=\"iconName\"\n translate\n >Icon name</label\n >\n <input\n class=\"form-control\"\n id=\"iconName\"\n aria-required=\"true\"\n aria-describedby=\"iconName-messages\"\n type=\"text\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 'acme-logo' }\"\n formControlName=\"name\"\n />\n <c8y-messages\n id=\"iconName-messages\"\n role=\"status\"\n [helpMessage]=\"'Lowercase letters, numbers and hyphens only' | translate\"\n >\n @for (message of nameMessages; track message.name) {\n <c8y-message\n [name]=\"message.name\"\n [text]=\"message.text | translate\"\n ></c8y-message>\n }\n </c8y-messages>\n </c8y-form-group>\n\n <c8y-form-group [hasError]=\"hasCategoryError()\">\n <label\n for=\"iconCategory\"\n translate\n >Collection</label\n >\n <div class=\"p-relative\">\n <c8y-typeahead\n [attr.aria-label]=\"'Icon collection' | translate\"\n id=\"iconCategory\"\n #categoryTypeahead\n [allowFreeEntries]=\"true\"\n [hideNew]=\"true\"\n [placeholder]=\"'Select or enter a collection name' | translate\"\n (onSearch)=\"onCategorySearch($event)\"\n >\n @for (cat of filteredCategories(); track cat) {\n <c8y-li\n class=\"p-l-8 p-r-8 c8y-list__item--link\"\n (click)=\"selectCategory(cat)\"\n [active]=\"form.value.category === cat\"\n >{{ cat | translate }}</c8y-li\n >\n }\n @if (showAddNew()) {\n <c8y-li class=\"bg-level-2 p-8\">\n @if (hasNoMatch()) {\n <span\n class=\"m-r-8\"\n translate\n >No existing collection found.</span\n >\n }\n <button\n class=\"btn btn-default btn-xs\"\n type=\"button\"\n (click)=\"selectCategory(categorySearch())\"\n >\n {{ 'Add new collection`collection`' | translate }}\n </button>\n </c8y-li>\n }\n </c8y-typeahead>\n @if (categorySearch()) {\n <button\n class=\"btn btn-dot p-absolute\"\n style=\"right: 36px; z-index: 10\"\n [attr.aria-label]=\"'Clear collection' | translate\"\n type=\"button\"\n (click)=\"clearCategory()\"\n >\n <i\n [c8yIcon]=\"'times'\"\n aria-hidden=\"true\"\n ></i>\n </button>\n }\n </div>\n <c8y-messages\n role=\"alert\"\n [show]=\"categoryErrors()\"\n >\n <c8y-message\n [name]=\"'required'\"\n [text]=\"'Collection is required.' | translate\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n\n @if (!isEditMode()) {\n <fieldset class=\"c8y-fieldset m-b-24\">\n <legend translate>Icon type</legend>\n <div class=\"d-flex gap-16 p-b-16\">\n <label class=\"c8y-radio a-i-start\">\n <input\n name=\"iconFamily\"\n type=\"radio\"\n [checked]=\"selectedFamily() === 'dlt'\"\n (change)=\"onFamilyChange('dlt')\"\n />\n <span></span>\n <span\n >{{ 'Single color' | translate }}<br />\n <small\n class=\"text-muted d-block\"\n translate\n >\n Single SVG, inherits text color via currentColor.\n </small>\n </span>\n </label>\n\n <label class=\"c8y-radio a-i-start\">\n <input\n name=\"iconFamily\"\n type=\"radio\"\n [checked]=\"selectedFamily() === 'c8y'\"\n (change)=\"onFamilyChange('c8y')\"\n />\n <span></span>\n <span>\n {{ 'Duocolor' | translate }}<br />\n <small\n class=\"text-muted d-block\"\n translate\n >\n Two SVGs required: a dark layer and a light accent layer.\n </small>\n </span>\n </label>\n </div>\n </fieldset>\n }\n </div>\n }\n\n <!-- SVG uploads -->\n <div\n class=\"m-b-16\"\n [class.row]=\"selectedFamily() === 'c8y'\"\n >\n @if (selectedFamily() === 'dlt') {\n <c8y-form-group [hasError]=\"hasPrimarySvgError()\">\n <label translate>Icon SVG</label>\n @if (!primarySvg()) {\n <c8y-drop-area\n class=\"drop-area-sm\"\n [icon]=\"'upload'\"\n [title]=\"'Upload SVG icon' | translate\"\n [attr.aria-label]=\"'Upload SVG icon' | translate\"\n [accept]=\"'.svg'\"\n [maxAllowedFiles]=\"1\"\n [maxFileSizeInMegaBytes]=\"5\"\n [message]=\"'Drop SVG file here or click to browse' | translate\"\n (dropped)=\"onPrimaryDropped($event)\"\n ></c8y-drop-area>\n <c8y-messages\n class=\"m-t-8\"\n role=\"status\"\n [show]=\"primarySvgErrors()\"\n [helpMessage]=\"\n hasPrimarySvgError()\n ? ''\n : ('Square viewBox (e.g. 24\u00D724) \u00B7 currentColor fills \u00B7 no external refs or scripts'\n | translate)\n \"\n >\n <c8y-message\n [name]=\"'required'\"\n [text]=\"'Upload an SVG file.' | translate\"\n ></c8y-message>\n @if (primaryValidation(); as pv) {\n @for (error of pv.errors; track error) {\n <div class=\"d-block text-danger\">{{ error }}</div>\n }\n @for (warning of pv.warnings; track warning) {\n <div class=\"d-block text-warning\">{{ warning }}</div>\n }\n }\n </c8y-messages>\n } @else {\n <fieldset class=\"c8y-fieldset m-b-8\">\n <legend class=\"d-flex a-i-center\">\n <span translate>Preview</span>\n <button\n class=\"btn btn-dot btn-dot--danger m-l-8\"\n [attr.aria-label]=\"'Remove SVG preview' | translate\"\n [tooltip]=\"'Remove SVG preview' | translate\"\n placement=\"top\"\n container=\"body\"\n [delay]=\"500\"\n (click)=\"clearPrimary()\"\n >\n <i\n [c8yIcon]=\"'minus-circle'\"\n aria-hidden=\"true\"\n ></i>\n </button>\n </legend>\n <div class=\"row d-flex a-i-center gap-16 p-b-16\">\n <div\n class=\"col-sm-6 d-flex a-i-end gap-8 j-c-around p-16 c8y-light-theme\"\n style=\"\n background: var(--c8y-root-component-background-default);\n border: 1px solid var(--c8y-root-component-border-color);\n border-radius: var(--c8y-component-border-radius-base, 4px);\n \"\n >\n @for (size of previewSizes; track size) {\n <div class=\"d-flex d-col a-i-center\">\n <div\n class=\"d-flex a-i-center j-c-center text-default\"\n [style.width.px]=\"size\"\n [style.height.px]=\"size\"\n aria-hidden=\"true\"\n [innerHTML]=\"primaryPreviewHtml()\"\n ></div>\n <small class=\"text-muted\">{{ size }}</small>\n </div>\n }\n </div>\n <div\n class=\"col-sm-6 d-flex a-i-end gap-16 j-c-around p-16 c8y-dark-theme\"\n style=\"\n background: var(--c8y-root-component-background-default);\n border: 1px solid var(--c8y-root-component-border-color);\n border-radius: var(--c8y-component-border-radius-base, 4px);\n \"\n >\n @for (size of previewSizes; track size) {\n <div class=\"d-flex d-col a-i-center\">\n <div\n class=\"d-flex a-i-center j-c-center text-default\"\n [style.width.px]=\"size\"\n [style.height.px]=\"size\"\n aria-hidden=\"true\"\n [innerHTML]=\"primaryPreviewHtml()\"\n ></div>\n <small class=\"text-muted\">{{ size }}</small>\n </div>\n }\n </div>\n </div>\n @if (primaryValidation(); as pv) {\n @if (pv.errors.length || pv.warnings.length) {\n <small\n class=\"form-control-feedback-message\"\n role=\"status\"\n >\n @for (error of pv.errors; track error) {\n <div class=\"d-block text-danger\">{{ error }}</div>\n }\n @for (warning of pv.warnings; track warning) {\n <div class=\"d-block text-warning\">{{ warning }}</div>\n }\n </small>\n }\n }\n </fieldset>\n }\n </c8y-form-group>\n } @else {\n <!-- C8Y duocolor: dark + light uploads side by side -->\n <div class=\"row d-flex-md gap-16\">\n <div class=\"col-sm-6\">\n <c8y-form-group [hasError]=\"hasPrimarySvgError()\">\n <label translate>Dark layer SVG</label>\n @if (!primarySvg()) {\n <c8y-drop-area\n class=\"drop-area-sm\"\n [icon]=\"'upload'\"\n [attr.aria-label]=\"'Upload dark layer SVG' | translate\"\n [accept]=\"'.svg'\"\n [maxAllowedFiles]=\"1\"\n [maxFileSizeInMegaBytes]=\"5\"\n [message]=\"'Drop dark layer SVG' | translate\"\n (dropped)=\"onPrimaryDropped($event)\"\n ></c8y-drop-area>\n <c8y-messages\n class=\"m-t-8\"\n role=\"status\"\n [show]=\"primarySvgErrors()\"\n [helpMessage]=\"\n hasPrimarySvgError()\n ? ''\n : ('Square viewBox \u00B7 currentColor fills \u00B7 main icon shape' | translate)\n \"\n >\n <c8y-message\n [name]=\"'required'\"\n [text]=\"'Upload an SVG file.' | translate\"\n ></c8y-message>\n @if (primaryValidation(); as pv) {\n @for (error of pv.errors; track error) {\n <div class=\"d-block text-danger\">{{ error }}</div>\n }\n @for (warning of pv.warnings; track warning) {\n <div class=\"d-block text-warning\">{{ warning }}</div>\n }\n }\n </c8y-messages>\n } @else {\n <div class=\"d-flex a-i-center gap-8 p-8 separator m-b-4\">\n <div\n class=\"d-flex a-i-center j-c-center\"\n [style.width.px]=\"32\"\n [style.height.px]=\"32\"\n aria-hidden=\"true\"\n [innerHTML]=\"primaryPreviewHtml()\"\n ></div>\n <span\n class=\"text-muted text-12 flex-grow\"\n translate\n >Dark layer</span\n >\n <button\n class=\"btn btn-dot btn-dot--danger\"\n [attr.aria-label]=\"'Remove dark layer SVG' | translate\"\n [tooltip]=\"'Remove dark layer SVG' | translate\"\n placement=\"top\"\n container=\"body\"\n [delay]=\"500\"\n (click)=\"clearPrimary()\"\n >\n <i\n [c8yIcon]=\"'minus-circle'\"\n aria-hidden=\"true\"\n ></i>\n </button>\n </div>\n @if (primaryValidation(); as pv) {\n @if (pv.errors.length || pv.warnings.length) {\n <small\n class=\"form-control-feedback-message\"\n role=\"status\"\n >\n @for (error of pv.errors; track error) {\n <div class=\"d-block text-danger\">{{ error }}</div>\n }\n @for (warning of pv.warnings; track warning) {\n <div class=\"d-block text-warning\">{{ warning }}</div>\n }\n </small>\n }\n }\n }\n </c8y-form-group>\n </div>\n <div class=\"col-sm-6\">\n <c8y-form-group [hasError]=\"hasLightSvgError()\">\n <label translate>Light layer SVG</label>\n @if (!lightSvg()) {\n <c8y-drop-area\n class=\"drop-area-sm\"\n [icon]=\"'upload'\"\n [attr.aria-label]=\"'Upload light layer SVG' | translate\"\n [accept]=\"'.svg'\"\n [maxAllowedFiles]=\"1\"\n [maxFileSizeInMegaBytes]=\"5\"\n [message]=\"'Drop light layer SVG' | translate\"\n (dropped)=\"onLightDropped($event)\"\n ></c8y-drop-area>\n <c8y-messages\n class=\"m-t-8\"\n role=\"status\"\n [show]=\"lightSvgErrors()\"\n [helpMessage]=\"\n hasLightSvgError()\n ? ''\n : ('Same viewBox as dark layer \u00B7 accent shape for the light tint'\n | translate)\n \"\n >\n <c8y-message\n [name]=\"'required'\"\n [text]=\"'Upload an SVG file.' | translate\"\n ></c8y-message>\n @if (lightValidation(); as lv) {\n @for (error of lv.errors; track error) {\n <div class=\"d-block text-danger\">{{ error }}</div>\n }\n @for (warning of lv.warnings; track warning) {\n <div class=\"d-block text-warning\">{{ warning }}</div>\n }\n }\n </c8y-messages>\n } @else {\n <div class=\"d-flex a-i-center gap-8 p-8 separator m-b-4\">\n <div\n class=\"d-flex a-i-center j-c-center\"\n [style.width.px]=\"32\"\n [style.height.px]=\"32\"\n aria-hidden=\"true\"\n [innerHTML]=\"lightPreviewHtml()\"\n ></div>\n <span\n class=\"text-muted text-12 flex-grow\"\n translate\n >Light layer</span\n >\n <button\n class=\"btn btn-dot btn-dot--danger\"\n [attr.aria-label]=\"'Remove light layer SVG' | translate\"\n [tooltip]=\"'Remove light layer SVG' | translate\"\n placement=\"top\"\n container=\"body\"\n [delay]=\"500\"\n (click)=\"clearLight()\"\n >\n <i\n [c8yIcon]=\"'minus-circle'\"\n aria-hidden=\"true\"\n ></i>\n </button>\n </div>\n @if (lightValidation(); as lv) {\n @if (lv.errors.length || lv.warnings.length) {\n <small\n class=\"form-control-feedback-message\"\n role=\"status\"\n >\n @for (error of lv.errors; track error) {\n <div class=\"d-block text-danger\">{{ error }}</div>\n }\n @for (warning of lv.warnings; track warning) {\n <div class=\"d-block text-warning\">{{ warning }}</div>\n }\n </small>\n }\n }\n }\n </c8y-form-group>\n </div>\n </div>\n\n @if (showCompositePreview()) {\n <fieldset\n class=\"c8y-fieldset m-t-16\"\n aria-hidden=\"true\"\n >\n <legend translate>Composited preview</legend>\n <div class=\"row d-flex-md gap-16 p-b-16\">\n <!-- Light background -->\n <div\n class=\"col-sm-6 d-flex a-i-end gap-8 j-c-around p-16 c8y-light-theme\"\n style=\"\n background: var(--c8y-root-component-background-default);\n border: 1px solid var(--c8y-root-component-border-color);\n border-radius: var(--c8y-component-border-radius-base, 4px);\n \"\n >\n @for (size of previewSizes; track size) {\n <div class=\"d-flex d-col a-i-center\">\n <div\n class=\"p-relative\"\n [style.width.px]=\"size\"\n [style.height.px]=\"size\"\n >\n <div\n class=\"d-flex a-i-center j-c-center fit-w fit-h\"\n style=\"color: var(--c8y-root-component-icon-dark-color-dark)\"\n [innerHTML]=\"primaryPreviewHtml()\"\n ></div>\n <div\n class=\"d-flex a-i-center j-c-center fit-w fit-h p-absolute\"\n style=\"\n top: 0;\n left: 0;\n color: var(--c8y-root-component-icon-dark-color-light);\n \"\n [innerHTML]=\"lightPreviewHtml()\"\n ></div>\n </div>\n <small class=\"text-muted\">{{ size }}</small>\n </div>\n }\n </div>\n <!-- Dark background -->\n <div\n class=\"col-sm-6 d-flex a-i-end gap-8 j-c-around p-16 c8y-dark-theme\"\n style=\"\n background: var(--c8y-root-component-background-default);\n border-radius: var(--c8y-component-border-radius-base, 4px);\n \"\n >\n @for (size of previewSizes; track size) {\n <div class=\"d-flex d-col a-i-center\">\n <div\n class=\"p-relative\"\n [style.width.px]=\"size\"\n [style.height.px]=\"size\"\n >\n <div\n class=\"d-flex a-i-center j-c-center fit-w fit-h\"\n style=\"color: var(--c8y-root-component-icon-dark-color-dark)\"\n [innerHTML]=\"primaryPreviewHtml()\"\n ></div>\n <div\n class=\"d-flex a-i-center j-c-center fit-w fit-h p-absolute\"\n style=\"\n top: 0;\n left: 0;\n color: var(\n --c8y-component-icon-dark-color-light,\n var(--c8y-root-component-icon-dark-color-light)\n );\n \"\n [innerHTML]=\"lightPreviewHtml()\"\n ></div>\n </div>\n <small class=\"text-muted\">{{ size }}</small>\n </div>\n }\n </div>\n </div>\n </fieldset>\n }\n }\n </div>\n </div>\n }\n</c8y-modal>\n", dependencies: [{ kind: "component", type: ModalComponent, selector: "c8y-modal", inputs: ["disabled", "close", "dismiss", "title", "body", "customFooter", "headerClasses", "flushBody", "labels"], outputs: ["onDismiss", "onClose"] }, { kind: "component", type: DropAreaComponent, selector: "c8y-drop-area", inputs: ["formControl", "title", "message", "icon", "loadingMessage", "forceHideList", "alwaysShow", "clickToOpen", "loading", "progress", "maxAllowedFiles", "files", "maxFileSizeInMegaBytes", "accept"], outputs: ["dropped"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "component", type: MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage", "additionalMessages"] }, { kind: "directive", type: MessageDirective, selector: "c8y-message", inputs: ["name", "text"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.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: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: TypeaheadComponent, selector: "c8y-typeahead", inputs: ["required", "maxlength", "disabled", "allowFreeEntries", "placeholder", "displayProperty", "icon", "name", "autoClose", "hideNew", "container", "selected", "title", "highlightFirstItem"], outputs: ["onSearch", "onIconClick"] }, { kind: "component", type: ListItemComponent, selector: "c8y-list-item, c8y-li", inputs: ["active", "highlighted", "emptyActions", "dense", "collapsed", "selectable"], outputs: ["collapsedChange"] }, { kind: "component", type: LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "progressLabel", "message"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i2.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: "pipe", type: C8yTranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
934
934
  }
935
935
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: AddCustomIconModalComponent, decorators: [{
936
936
  type: Component,
@@ -949,7 +949,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
949
949
  ListItemComponent,
950
950
  LoadingComponent,
951
951
  TooltipModule
952
- ], changeDetection: ChangeDetectionStrategy.Eager, template: "<c8y-modal\n [title]=\"modalTitle() | translate\"\n [headerClasses]=\"'dialog-header'\"\n (onDismiss)=\"cancel()\"\n (onClose)=\"save()\"\n [close]=\"keepOpenOnOk\"\n [labels]=\"{ cancel: 'Cancel', ok: okLabel() | translate }\"\n>\n <ng-container c8y-modal-title>\n <span\n [c8yIcon]=\"isOverrideMode() ? 'replace' : isEditMode() ? 'pencil' : 'plus-circle-o'\"\n aria-hidden=\"true\"\n ></span>\n </ng-container>\n\n @if (!isReady()) {\n <!-- min-height matches the approximate form height so Bootstrap animates\n the modal at its full size before the form is rendered. -->\n <div\n class=\"p-32 d-flex a-i-center j-c-center\"\n style=\"min-height: 380px\"\n [attr.aria-label]=\"'Loading\u2026' | translate\"\n role=\"status\"\n >\n <c8y-loading></c8y-loading>\n </div>\n }\n\n @if (isReady()) {\n <div\n class=\"p-t-16 p-b-24 p-l-32 p-r-32\"\n #modalBody\n >\n @if (isOverrideMode()) {\n <p class=\"text-center text-16 text-bold text-balance m-b-16\">\n {{ 'Upload a replacement SVG for {{iconName}}' | translate: { iconName:\n editingIcon?.overriddenIconName } }}\n </p>\n }\n\n <!-- Name, collection and icon type (hidden in override mode) -->\n @if (!isOverrideMode()) {\n @if (!isEditMode()) {\n <p\n class=\"text-muted m-b-16\"\n translate\n >\n Upload a custom SVG icon and assign it to a collection to keep your icons organized.\n </p>\n }\n <div\n class=\"m-b-16\"\n [formGroup]=\"form\"\n >\n <c8y-form-group class=\"p-b-16\">\n <label\n for=\"iconName\"\n translate\n >Icon name</label\n >\n <input\n class=\"form-control\"\n id=\"iconName\"\n aria-required=\"true\"\n aria-describedby=\"iconName-messages\"\n type=\"text\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 'acme-logo' }\"\n formControlName=\"name\"\n />\n <c8y-messages\n id=\"iconName-messages\"\n role=\"status\"\n [helpMessage]=\"'Lowercase letters, numbers and hyphens only' | translate\"\n >\n @for (message of nameMessages; track message.name) {\n <c8y-message\n [name]=\"message.name\"\n [text]=\"message.text | translate\"\n ></c8y-message>\n }\n </c8y-messages>\n </c8y-form-group>\n\n <c8y-form-group [hasError]=\"hasCategoryError()\">\n <label\n for=\"iconCategory\"\n translate\n >Collection</label\n >\n <div class=\"p-relative\">\n <c8y-typeahead\n [attr.aria-label]=\"'Icon collection' | translate\"\n id=\"iconCategory\"\n #categoryTypeahead\n [allowFreeEntries]=\"true\"\n [hideNew]=\"true\"\n [placeholder]=\"'Select or enter a collection name' | translate\"\n (onSearch)=\"onCategorySearch($event)\"\n >\n @for (cat of filteredCategories(); track cat) {\n <c8y-li\n class=\"p-l-8 p-r-8 c8y-list__item--link\"\n (click)=\"selectCategory(cat)\"\n [active]=\"form.value.category === cat\"\n >{{ cat | translate }}</c8y-li\n >\n }\n @if (showAddNew()) {\n <c8y-li class=\"bg-level-2 p-8\">\n @if (hasNoMatch()) {\n <span\n class=\"m-r-8\"\n translate\n >No existing collection found.</span\n >\n }\n <button\n class=\"btn btn-default btn-xs\"\n type=\"button\"\n (click)=\"selectCategory(categorySearch())\"\n >\n {{ 'Add new collection`collection`' | translate }}\n </button>\n </c8y-li>\n }\n </c8y-typeahead>\n @if (categorySearch()) {\n <button\n class=\"btn btn-dot p-absolute\"\n style=\"right: 36px; z-index: 10\"\n [attr.aria-label]=\"'Clear collection' | translate\"\n type=\"button\"\n (click)=\"clearCategory()\"\n >\n <i\n [c8yIcon]=\"'times'\"\n aria-hidden=\"true\"\n ></i>\n </button>\n }\n </div>\n <c8y-messages\n role=\"alert\"\n [show]=\"categoryErrors()\"\n >\n <c8y-message\n [name]=\"'required'\"\n [text]=\"'Collection is required.' | translate\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n\n @if (!isEditMode()) {\n <fieldset class=\"c8y-fieldset m-b-24\">\n <legend translate>Icon type</legend>\n <div class=\"d-flex gap-16 p-b-16\">\n <label class=\"c8y-radio a-i-start\">\n <input\n name=\"iconFamily\"\n type=\"radio\"\n [checked]=\"selectedFamily() === 'dlt'\"\n (change)=\"onFamilyChange('dlt')\"\n />\n <span></span>\n <span\n >{{ 'Single color' | translate }}<br />\n <small\n class=\"text-muted d-block\"\n translate\n >\n Single SVG, inherits text color via currentColor.\n </small>\n </span>\n </label>\n\n <label class=\"c8y-radio a-i-start\">\n <input\n name=\"iconFamily\"\n type=\"radio\"\n [checked]=\"selectedFamily() === 'c8y'\"\n (change)=\"onFamilyChange('c8y')\"\n />\n <span></span>\n <span>\n {{ 'Duocolor' | translate }}<br />\n <small\n class=\"text-muted d-block\"\n translate\n >\n Two SVGs required: a dark layer and a light accent layer.\n </small>\n </span>\n </label>\n </div>\n </fieldset>\n }\n </div>\n }\n\n <!-- SVG uploads -->\n <div\n class=\"m-b-16\"\n [class.row]=\"selectedFamily() === 'c8y'\"\n >\n @if (selectedFamily() === 'dlt') {\n <c8y-form-group [hasError]=\"hasPrimarySvgError()\">\n <label translate>Icon SVG</label>\n @if (!primarySvg()) {\n <c8y-drop-area\n class=\"drop-area-sm\"\n [icon]=\"'upload'\"\n [title]=\"'Upload SVG icon' | translate\"\n [attr.aria-label]=\"'Upload SVG icon' | translate\"\n [accept]=\"'.svg'\"\n [maxAllowedFiles]=\"1\"\n [maxFileSizeInMegaBytes]=\"5\"\n [message]=\"'Drop SVG file here or click to browse' | translate\"\n (dropped)=\"onPrimaryDropped($event)\"\n ></c8y-drop-area>\n <c8y-messages\n class=\"m-t-8\"\n role=\"status\"\n [show]=\"primarySvgErrors()\"\n [helpMessage]=\"\n hasPrimarySvgError()\n ? ''\n : ('Square viewBox (e.g. 24\u00D724) \u00B7 currentColor fills \u00B7 no external refs or scripts'\n | translate)\n \"\n >\n <c8y-message\n [name]=\"'required'\"\n [text]=\"'Upload an SVG file.' | translate\"\n ></c8y-message>\n @if (primaryValidation(); as pv) {\n @for (error of pv.errors; track error) {\n <div class=\"d-block text-danger\">{{ error }}</div>\n }\n @for (warning of pv.warnings; track warning) {\n <div class=\"d-block text-warning\">{{ warning }}</div>\n }\n }\n </c8y-messages>\n } @else {\n <fieldset class=\"c8y-fieldset m-b-8\">\n <legend class=\"d-flex a-i-center\">\n <span translate>Preview</span>\n <button\n class=\"btn btn-dot btn-dot--danger m-l-8\"\n [attr.aria-label]=\"'Remove SVG preview' | translate\"\n [tooltip]=\"'Remove SVG preview' | translate\"\n placement=\"top\"\n container=\"body\"\n [delay]=\"500\"\n (click)=\"clearPrimary()\"\n >\n <i\n [c8yIcon]=\"'minus-circle'\"\n aria-hidden=\"true\"\n ></i>\n </button>\n </legend>\n <div class=\"row d-flex a-i-center gap-16 p-b-16\">\n <div\n class=\"col-sm-6 d-flex a-i-end gap-8 j-c-around p-16 c8y-light-theme\"\n style=\"\n background: var(--c8y-root-component-background-default);\n border: 1px solid var(--c8y-root-component-border-color);\n border-radius: var(--c8y-component-border-radius-base, 4px);\n \"\n >\n @for (size of previewSizes; track size) {\n <div class=\"d-flex d-col a-i-center\">\n <div\n class=\"d-flex a-i-center j-c-center text-default\"\n [style.width.px]=\"size\"\n [style.height.px]=\"size\"\n aria-hidden=\"true\"\n [innerHTML]=\"primaryPreviewHtml()\"\n ></div>\n <small class=\"text-muted\">{{ size }}</small>\n </div>\n }\n </div>\n <div\n class=\"col-sm-6 d-flex a-i-end gap-16 j-c-around p-16 c8y-dark-theme\"\n style=\"\n background: var(--c8y-root-component-background-default);\n border: 1px solid var(--c8y-root-component-border-color);\n border-radius: var(--c8y-component-border-radius-base, 4px);\n \"\n >\n @for (size of previewSizes; track size) {\n <div class=\"d-flex d-col a-i-center\">\n <div\n class=\"d-flex a-i-center j-c-center text-default\"\n [style.width.px]=\"size\"\n [style.height.px]=\"size\"\n aria-hidden=\"true\"\n [innerHTML]=\"primaryPreviewHtml()\"\n ></div>\n <small class=\"text-muted\">{{ size }}</small>\n </div>\n }\n </div>\n </div>\n @if (primaryValidation(); as pv) {\n @if (pv.errors.length || pv.warnings.length) {\n <small\n class=\"form-control-feedback-message\"\n role=\"status\"\n >\n @for (error of pv.errors; track error) {\n <div class=\"d-block text-danger\">{{ error }}</div>\n }\n @for (warning of pv.warnings; track warning) {\n <div class=\"d-block text-warning\">{{ warning }}</div>\n }\n </small>\n }\n }\n </fieldset>\n }\n </c8y-form-group>\n } @else {\n <!-- C8Y duocolor: dark + light uploads side by side -->\n <div class=\"row d-flex-md gap-16\">\n <div class=\"col-sm-6\">\n <c8y-form-group [hasError]=\"hasPrimarySvgError()\">\n <label translate>Dark layer SVG</label>\n @if (!primarySvg()) {\n <c8y-drop-area\n class=\"drop-area-sm\"\n [icon]=\"'upload'\"\n [attr.aria-label]=\"'Upload dark layer SVG' | translate\"\n [accept]=\"'.svg'\"\n [maxAllowedFiles]=\"1\"\n [maxFileSizeInMegaBytes]=\"5\"\n [message]=\"'Drop dark layer SVG' | translate\"\n (dropped)=\"onPrimaryDropped($event)\"\n ></c8y-drop-area>\n <c8y-messages\n class=\"m-t-8\"\n role=\"status\"\n [show]=\"primarySvgErrors()\"\n [helpMessage]=\"\n hasPrimarySvgError()\n ? ''\n : ('Square viewBox \u00B7 currentColor fills \u00B7 main icon shape' | translate)\n \"\n >\n <c8y-message\n [name]=\"'required'\"\n [text]=\"'Upload an SVG file.' | translate\"\n ></c8y-message>\n @if (primaryValidation(); as pv) {\n @for (error of pv.errors; track error) {\n <div class=\"d-block text-danger\">{{ error }}</div>\n }\n @for (warning of pv.warnings; track warning) {\n <div class=\"d-block text-warning\">{{ warning }}</div>\n }\n }\n </c8y-messages>\n } @else {\n <div class=\"d-flex a-i-center gap-8 p-8 separator m-b-4\">\n <div\n class=\"d-flex a-i-center j-c-center\"\n [style.width.px]=\"32\"\n [style.height.px]=\"32\"\n aria-hidden=\"true\"\n [innerHTML]=\"primaryPreviewHtml()\"\n ></div>\n <span\n class=\"text-muted text-12 flex-grow\"\n translate\n >Dark layer</span\n >\n <button\n class=\"btn btn-dot btn-dot--danger\"\n [attr.aria-label]=\"'Remove dark layer SVG' | translate\"\n [tooltip]=\"'Remove dark layer SVG' | translate\"\n placement=\"top\"\n container=\"body\"\n [delay]=\"500\"\n (click)=\"clearPrimary()\"\n >\n <i\n [c8yIcon]=\"'minus-circle'\"\n aria-hidden=\"true\"\n ></i>\n </button>\n </div>\n @if (primaryValidation(); as pv) {\n @if (pv.errors.length || pv.warnings.length) {\n <small\n class=\"form-control-feedback-message\"\n role=\"status\"\n >\n @for (error of pv.errors; track error) {\n <div class=\"d-block text-danger\">{{ error }}</div>\n }\n @for (warning of pv.warnings; track warning) {\n <div class=\"d-block text-warning\">{{ warning }}</div>\n }\n </small>\n }\n }\n }\n </c8y-form-group>\n </div>\n <div class=\"col-sm-6\">\n <c8y-form-group [hasError]=\"hasLightSvgError()\">\n <label translate>Light layer SVG</label>\n @if (!lightSvg()) {\n <c8y-drop-area\n class=\"drop-area-sm\"\n [icon]=\"'upload'\"\n [attr.aria-label]=\"'Upload light layer SVG' | translate\"\n [accept]=\"'.svg'\"\n [maxAllowedFiles]=\"1\"\n [maxFileSizeInMegaBytes]=\"5\"\n [message]=\"'Drop light layer SVG' | translate\"\n (dropped)=\"onLightDropped($event)\"\n ></c8y-drop-area>\n <c8y-messages\n class=\"m-t-8\"\n role=\"status\"\n [show]=\"lightSvgErrors()\"\n [helpMessage]=\"\n hasLightSvgError()\n ? ''\n : ('Same viewBox as dark layer \u00B7 accent shape for the light tint'\n | translate)\n \"\n >\n <c8y-message\n [name]=\"'required'\"\n [text]=\"'Upload an SVG file.' | translate\"\n ></c8y-message>\n @if (lightValidation(); as lv) {\n @for (error of lv.errors; track error) {\n <div class=\"d-block text-danger\">{{ error }}</div>\n }\n @for (warning of lv.warnings; track warning) {\n <div class=\"d-block text-warning\">{{ warning }}</div>\n }\n }\n </c8y-messages>\n } @else {\n <div class=\"d-flex a-i-center gap-8 p-8 separator m-b-4\">\n <div\n class=\"d-flex a-i-center j-c-center\"\n [style.width.px]=\"32\"\n [style.height.px]=\"32\"\n aria-hidden=\"true\"\n [innerHTML]=\"lightPreviewHtml()\"\n ></div>\n <span\n class=\"text-muted text-12 flex-grow\"\n translate\n >Light layer</span\n >\n <button\n class=\"btn btn-dot btn-dot--danger\"\n [attr.aria-label]=\"'Remove light layer SVG' | translate\"\n [tooltip]=\"'Remove light layer SVG' | translate\"\n placement=\"top\"\n container=\"body\"\n [delay]=\"500\"\n (click)=\"clearLight()\"\n >\n <i\n [c8yIcon]=\"'minus-circle'\"\n aria-hidden=\"true\"\n ></i>\n </button>\n </div>\n @if (lightValidation(); as lv) {\n @if (lv.errors.length || lv.warnings.length) {\n <small\n class=\"form-control-feedback-message\"\n role=\"status\"\n >\n @for (error of lv.errors; track error) {\n <div class=\"d-block text-danger\">{{ error }}</div>\n }\n @for (warning of lv.warnings; track warning) {\n <div class=\"d-block text-warning\">{{ warning }}</div>\n }\n </small>\n }\n }\n }\n </c8y-form-group>\n </div>\n </div>\n\n @if (showCompositePreview()) {\n <fieldset\n class=\"c8y-fieldset m-t-16\"\n aria-hidden=\"true\"\n >\n <legend translate>Composited preview</legend>\n <div class=\"row d-flex-md gap-16 p-b-16\">\n <!-- Light background -->\n <div\n class=\"col-sm-6 d-flex a-i-end gap-8 j-c-around p-16 c8y-light-theme\"\n style=\"\n background: var(--c8y-root-component-background-default);\n border: 1px solid var(--c8y-root-component-border-color);\n border-radius: var(--c8y-component-border-radius-base, 4px);\n \"\n >\n @for (size of previewSizes; track size) {\n <div class=\"d-flex d-col a-i-center\">\n <div\n class=\"p-relative\"\n [style.width.px]=\"size\"\n [style.height.px]=\"size\"\n >\n <div\n class=\"d-flex a-i-center j-c-center fit-w fit-h\"\n style=\"color: var(--c8y-root-component-icon-dark-color-dark)\"\n [innerHTML]=\"primaryPreviewHtml()\"\n ></div>\n <div\n class=\"d-flex a-i-center j-c-center fit-w fit-h p-absolute\"\n style=\"\n top: 0;\n left: 0;\n color: var(--c8y-root-component-icon-dark-color-light);\n \"\n [innerHTML]=\"lightPreviewHtml()\"\n ></div>\n </div>\n <small class=\"text-muted\">{{ size }}</small>\n </div>\n }\n </div>\n <!-- Dark background -->\n <div\n class=\"col-sm-6 d-flex a-i-end gap-8 j-c-around p-16 c8y-dark-theme\"\n style=\"\n background: var(--c8y-root-component-background-default);\n border-radius: var(--c8y-component-border-radius-base, 4px);\n \"\n >\n @for (size of previewSizes; track size) {\n <div class=\"d-flex d-col a-i-center\">\n <div\n class=\"p-relative\"\n [style.width.px]=\"size\"\n [style.height.px]=\"size\"\n >\n <div\n class=\"d-flex a-i-center j-c-center fit-w fit-h\"\n style=\"color: var(--c8y-root-component-icon-dark-color-dark)\"\n [innerHTML]=\"primaryPreviewHtml()\"\n ></div>\n <div\n class=\"d-flex a-i-center j-c-center fit-w fit-h p-absolute\"\n style=\"\n top: 0;\n left: 0;\n color: var(\n --c8y-component-icon-dark-color-light,\n var(--c8y-root-component-icon-dark-color-light)\n );\n \"\n [innerHTML]=\"lightPreviewHtml()\"\n ></div>\n </div>\n <small class=\"text-muted\">{{ size }}</small>\n </div>\n }\n </div>\n </div>\n </fieldset>\n }\n }\n </div>\n </div>\n }\n</c8y-modal>\n" }]
952
+ ], changeDetection: ChangeDetectionStrategy.Eager, template: "<c8y-modal\n [title]=\"modalTitle() | translate\"\n [headerClasses]=\"'dialog-header'\"\n (onDismiss)=\"cancel()\"\n (onClose)=\"save()\"\n [close]=\"keepOpenOnOk\"\n [labels]=\"{ cancel: 'Cancel', ok: okLabel() | translate }\"\n [flushBody]=\"true\"\n>\n <ng-container c8y-modal-title>\n <span\n [c8yIcon]=\"isOverrideMode() ? 'replace' : isEditMode() ? 'pencil' : 'plus-circle-o'\"\n aria-hidden=\"true\"\n ></span>\n </ng-container>\n\n @if (!isReady()) {\n <!-- min-height matches the approximate form height so Bootstrap animates\n the modal at its full size before the form is rendered. -->\n <div\n class=\"p-32 d-flex a-i-center j-c-center\"\n style=\"min-height: 380px\"\n [attr.aria-label]=\"'Loading\u2026' | translate\"\n role=\"status\"\n >\n <c8y-loading></c8y-loading>\n </div>\n }\n\n @if (isReady()) {\n <div\n class=\"p-t-16 p-b-24 p-l-32 p-r-32\"\n #modalBody\n >\n @if (isOverrideMode()) {\n <p class=\"text-center text-16 text-bold text-balance m-b-16\">\n {{ 'Upload a replacement SVG for {{iconName}}' | translate: { iconName:\n editingIcon?.overriddenIconName } }}\n </p>\n }\n\n <!-- Name, collection and icon type (hidden in override mode) -->\n @if (!isOverrideMode()) {\n @if (!isEditMode()) {\n <p\n class=\"text-muted m-b-16\"\n translate\n >\n Upload a custom SVG icon and assign it to a collection to keep your icons organized.\n </p>\n }\n <div\n class=\"m-b-16\"\n [formGroup]=\"form\"\n >\n <c8y-form-group class=\"p-b-16\">\n <label\n for=\"iconName\"\n translate\n >Icon name</label\n >\n <input\n class=\"form-control\"\n id=\"iconName\"\n aria-required=\"true\"\n aria-describedby=\"iconName-messages\"\n type=\"text\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 'acme-logo' }\"\n formControlName=\"name\"\n />\n <c8y-messages\n id=\"iconName-messages\"\n role=\"status\"\n [helpMessage]=\"'Lowercase letters, numbers and hyphens only' | translate\"\n >\n @for (message of nameMessages; track message.name) {\n <c8y-message\n [name]=\"message.name\"\n [text]=\"message.text | translate\"\n ></c8y-message>\n }\n </c8y-messages>\n </c8y-form-group>\n\n <c8y-form-group [hasError]=\"hasCategoryError()\">\n <label\n for=\"iconCategory\"\n translate\n >Collection</label\n >\n <div class=\"p-relative\">\n <c8y-typeahead\n [attr.aria-label]=\"'Icon collection' | translate\"\n id=\"iconCategory\"\n #categoryTypeahead\n [allowFreeEntries]=\"true\"\n [hideNew]=\"true\"\n [placeholder]=\"'Select or enter a collection name' | translate\"\n (onSearch)=\"onCategorySearch($event)\"\n >\n @for (cat of filteredCategories(); track cat) {\n <c8y-li\n class=\"p-l-8 p-r-8 c8y-list__item--link\"\n (click)=\"selectCategory(cat)\"\n [active]=\"form.value.category === cat\"\n >{{ cat | translate }}</c8y-li\n >\n }\n @if (showAddNew()) {\n <c8y-li class=\"bg-level-2 p-8\">\n @if (hasNoMatch()) {\n <span\n class=\"m-r-8\"\n translate\n >No existing collection found.</span\n >\n }\n <button\n class=\"btn btn-default btn-xs\"\n type=\"button\"\n (click)=\"selectCategory(categorySearch())\"\n >\n {{ 'Add new collection`collection`' | translate }}\n </button>\n </c8y-li>\n }\n </c8y-typeahead>\n @if (categorySearch()) {\n <button\n class=\"btn btn-dot p-absolute\"\n style=\"right: 36px; z-index: 10\"\n [attr.aria-label]=\"'Clear collection' | translate\"\n type=\"button\"\n (click)=\"clearCategory()\"\n >\n <i\n [c8yIcon]=\"'times'\"\n aria-hidden=\"true\"\n ></i>\n </button>\n }\n </div>\n <c8y-messages\n role=\"alert\"\n [show]=\"categoryErrors()\"\n >\n <c8y-message\n [name]=\"'required'\"\n [text]=\"'Collection is required.' | translate\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n\n @if (!isEditMode()) {\n <fieldset class=\"c8y-fieldset m-b-24\">\n <legend translate>Icon type</legend>\n <div class=\"d-flex gap-16 p-b-16\">\n <label class=\"c8y-radio a-i-start\">\n <input\n name=\"iconFamily\"\n type=\"radio\"\n [checked]=\"selectedFamily() === 'dlt'\"\n (change)=\"onFamilyChange('dlt')\"\n />\n <span></span>\n <span\n >{{ 'Single color' | translate }}<br />\n <small\n class=\"text-muted d-block\"\n translate\n >\n Single SVG, inherits text color via currentColor.\n </small>\n </span>\n </label>\n\n <label class=\"c8y-radio a-i-start\">\n <input\n name=\"iconFamily\"\n type=\"radio\"\n [checked]=\"selectedFamily() === 'c8y'\"\n (change)=\"onFamilyChange('c8y')\"\n />\n <span></span>\n <span>\n {{ 'Duocolor' | translate }}<br />\n <small\n class=\"text-muted d-block\"\n translate\n >\n Two SVGs required: a dark layer and a light accent layer.\n </small>\n </span>\n </label>\n </div>\n </fieldset>\n }\n </div>\n }\n\n <!-- SVG uploads -->\n <div\n class=\"m-b-16\"\n [class.row]=\"selectedFamily() === 'c8y'\"\n >\n @if (selectedFamily() === 'dlt') {\n <c8y-form-group [hasError]=\"hasPrimarySvgError()\">\n <label translate>Icon SVG</label>\n @if (!primarySvg()) {\n <c8y-drop-area\n class=\"drop-area-sm\"\n [icon]=\"'upload'\"\n [title]=\"'Upload SVG icon' | translate\"\n [attr.aria-label]=\"'Upload SVG icon' | translate\"\n [accept]=\"'.svg'\"\n [maxAllowedFiles]=\"1\"\n [maxFileSizeInMegaBytes]=\"5\"\n [message]=\"'Drop SVG file here or click to browse' | translate\"\n (dropped)=\"onPrimaryDropped($event)\"\n ></c8y-drop-area>\n <c8y-messages\n class=\"m-t-8\"\n role=\"status\"\n [show]=\"primarySvgErrors()\"\n [helpMessage]=\"\n hasPrimarySvgError()\n ? ''\n : ('Square viewBox (e.g. 24\u00D724) \u00B7 currentColor fills \u00B7 no external refs or scripts'\n | translate)\n \"\n >\n <c8y-message\n [name]=\"'required'\"\n [text]=\"'Upload an SVG file.' | translate\"\n ></c8y-message>\n @if (primaryValidation(); as pv) {\n @for (error of pv.errors; track error) {\n <div class=\"d-block text-danger\">{{ error }}</div>\n }\n @for (warning of pv.warnings; track warning) {\n <div class=\"d-block text-warning\">{{ warning }}</div>\n }\n }\n </c8y-messages>\n } @else {\n <fieldset class=\"c8y-fieldset m-b-8\">\n <legend class=\"d-flex a-i-center\">\n <span translate>Preview</span>\n <button\n class=\"btn btn-dot btn-dot--danger m-l-8\"\n [attr.aria-label]=\"'Remove SVG preview' | translate\"\n [tooltip]=\"'Remove SVG preview' | translate\"\n placement=\"top\"\n container=\"body\"\n [delay]=\"500\"\n (click)=\"clearPrimary()\"\n >\n <i\n [c8yIcon]=\"'minus-circle'\"\n aria-hidden=\"true\"\n ></i>\n </button>\n </legend>\n <div class=\"row d-flex a-i-center gap-16 p-b-16\">\n <div\n class=\"col-sm-6 d-flex a-i-end gap-8 j-c-around p-16 c8y-light-theme\"\n style=\"\n background: var(--c8y-root-component-background-default);\n border: 1px solid var(--c8y-root-component-border-color);\n border-radius: var(--c8y-component-border-radius-base, 4px);\n \"\n >\n @for (size of previewSizes; track size) {\n <div class=\"d-flex d-col a-i-center\">\n <div\n class=\"d-flex a-i-center j-c-center text-default\"\n [style.width.px]=\"size\"\n [style.height.px]=\"size\"\n aria-hidden=\"true\"\n [innerHTML]=\"primaryPreviewHtml()\"\n ></div>\n <small class=\"text-muted\">{{ size }}</small>\n </div>\n }\n </div>\n <div\n class=\"col-sm-6 d-flex a-i-end gap-16 j-c-around p-16 c8y-dark-theme\"\n style=\"\n background: var(--c8y-root-component-background-default);\n border: 1px solid var(--c8y-root-component-border-color);\n border-radius: var(--c8y-component-border-radius-base, 4px);\n \"\n >\n @for (size of previewSizes; track size) {\n <div class=\"d-flex d-col a-i-center\">\n <div\n class=\"d-flex a-i-center j-c-center text-default\"\n [style.width.px]=\"size\"\n [style.height.px]=\"size\"\n aria-hidden=\"true\"\n [innerHTML]=\"primaryPreviewHtml()\"\n ></div>\n <small class=\"text-muted\">{{ size }}</small>\n </div>\n }\n </div>\n </div>\n @if (primaryValidation(); as pv) {\n @if (pv.errors.length || pv.warnings.length) {\n <small\n class=\"form-control-feedback-message\"\n role=\"status\"\n >\n @for (error of pv.errors; track error) {\n <div class=\"d-block text-danger\">{{ error }}</div>\n }\n @for (warning of pv.warnings; track warning) {\n <div class=\"d-block text-warning\">{{ warning }}</div>\n }\n </small>\n }\n }\n </fieldset>\n }\n </c8y-form-group>\n } @else {\n <!-- C8Y duocolor: dark + light uploads side by side -->\n <div class=\"row d-flex-md gap-16\">\n <div class=\"col-sm-6\">\n <c8y-form-group [hasError]=\"hasPrimarySvgError()\">\n <label translate>Dark layer SVG</label>\n @if (!primarySvg()) {\n <c8y-drop-area\n class=\"drop-area-sm\"\n [icon]=\"'upload'\"\n [attr.aria-label]=\"'Upload dark layer SVG' | translate\"\n [accept]=\"'.svg'\"\n [maxAllowedFiles]=\"1\"\n [maxFileSizeInMegaBytes]=\"5\"\n [message]=\"'Drop dark layer SVG' | translate\"\n (dropped)=\"onPrimaryDropped($event)\"\n ></c8y-drop-area>\n <c8y-messages\n class=\"m-t-8\"\n role=\"status\"\n [show]=\"primarySvgErrors()\"\n [helpMessage]=\"\n hasPrimarySvgError()\n ? ''\n : ('Square viewBox \u00B7 currentColor fills \u00B7 main icon shape' | translate)\n \"\n >\n <c8y-message\n [name]=\"'required'\"\n [text]=\"'Upload an SVG file.' | translate\"\n ></c8y-message>\n @if (primaryValidation(); as pv) {\n @for (error of pv.errors; track error) {\n <div class=\"d-block text-danger\">{{ error }}</div>\n }\n @for (warning of pv.warnings; track warning) {\n <div class=\"d-block text-warning\">{{ warning }}</div>\n }\n }\n </c8y-messages>\n } @else {\n <div class=\"d-flex a-i-center gap-8 p-8 separator m-b-4\">\n <div\n class=\"d-flex a-i-center j-c-center\"\n [style.width.px]=\"32\"\n [style.height.px]=\"32\"\n aria-hidden=\"true\"\n [innerHTML]=\"primaryPreviewHtml()\"\n ></div>\n <span\n class=\"text-muted text-12 flex-grow\"\n translate\n >Dark layer</span\n >\n <button\n class=\"btn btn-dot btn-dot--danger\"\n [attr.aria-label]=\"'Remove dark layer SVG' | translate\"\n [tooltip]=\"'Remove dark layer SVG' | translate\"\n placement=\"top\"\n container=\"body\"\n [delay]=\"500\"\n (click)=\"clearPrimary()\"\n >\n <i\n [c8yIcon]=\"'minus-circle'\"\n aria-hidden=\"true\"\n ></i>\n </button>\n </div>\n @if (primaryValidation(); as pv) {\n @if (pv.errors.length || pv.warnings.length) {\n <small\n class=\"form-control-feedback-message\"\n role=\"status\"\n >\n @for (error of pv.errors; track error) {\n <div class=\"d-block text-danger\">{{ error }}</div>\n }\n @for (warning of pv.warnings; track warning) {\n <div class=\"d-block text-warning\">{{ warning }}</div>\n }\n </small>\n }\n }\n }\n </c8y-form-group>\n </div>\n <div class=\"col-sm-6\">\n <c8y-form-group [hasError]=\"hasLightSvgError()\">\n <label translate>Light layer SVG</label>\n @if (!lightSvg()) {\n <c8y-drop-area\n class=\"drop-area-sm\"\n [icon]=\"'upload'\"\n [attr.aria-label]=\"'Upload light layer SVG' | translate\"\n [accept]=\"'.svg'\"\n [maxAllowedFiles]=\"1\"\n [maxFileSizeInMegaBytes]=\"5\"\n [message]=\"'Drop light layer SVG' | translate\"\n (dropped)=\"onLightDropped($event)\"\n ></c8y-drop-area>\n <c8y-messages\n class=\"m-t-8\"\n role=\"status\"\n [show]=\"lightSvgErrors()\"\n [helpMessage]=\"\n hasLightSvgError()\n ? ''\n : ('Same viewBox as dark layer \u00B7 accent shape for the light tint'\n | translate)\n \"\n >\n <c8y-message\n [name]=\"'required'\"\n [text]=\"'Upload an SVG file.' | translate\"\n ></c8y-message>\n @if (lightValidation(); as lv) {\n @for (error of lv.errors; track error) {\n <div class=\"d-block text-danger\">{{ error }}</div>\n }\n @for (warning of lv.warnings; track warning) {\n <div class=\"d-block text-warning\">{{ warning }}</div>\n }\n }\n </c8y-messages>\n } @else {\n <div class=\"d-flex a-i-center gap-8 p-8 separator m-b-4\">\n <div\n class=\"d-flex a-i-center j-c-center\"\n [style.width.px]=\"32\"\n [style.height.px]=\"32\"\n aria-hidden=\"true\"\n [innerHTML]=\"lightPreviewHtml()\"\n ></div>\n <span\n class=\"text-muted text-12 flex-grow\"\n translate\n >Light layer</span\n >\n <button\n class=\"btn btn-dot btn-dot--danger\"\n [attr.aria-label]=\"'Remove light layer SVG' | translate\"\n [tooltip]=\"'Remove light layer SVG' | translate\"\n placement=\"top\"\n container=\"body\"\n [delay]=\"500\"\n (click)=\"clearLight()\"\n >\n <i\n [c8yIcon]=\"'minus-circle'\"\n aria-hidden=\"true\"\n ></i>\n </button>\n </div>\n @if (lightValidation(); as lv) {\n @if (lv.errors.length || lv.warnings.length) {\n <small\n class=\"form-control-feedback-message\"\n role=\"status\"\n >\n @for (error of lv.errors; track error) {\n <div class=\"d-block text-danger\">{{ error }}</div>\n }\n @for (warning of lv.warnings; track warning) {\n <div class=\"d-block text-warning\">{{ warning }}</div>\n }\n </small>\n }\n }\n }\n </c8y-form-group>\n </div>\n </div>\n\n @if (showCompositePreview()) {\n <fieldset\n class=\"c8y-fieldset m-t-16\"\n aria-hidden=\"true\"\n >\n <legend translate>Composited preview</legend>\n <div class=\"row d-flex-md gap-16 p-b-16\">\n <!-- Light background -->\n <div\n class=\"col-sm-6 d-flex a-i-end gap-8 j-c-around p-16 c8y-light-theme\"\n style=\"\n background: var(--c8y-root-component-background-default);\n border: 1px solid var(--c8y-root-component-border-color);\n border-radius: var(--c8y-component-border-radius-base, 4px);\n \"\n >\n @for (size of previewSizes; track size) {\n <div class=\"d-flex d-col a-i-center\">\n <div\n class=\"p-relative\"\n [style.width.px]=\"size\"\n [style.height.px]=\"size\"\n >\n <div\n class=\"d-flex a-i-center j-c-center fit-w fit-h\"\n style=\"color: var(--c8y-root-component-icon-dark-color-dark)\"\n [innerHTML]=\"primaryPreviewHtml()\"\n ></div>\n <div\n class=\"d-flex a-i-center j-c-center fit-w fit-h p-absolute\"\n style=\"\n top: 0;\n left: 0;\n color: var(--c8y-root-component-icon-dark-color-light);\n \"\n [innerHTML]=\"lightPreviewHtml()\"\n ></div>\n </div>\n <small class=\"text-muted\">{{ size }}</small>\n </div>\n }\n </div>\n <!-- Dark background -->\n <div\n class=\"col-sm-6 d-flex a-i-end gap-8 j-c-around p-16 c8y-dark-theme\"\n style=\"\n background: var(--c8y-root-component-background-default);\n border-radius: var(--c8y-component-border-radius-base, 4px);\n \"\n >\n @for (size of previewSizes; track size) {\n <div class=\"d-flex d-col a-i-center\">\n <div\n class=\"p-relative\"\n [style.width.px]=\"size\"\n [style.height.px]=\"size\"\n >\n <div\n class=\"d-flex a-i-center j-c-center fit-w fit-h\"\n style=\"color: var(--c8y-root-component-icon-dark-color-dark)\"\n [innerHTML]=\"primaryPreviewHtml()\"\n ></div>\n <div\n class=\"d-flex a-i-center j-c-center fit-w fit-h p-absolute\"\n style=\"\n top: 0;\n left: 0;\n color: var(\n --c8y-component-icon-dark-color-light,\n var(--c8y-root-component-icon-dark-color-light)\n );\n \"\n [innerHTML]=\"lightPreviewHtml()\"\n ></div>\n </div>\n <small class=\"text-muted\">{{ size }}</small>\n </div>\n }\n </div>\n </div>\n </fieldset>\n }\n }\n </div>\n </div>\n }\n</c8y-modal>\n" }]
953
953
  }], ctorParameters: () => [], propDecorators: { categoryTypeahead: [{ type: i0.ViewChild, args: ['categoryTypeahead', { isSignal: true }] }], modalBody: [{ type: i0.ViewChild, args: ['modalBody', { isSignal: true }] }] } });
954
954
 
955
955
  class AddCustomIconModalService {
@@ -1114,7 +1114,7 @@ class ImportCustomIconsModalComponent {
1114
1114
  return this.domSanitizer.bypassSecurityTrustHtml(sanitized.replace(/<svg/i, '<svg width="100%" height="100%" style="display:block"'));
1115
1115
  }
1116
1116
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: ImportCustomIconsModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1117
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.19", type: ImportCustomIconsModalComponent, isStandalone: true, selector: "c8y-import-custom-icons-modal", ngImport: i0, template: "<c8y-modal\n [title]=\"'Import custom icons' | translate\"\n [headerClasses]=\"'dialog-header'\"\n (onDismiss)=\"close()\"\n (onClose)=\"confirmImport()\"\n [close]=\"keepOpen\"\n [dismiss]=\"keepOpen\"\n [disabled]=\"state() !== 'previewing' || toImportIcons().length === 0\"\n [labels]=\"{\n cancel: 'Cancel',\n ok: 'Import' | translate\n }\"\n>\n <ng-container c8y-modal-title>\n <span\n [c8yIcon]=\"'data-import'\"\n aria-hidden=\"true\"\n ></span>\n </ng-container>\n\n <div class=\"p-24\">\n @if (state() === 'idle') {\n <p\n class=\"text-16 text-center text-balance m-b-16\"\n translate\n >\n Upload a ZIP file previously exported from the icon selector to import custom icons into\n this tenant.\n </p>\n <div class=\"d-flex j-c-center\">\n <c8y-drop-area\n [icon]=\"'upload'\"\n style=\"width: 390px\"\n [title]=\"'Upload ZIP file' | translate\"\n accept=\".zip\"\n [maxAllowedFiles]=\"1\"\n [message]=\"'Drop ZIP file here or click to browse' | translate\"\n (dropped)=\"onZipDropped($event)\"\n ></c8y-drop-area>\n @if (dropError()) {\n <div\n class=\"alert alert-danger m-t-8\"\n role=\"alert\"\n >\n {{ dropError() }}\n </div>\n }\n </div>\n }\n\n @if (state() === 'previewing') {\n @if (toImportIcons().length) {\n <p\n class=\"m-b-8 text-bold\"\n translate\n >\n The following icons will be imported:\n </p>\n <ul class=\"list-unstyled m-b-16 separator-top\">\n @for (icon of toImportIcons(); track icon.name) {\n <li class=\"d-flex a-i-center gap-8 p-t-8 p-b-8 separator-bottom\">\n <span\n class=\"p-relative flex-no-shrink\"\n [style.width.px]=\"24\"\n [style.height.px]=\"24\"\n aria-hidden=\"true\"\n >\n <span\n class=\"d-flex a-i-center j-c-center fit-w fit-h\"\n [class.text-default]=\"!icon.previewHtmlSecondary\"\n [style.color]=\"\n icon.previewHtmlSecondary\n ? 'var(--c8y-root-component-icon-dark-color-dark)'\n : null\n \"\n [innerHTML]=\"icon.previewHtml\"\n ></span>\n @if (icon.previewHtmlSecondary) {\n <span\n class=\"d-flex a-i-center j-c-center fit-w fit-h p-absolute\"\n style=\"top: 0; left: 0; color: var(--c8y-root-component-icon-dark-color-light)\"\n [innerHTML]=\"icon.previewHtmlSecondary\"\n ></span>\n }\n </span>\n <span class=\"flex-grow min-width-0 text-truncate\">\n {{ icon.name }}\n @if (icon.category && !icon.isOverride) {\n <small class=\"text-muted m-l-4\">{{ icon.category | translate }}</small>\n }\n </span>\n <span class=\"tag tag--info flex-no-shrink\">\n {{ familyLabels[icon.family] | translate }}\n </span>\n @if (icon.isOverride) {\n <span\n class=\"tag tag--warning flex-no-shrink\"\n translate\n >Override</span\n >\n }\n </li>\n }\n </ul>\n } @else if (allSkippedExist()) {\n <div\n class=\"alert alert-warning\"\n role=\"status\"\n translate\n >\n All icons in this file already exist in this tenant.\n </div>\n } @else {\n <div\n class=\"alert alert-warning\"\n role=\"status\"\n translate\n >\n No icons to import.\n </div>\n }\n @if (skippedIcons().length && !(allSkippedExist() && toImportIcons().length === 0)) {\n <p\n class=\"m-b-8 text-bold\"\n translate\n >\n The following icons will be skipped:\n </p>\n <ul class=\"list-unstyled m-b-16 separator-top\">\n @for (icon of skippedIcons(); track icon.name) {\n <li class=\"d-flex a-i-center gap-8 p-t-8 p-b-8 separator-bottom\">\n <span\n class=\"p-relative flex-no-shrink\"\n [style.width.px]=\"24\"\n [style.height.px]=\"24\"\n aria-hidden=\"true\"\n >\n <span\n class=\"d-flex a-i-center j-c-center fit-w fit-h\"\n [class.text-default]=\"!icon.previewHtmlSecondary\"\n [style.color]=\"\n icon.previewHtmlSecondary\n ? 'var(--c8y-root-component-icon-dark-color-dark)'\n : null\n \"\n [innerHTML]=\"icon.previewHtml\"\n ></span>\n @if (icon.previewHtmlSecondary) {\n <span\n class=\"d-flex a-i-center j-c-center fit-w fit-h p-absolute\"\n style=\"top: 0; left: 0; color: var(--c8y-root-component-icon-dark-color-light)\"\n [innerHTML]=\"icon.previewHtmlSecondary\"\n ></span>\n }\n </span>\n <span class=\"flex-grow min-width-0 text-truncate\">\n {{ icon.name }}\n @if (icon.category && !icon.isOverride) {\n <small class=\"text-muted m-l-4\">{{ icon.category | translate }}</small>\n }\n </span>\n <span class=\"tag tag--warning flex-no-shrink\">\n {{ skipReasonLabels[icon.reason] | translate }}\n </span>\n </li>\n }\n </ul>\n }\n }\n\n @if (state() === 'importing') {\n <c8y-loading\n role=\"status\"\n [message]=\"'Importing icons\u2026' | translate\"\n ></c8y-loading>\n }\n </div>\n</c8y-modal>\n", dependencies: [{ kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "component", type: DropAreaComponent, selector: "c8y-drop-area", inputs: ["formControl", "title", "message", "icon", "loadingMessage", "forceHideList", "alwaysShow", "clickToOpen", "loading", "progress", "maxAllowedFiles", "files", "maxFileSizeInMegaBytes", "accept"], outputs: ["dropped"] }, { kind: "component", type: LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "progressLabel", "message"] }, { kind: "component", type: ModalComponent, selector: "c8y-modal", inputs: ["disabled", "close", "dismiss", "title", "body", "customFooter", "headerClasses", "labels"], outputs: ["onDismiss", "onClose"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1117
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.19", type: ImportCustomIconsModalComponent, isStandalone: true, selector: "c8y-import-custom-icons-modal", ngImport: i0, template: "<c8y-modal\n [title]=\"'Import custom icons' | translate\"\n [headerClasses]=\"'dialog-header'\"\n (onDismiss)=\"close()\"\n (onClose)=\"confirmImport()\"\n [close]=\"keepOpen\"\n [dismiss]=\"keepOpen\"\n [disabled]=\"state() !== 'previewing' || toImportIcons().length === 0\"\n [labels]=\"{\n cancel: 'Cancel',\n ok: 'Import' | translate\n }\"\n>\n <ng-container c8y-modal-title>\n <span\n [c8yIcon]=\"'data-import'\"\n aria-hidden=\"true\"\n ></span>\n </ng-container>\n\n <div>\n @if (state() === 'idle') {\n <p\n class=\"text-16 text-center text-balance m-b-16\"\n translate\n >\n Upload a ZIP file previously exported from the icon selector to import custom icons into\n this tenant.\n </p>\n <div class=\"d-flex j-c-center\">\n <c8y-drop-area\n [icon]=\"'upload'\"\n style=\"width: 390px\"\n [title]=\"'Upload ZIP file' | translate\"\n accept=\".zip\"\n [maxAllowedFiles]=\"1\"\n [message]=\"'Drop ZIP file here or click to browse' | translate\"\n (dropped)=\"onZipDropped($event)\"\n ></c8y-drop-area>\n @if (dropError()) {\n <div\n class=\"alert alert-danger m-t-8\"\n role=\"alert\"\n >\n {{ dropError() }}\n </div>\n }\n </div>\n }\n\n @if (state() === 'previewing') {\n @if (toImportIcons().length) {\n <p\n class=\"m-b-8 text-bold\"\n translate\n >\n The following icons will be imported:\n </p>\n <ul class=\"list-unstyled m-b-16 separator-top\">\n @for (icon of toImportIcons(); track icon.name) {\n <li class=\"d-flex a-i-center gap-8 p-t-8 p-b-8 separator-bottom\">\n <span\n class=\"p-relative flex-no-shrink\"\n [style.width.px]=\"24\"\n [style.height.px]=\"24\"\n aria-hidden=\"true\"\n >\n <span\n class=\"d-flex a-i-center j-c-center fit-w fit-h\"\n [class.text-default]=\"!icon.previewHtmlSecondary\"\n [style.color]=\"\n icon.previewHtmlSecondary\n ? 'var(--c8y-root-component-icon-dark-color-dark)'\n : null\n \"\n [innerHTML]=\"icon.previewHtml\"\n ></span>\n @if (icon.previewHtmlSecondary) {\n <span\n class=\"d-flex a-i-center j-c-center fit-w fit-h p-absolute\"\n style=\"top: 0; left: 0; color: var(--c8y-root-component-icon-dark-color-light)\"\n [innerHTML]=\"icon.previewHtmlSecondary\"\n ></span>\n }\n </span>\n <span class=\"flex-grow min-width-0 text-truncate\">\n {{ icon.name }}\n @if (icon.category && !icon.isOverride) {\n <small class=\"text-muted m-l-4\">{{ icon.category | translate }}</small>\n }\n </span>\n <span class=\"tag tag--info flex-no-shrink\">\n {{ familyLabels[icon.family] | translate }}\n </span>\n @if (icon.isOverride) {\n <span\n class=\"tag tag--warning flex-no-shrink\"\n translate\n >Override</span\n >\n }\n </li>\n }\n </ul>\n } @else if (allSkippedExist()) {\n <div\n class=\"alert alert-warning\"\n role=\"status\"\n translate\n >\n All icons in this file already exist in this tenant.\n </div>\n } @else {\n <div\n class=\"alert alert-warning\"\n role=\"status\"\n translate\n >\n No icons to import.\n </div>\n }\n @if (skippedIcons().length && !(allSkippedExist() && toImportIcons().length === 0)) {\n <p\n class=\"m-b-8 text-bold\"\n translate\n >\n The following icons will be skipped:\n </p>\n <ul class=\"list-unstyled m-b-16 separator-top\">\n @for (icon of skippedIcons(); track icon.name) {\n <li class=\"d-flex a-i-center gap-8 p-t-8 p-b-8 separator-bottom\">\n <span\n class=\"p-relative flex-no-shrink\"\n [style.width.px]=\"24\"\n [style.height.px]=\"24\"\n aria-hidden=\"true\"\n >\n <span\n class=\"d-flex a-i-center j-c-center fit-w fit-h\"\n [class.text-default]=\"!icon.previewHtmlSecondary\"\n [style.color]=\"\n icon.previewHtmlSecondary\n ? 'var(--c8y-root-component-icon-dark-color-dark)'\n : null\n \"\n [innerHTML]=\"icon.previewHtml\"\n ></span>\n @if (icon.previewHtmlSecondary) {\n <span\n class=\"d-flex a-i-center j-c-center fit-w fit-h p-absolute\"\n style=\"top: 0; left: 0; color: var(--c8y-root-component-icon-dark-color-light)\"\n [innerHTML]=\"icon.previewHtmlSecondary\"\n ></span>\n }\n </span>\n <span class=\"flex-grow min-width-0 text-truncate\">\n {{ icon.name }}\n @if (icon.category && !icon.isOverride) {\n <small class=\"text-muted m-l-4\">{{ icon.category | translate }}</small>\n }\n </span>\n <span class=\"tag tag--warning flex-no-shrink\">\n {{ skipReasonLabels[icon.reason] | translate }}\n </span>\n </li>\n }\n </ul>\n }\n }\n\n @if (state() === 'importing') {\n <c8y-loading\n role=\"status\"\n [message]=\"'Importing icons\u2026' | translate\"\n ></c8y-loading>\n }\n </div>\n</c8y-modal>\n", dependencies: [{ kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "component", type: DropAreaComponent, selector: "c8y-drop-area", inputs: ["formControl", "title", "message", "icon", "loadingMessage", "forceHideList", "alwaysShow", "clickToOpen", "loading", "progress", "maxAllowedFiles", "files", "maxFileSizeInMegaBytes", "accept"], outputs: ["dropped"] }, { kind: "component", type: LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "progressLabel", "message"] }, { kind: "component", type: ModalComponent, selector: "c8y-modal", inputs: ["disabled", "close", "dismiss", "title", "body", "customFooter", "headerClasses", "flushBody", "labels"], outputs: ["onDismiss", "onClose"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1118
1118
  }
1119
1119
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: ImportCustomIconsModalComponent, decorators: [{
1120
1120
  type: Component,
@@ -1125,7 +1125,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
1125
1125
  LoadingComponent,
1126
1126
  ModalComponent,
1127
1127
  IconDirective
1128
- ], template: "<c8y-modal\n [title]=\"'Import custom icons' | translate\"\n [headerClasses]=\"'dialog-header'\"\n (onDismiss)=\"close()\"\n (onClose)=\"confirmImport()\"\n [close]=\"keepOpen\"\n [dismiss]=\"keepOpen\"\n [disabled]=\"state() !== 'previewing' || toImportIcons().length === 0\"\n [labels]=\"{\n cancel: 'Cancel',\n ok: 'Import' | translate\n }\"\n>\n <ng-container c8y-modal-title>\n <span\n [c8yIcon]=\"'data-import'\"\n aria-hidden=\"true\"\n ></span>\n </ng-container>\n\n <div class=\"p-24\">\n @if (state() === 'idle') {\n <p\n class=\"text-16 text-center text-balance m-b-16\"\n translate\n >\n Upload a ZIP file previously exported from the icon selector to import custom icons into\n this tenant.\n </p>\n <div class=\"d-flex j-c-center\">\n <c8y-drop-area\n [icon]=\"'upload'\"\n style=\"width: 390px\"\n [title]=\"'Upload ZIP file' | translate\"\n accept=\".zip\"\n [maxAllowedFiles]=\"1\"\n [message]=\"'Drop ZIP file here or click to browse' | translate\"\n (dropped)=\"onZipDropped($event)\"\n ></c8y-drop-area>\n @if (dropError()) {\n <div\n class=\"alert alert-danger m-t-8\"\n role=\"alert\"\n >\n {{ dropError() }}\n </div>\n }\n </div>\n }\n\n @if (state() === 'previewing') {\n @if (toImportIcons().length) {\n <p\n class=\"m-b-8 text-bold\"\n translate\n >\n The following icons will be imported:\n </p>\n <ul class=\"list-unstyled m-b-16 separator-top\">\n @for (icon of toImportIcons(); track icon.name) {\n <li class=\"d-flex a-i-center gap-8 p-t-8 p-b-8 separator-bottom\">\n <span\n class=\"p-relative flex-no-shrink\"\n [style.width.px]=\"24\"\n [style.height.px]=\"24\"\n aria-hidden=\"true\"\n >\n <span\n class=\"d-flex a-i-center j-c-center fit-w fit-h\"\n [class.text-default]=\"!icon.previewHtmlSecondary\"\n [style.color]=\"\n icon.previewHtmlSecondary\n ? 'var(--c8y-root-component-icon-dark-color-dark)'\n : null\n \"\n [innerHTML]=\"icon.previewHtml\"\n ></span>\n @if (icon.previewHtmlSecondary) {\n <span\n class=\"d-flex a-i-center j-c-center fit-w fit-h p-absolute\"\n style=\"top: 0; left: 0; color: var(--c8y-root-component-icon-dark-color-light)\"\n [innerHTML]=\"icon.previewHtmlSecondary\"\n ></span>\n }\n </span>\n <span class=\"flex-grow min-width-0 text-truncate\">\n {{ icon.name }}\n @if (icon.category && !icon.isOverride) {\n <small class=\"text-muted m-l-4\">{{ icon.category | translate }}</small>\n }\n </span>\n <span class=\"tag tag--info flex-no-shrink\">\n {{ familyLabels[icon.family] | translate }}\n </span>\n @if (icon.isOverride) {\n <span\n class=\"tag tag--warning flex-no-shrink\"\n translate\n >Override</span\n >\n }\n </li>\n }\n </ul>\n } @else if (allSkippedExist()) {\n <div\n class=\"alert alert-warning\"\n role=\"status\"\n translate\n >\n All icons in this file already exist in this tenant.\n </div>\n } @else {\n <div\n class=\"alert alert-warning\"\n role=\"status\"\n translate\n >\n No icons to import.\n </div>\n }\n @if (skippedIcons().length && !(allSkippedExist() && toImportIcons().length === 0)) {\n <p\n class=\"m-b-8 text-bold\"\n translate\n >\n The following icons will be skipped:\n </p>\n <ul class=\"list-unstyled m-b-16 separator-top\">\n @for (icon of skippedIcons(); track icon.name) {\n <li class=\"d-flex a-i-center gap-8 p-t-8 p-b-8 separator-bottom\">\n <span\n class=\"p-relative flex-no-shrink\"\n [style.width.px]=\"24\"\n [style.height.px]=\"24\"\n aria-hidden=\"true\"\n >\n <span\n class=\"d-flex a-i-center j-c-center fit-w fit-h\"\n [class.text-default]=\"!icon.previewHtmlSecondary\"\n [style.color]=\"\n icon.previewHtmlSecondary\n ? 'var(--c8y-root-component-icon-dark-color-dark)'\n : null\n \"\n [innerHTML]=\"icon.previewHtml\"\n ></span>\n @if (icon.previewHtmlSecondary) {\n <span\n class=\"d-flex a-i-center j-c-center fit-w fit-h p-absolute\"\n style=\"top: 0; left: 0; color: var(--c8y-root-component-icon-dark-color-light)\"\n [innerHTML]=\"icon.previewHtmlSecondary\"\n ></span>\n }\n </span>\n <span class=\"flex-grow min-width-0 text-truncate\">\n {{ icon.name }}\n @if (icon.category && !icon.isOverride) {\n <small class=\"text-muted m-l-4\">{{ icon.category | translate }}</small>\n }\n </span>\n <span class=\"tag tag--warning flex-no-shrink\">\n {{ skipReasonLabels[icon.reason] | translate }}\n </span>\n </li>\n }\n </ul>\n }\n }\n\n @if (state() === 'importing') {\n <c8y-loading\n role=\"status\"\n [message]=\"'Importing icons\u2026' | translate\"\n ></c8y-loading>\n }\n </div>\n</c8y-modal>\n" }]
1128
+ ], template: "<c8y-modal\n [title]=\"'Import custom icons' | translate\"\n [headerClasses]=\"'dialog-header'\"\n (onDismiss)=\"close()\"\n (onClose)=\"confirmImport()\"\n [close]=\"keepOpen\"\n [dismiss]=\"keepOpen\"\n [disabled]=\"state() !== 'previewing' || toImportIcons().length === 0\"\n [labels]=\"{\n cancel: 'Cancel',\n ok: 'Import' | translate\n }\"\n>\n <ng-container c8y-modal-title>\n <span\n [c8yIcon]=\"'data-import'\"\n aria-hidden=\"true\"\n ></span>\n </ng-container>\n\n <div>\n @if (state() === 'idle') {\n <p\n class=\"text-16 text-center text-balance m-b-16\"\n translate\n >\n Upload a ZIP file previously exported from the icon selector to import custom icons into\n this tenant.\n </p>\n <div class=\"d-flex j-c-center\">\n <c8y-drop-area\n [icon]=\"'upload'\"\n style=\"width: 390px\"\n [title]=\"'Upload ZIP file' | translate\"\n accept=\".zip\"\n [maxAllowedFiles]=\"1\"\n [message]=\"'Drop ZIP file here or click to browse' | translate\"\n (dropped)=\"onZipDropped($event)\"\n ></c8y-drop-area>\n @if (dropError()) {\n <div\n class=\"alert alert-danger m-t-8\"\n role=\"alert\"\n >\n {{ dropError() }}\n </div>\n }\n </div>\n }\n\n @if (state() === 'previewing') {\n @if (toImportIcons().length) {\n <p\n class=\"m-b-8 text-bold\"\n translate\n >\n The following icons will be imported:\n </p>\n <ul class=\"list-unstyled m-b-16 separator-top\">\n @for (icon of toImportIcons(); track icon.name) {\n <li class=\"d-flex a-i-center gap-8 p-t-8 p-b-8 separator-bottom\">\n <span\n class=\"p-relative flex-no-shrink\"\n [style.width.px]=\"24\"\n [style.height.px]=\"24\"\n aria-hidden=\"true\"\n >\n <span\n class=\"d-flex a-i-center j-c-center fit-w fit-h\"\n [class.text-default]=\"!icon.previewHtmlSecondary\"\n [style.color]=\"\n icon.previewHtmlSecondary\n ? 'var(--c8y-root-component-icon-dark-color-dark)'\n : null\n \"\n [innerHTML]=\"icon.previewHtml\"\n ></span>\n @if (icon.previewHtmlSecondary) {\n <span\n class=\"d-flex a-i-center j-c-center fit-w fit-h p-absolute\"\n style=\"top: 0; left: 0; color: var(--c8y-root-component-icon-dark-color-light)\"\n [innerHTML]=\"icon.previewHtmlSecondary\"\n ></span>\n }\n </span>\n <span class=\"flex-grow min-width-0 text-truncate\">\n {{ icon.name }}\n @if (icon.category && !icon.isOverride) {\n <small class=\"text-muted m-l-4\">{{ icon.category | translate }}</small>\n }\n </span>\n <span class=\"tag tag--info flex-no-shrink\">\n {{ familyLabels[icon.family] | translate }}\n </span>\n @if (icon.isOverride) {\n <span\n class=\"tag tag--warning flex-no-shrink\"\n translate\n >Override</span\n >\n }\n </li>\n }\n </ul>\n } @else if (allSkippedExist()) {\n <div\n class=\"alert alert-warning\"\n role=\"status\"\n translate\n >\n All icons in this file already exist in this tenant.\n </div>\n } @else {\n <div\n class=\"alert alert-warning\"\n role=\"status\"\n translate\n >\n No icons to import.\n </div>\n }\n @if (skippedIcons().length && !(allSkippedExist() && toImportIcons().length === 0)) {\n <p\n class=\"m-b-8 text-bold\"\n translate\n >\n The following icons will be skipped:\n </p>\n <ul class=\"list-unstyled m-b-16 separator-top\">\n @for (icon of skippedIcons(); track icon.name) {\n <li class=\"d-flex a-i-center gap-8 p-t-8 p-b-8 separator-bottom\">\n <span\n class=\"p-relative flex-no-shrink\"\n [style.width.px]=\"24\"\n [style.height.px]=\"24\"\n aria-hidden=\"true\"\n >\n <span\n class=\"d-flex a-i-center j-c-center fit-w fit-h\"\n [class.text-default]=\"!icon.previewHtmlSecondary\"\n [style.color]=\"\n icon.previewHtmlSecondary\n ? 'var(--c8y-root-component-icon-dark-color-dark)'\n : null\n \"\n [innerHTML]=\"icon.previewHtml\"\n ></span>\n @if (icon.previewHtmlSecondary) {\n <span\n class=\"d-flex a-i-center j-c-center fit-w fit-h p-absolute\"\n style=\"top: 0; left: 0; color: var(--c8y-root-component-icon-dark-color-light)\"\n [innerHTML]=\"icon.previewHtmlSecondary\"\n ></span>\n }\n </span>\n <span class=\"flex-grow min-width-0 text-truncate\">\n {{ icon.name }}\n @if (icon.category && !icon.isOverride) {\n <small class=\"text-muted m-l-4\">{{ icon.category | translate }}</small>\n }\n </span>\n <span class=\"tag tag--warning flex-no-shrink\">\n {{ skipReasonLabels[icon.reason] | translate }}\n </span>\n </li>\n }\n </ul>\n }\n }\n\n @if (state() === 'importing') {\n <c8y-loading\n role=\"status\"\n [message]=\"'Importing icons\u2026' | translate\"\n ></c8y-loading>\n }\n </div>\n</c8y-modal>\n" }]
1129
1129
  }] });
1130
1130
 
1131
1131
  class ImportCustomIconsModalService {
@@ -1621,11 +1621,11 @@ class IconSelectorModalComponent {
1621
1621
  this.cdr.markForCheck();
1622
1622
  }
1623
1623
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: IconSelectorModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1624
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.19", type: IconSelectorModalComponent, isStandalone: true, selector: "c8y-icon-selector-modal", ngImport: i0, template: "<c8y-modal\n [title]=\"title | translate\"\n (onDismiss)=\"close()\"\n (onClose)=\"saveChanges()\"\n headerClasses=\"text-center\"\n [disabled]=\"!selectionExplicitlySet\"\n [labels]=\"{ cancel: 'Cancel', ok: saveButtonLabel | translate }\"\n>\n <c8y-icon-selector\n [selectedIcon]=\"currentSelection\"\n [iconCategoriesToExclude]=\"iconCategoriesToExclude\"\n [showIconClass]=\"showIconClass\"\n (onSelect)=\"selectionChange($event)\"\n ></c8y-icon-selector>\n</c8y-modal>\n", dependencies: [{ kind: "component", type: IconSelectorComponent, selector: "c8y-icon-selector", inputs: ["iconCategoriesToExclude", "showIconClass", "selectedIcon", "disableCustomIcons"], outputs: ["onSelect"] }, { kind: "component", type: ModalComponent, selector: "c8y-modal", inputs: ["disabled", "close", "dismiss", "title", "body", "customFooter", "headerClasses", "labels"], outputs: ["onDismiss", "onClose"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1624
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.19", type: IconSelectorModalComponent, isStandalone: true, selector: "c8y-icon-selector-modal", ngImport: i0, template: "<c8y-modal\n [title]=\"title | translate\"\n (onDismiss)=\"close()\"\n (onClose)=\"saveChanges()\"\n headerClasses=\"text-center\"\n [disabled]=\"!selectionExplicitlySet\"\n [labels]=\"{ cancel: 'Cancel', ok: saveButtonLabel | translate }\"\n [flushBody]=\"true\"\n>\n <c8y-icon-selector\n [selectedIcon]=\"currentSelection\"\n [iconCategoriesToExclude]=\"iconCategoriesToExclude\"\n [showIconClass]=\"showIconClass\"\n (onSelect)=\"selectionChange($event)\"\n ></c8y-icon-selector>\n</c8y-modal>\n", dependencies: [{ kind: "component", type: IconSelectorComponent, selector: "c8y-icon-selector", inputs: ["iconCategoriesToExclude", "showIconClass", "selectedIcon", "disableCustomIcons"], outputs: ["onSelect"] }, { kind: "component", type: ModalComponent, selector: "c8y-modal", inputs: ["disabled", "close", "dismiss", "title", "body", "customFooter", "headerClasses", "flushBody", "labels"], outputs: ["onDismiss", "onClose"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1625
1625
  }
1626
1626
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: IconSelectorModalComponent, decorators: [{
1627
1627
  type: Component,
1628
- args: [{ selector: 'c8y-icon-selector-modal', changeDetection: ChangeDetectionStrategy.OnPush, imports: [IconSelectorComponent, C8yTranslatePipe, ModalComponent], template: "<c8y-modal\n [title]=\"title | translate\"\n (onDismiss)=\"close()\"\n (onClose)=\"saveChanges()\"\n headerClasses=\"text-center\"\n [disabled]=\"!selectionExplicitlySet\"\n [labels]=\"{ cancel: 'Cancel', ok: saveButtonLabel | translate }\"\n>\n <c8y-icon-selector\n [selectedIcon]=\"currentSelection\"\n [iconCategoriesToExclude]=\"iconCategoriesToExclude\"\n [showIconClass]=\"showIconClass\"\n (onSelect)=\"selectionChange($event)\"\n ></c8y-icon-selector>\n</c8y-modal>\n" }]
1628
+ args: [{ selector: 'c8y-icon-selector-modal', changeDetection: ChangeDetectionStrategy.OnPush, imports: [IconSelectorComponent, C8yTranslatePipe, ModalComponent], template: "<c8y-modal\n [title]=\"title | translate\"\n (onDismiss)=\"close()\"\n (onClose)=\"saveChanges()\"\n headerClasses=\"text-center\"\n [disabled]=\"!selectionExplicitlySet\"\n [labels]=\"{ cancel: 'Cancel', ok: saveButtonLabel | translate }\"\n [flushBody]=\"true\"\n>\n <c8y-icon-selector\n [selectedIcon]=\"currentSelection\"\n [iconCategoriesToExclude]=\"iconCategoriesToExclude\"\n [showIconClass]=\"showIconClass\"\n (onSelect)=\"selectionChange($event)\"\n ></c8y-icon-selector>\n</c8y-modal>\n" }]
1629
1629
  }] });
1630
1630
 
1631
1631
  class IconSelectorService {