@c8y/ngx-components 1023.37.0 → 1023.43.2

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 (69) hide show
  1. package/ai/agent-chat/index.d.ts +1 -1
  2. package/ai/agent-chat/index.d.ts.map +1 -1
  3. package/ai/index.d.ts +1 -0
  4. package/ai/index.d.ts.map +1 -1
  5. package/context-dashboard/index.d.ts.map +1 -1
  6. package/fesm2022/c8y-ngx-components-ai-agent-chat.mjs +19 -2
  7. package/fesm2022/c8y-ngx-components-ai-agent-chat.mjs.map +1 -1
  8. package/fesm2022/c8y-ngx-components-ai-ai-chat.mjs +2 -2
  9. package/fesm2022/c8y-ngx-components-ai-ai-chat.mjs.map +1 -1
  10. package/fesm2022/c8y-ngx-components-ai.mjs.map +1 -1
  11. package/fesm2022/c8y-ngx-components-context-dashboard.mjs +2 -1
  12. package/fesm2022/c8y-ngx-components-context-dashboard.mjs.map +1 -1
  13. package/fesm2022/c8y-ngx-components-device-list.mjs +2 -2
  14. package/fesm2022/c8y-ngx-components-device-list.mjs.map +1 -1
  15. package/fesm2022/c8y-ngx-components-global-context.mjs +39 -11
  16. package/fesm2022/c8y-ngx-components-global-context.mjs.map +1 -1
  17. package/fesm2022/c8y-ngx-components-report-dashboard.mjs +3 -4
  18. package/fesm2022/c8y-ngx-components-report-dashboard.mjs.map +1 -1
  19. package/fesm2022/c8y-ngx-components-repository-configuration.mjs +2 -2
  20. package/fesm2022/c8y-ngx-components-repository-configuration.mjs.map +1 -1
  21. package/fesm2022/c8y-ngx-components-tenants.mjs +6 -7
  22. package/fesm2022/c8y-ngx-components-tenants.mjs.map +1 -1
  23. package/fesm2022/c8y-ngx-components-trusted-certificates.mjs +2 -2
  24. package/fesm2022/c8y-ngx-components-trusted-certificates.mjs.map +1 -1
  25. package/fesm2022/c8y-ngx-components-upgrade.mjs +13 -1
  26. package/fesm2022/c8y-ngx-components-upgrade.mjs.map +1 -1
  27. package/fesm2022/c8y-ngx-components-widgets-implementations-alarms.mjs +1 -1
  28. package/fesm2022/c8y-ngx-components-widgets-implementations-alarms.mjs.map +1 -1
  29. package/fesm2022/c8y-ngx-components-widgets-implementations-cockpit-legacy-welcome.mjs +2 -2
  30. package/fesm2022/c8y-ngx-components-widgets-implementations-cockpit-legacy-welcome.mjs.map +1 -1
  31. package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-graph.mjs +2 -2
  32. package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-graph.mjs.map +1 -1
  33. package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-table.mjs +4 -4
  34. package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-table.mjs.map +1 -1
  35. package/fesm2022/c8y-ngx-components-widgets-implementations-html-widget.mjs +5 -0
  36. package/fesm2022/c8y-ngx-components-widgets-implementations-html-widget.mjs.map +1 -1
  37. package/fesm2022/c8y-ngx-components-widgets-implementations-info-gauge.mjs +5 -7
  38. package/fesm2022/c8y-ngx-components-widgets-implementations-info-gauge.mjs.map +1 -1
  39. package/fesm2022/c8y-ngx-components-widgets-implementations-kpi.mjs +2 -2
  40. package/fesm2022/c8y-ngx-components-widgets-implementations-kpi.mjs.map +1 -1
  41. package/fesm2022/c8y-ngx-components-widgets-implementations-linear-gauge.mjs +2 -2
  42. package/fesm2022/c8y-ngx-components-widgets-implementations-linear-gauge.mjs.map +1 -1
  43. package/fesm2022/c8y-ngx-components-widgets-implementations-markdown.mjs +156 -67
  44. package/fesm2022/c8y-ngx-components-widgets-implementations-markdown.mjs.map +1 -1
  45. package/fesm2022/c8y-ngx-components.mjs +14 -6
  46. package/fesm2022/c8y-ngx-components.mjs.map +1 -1
  47. package/global-context/index.d.ts +11 -2
  48. package/global-context/index.d.ts.map +1 -1
  49. package/index.d.ts +5 -0
  50. package/index.d.ts.map +1 -1
  51. package/locales/de.po +271 -239
  52. package/locales/es.po +211 -199
  53. package/locales/fr.po +341 -309
  54. package/locales/ja_JP.po +382 -381
  55. package/locales/ko.po +206 -178
  56. package/locales/locales.pot +46 -79
  57. package/locales/nl.po +206 -179
  58. package/locales/pl.po +208 -178
  59. package/locales/pt_BR.po +208 -179
  60. package/locales/zh_CN.po +217 -192
  61. package/locales/zh_TW.po +214 -196
  62. package/package.json +1 -1
  63. package/report-dashboard/index.d.ts.map +1 -1
  64. package/tenants/index.d.ts.map +1 -1
  65. package/upgrade/index.d.ts.map +1 -1
  66. package/widgets/implementations/html-widget/index.d.ts.map +1 -1
  67. package/widgets/implementations/info-gauge/index.d.ts.map +1 -1
  68. package/widgets/implementations/markdown/index.d.ts +52 -18
  69. package/widgets/implementations/markdown/index.d.ts.map +1 -1
@@ -1,22 +1,33 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, Input, Component } from '@angular/core';
2
+ import { Injectable, inject, ViewChild, Input, Component } from '@angular/core';
3
3
  import { gettext } from '@c8y/ngx-components/gettext';
4
- import * as i2 from '@c8y/ngx-components';
5
- import { C8yValidators, CoreModule, MarkdownToHtmlPipe } from '@c8y/ngx-components';
6
- import * as i1 from '@angular/forms';
4
+ import * as i1 from '@c8y/ngx-components';
5
+ import { C8yValidators, FormGroupComponent, DropAreaComponent, EmptyStateComponent, IconDirective, C8yTranslatePipe, MarkdownToHtmlPipe } from '@c8y/ngx-components';
6
+ import * as i1$1 from '@angular/forms';
7
7
  import { Validators, ReactiveFormsModule } from '@angular/forms';
8
- import * as i2$1 from '@c8y/client';
8
+ import * as i2 from '@c8y/client';
9
9
  import * as i3 from '@ngx-translate/core';
10
+ import { WidgetConfigService } from '@c8y/ngx-components/context-dashboard';
10
11
  import { AsyncPipe } from '@angular/common';
12
+ import { Subject } from 'rxjs';
13
+ import { takeUntil } from 'rxjs/operators';
11
14
 
12
15
  class MarkdownWidgetService {
13
- constructor(fileService, inventory, binary, alert, translate) {
16
+ constructor(fileService, inventory, binary, alert, translate, fetchClient, appStateService) {
14
17
  this.fileService = fileService;
15
18
  this.inventory = inventory;
16
19
  this.binary = binary;
17
20
  this.alert = alert;
18
21
  this.translate = translate;
22
+ this.fetchClient = fetchClient;
23
+ this.appStateService = appStateService;
24
+ this.headers = { 'Content-Type': 'text/markdown' };
19
25
  }
26
+ /**
27
+ * Retrieves a markdown file from the inventory by its binary ID.
28
+ * @param markdownBinaryId - The ID of the binary managed object.
29
+ * @returns The file if found, otherwise null.
30
+ */
20
31
  async getFile(markdownBinaryId) {
21
32
  if (!markdownBinaryId) {
22
33
  return null;
@@ -32,26 +43,86 @@ class MarkdownWidgetService {
32
43
  }
33
44
  return null;
34
45
  }
46
+ /**
47
+ * Uploads a markdown file to the inventory as a binary.
48
+ * @param file - The file to upload.
49
+ * @returns The ID of the created binary managed object.
50
+ */
35
51
  async uploadFile(file) {
36
52
  const { data: mo } = await this.binary.create(file);
37
53
  return mo.id;
38
54
  }
39
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MarkdownWidgetService, deps: [{ token: i2.FilesService }, { token: i2$1.InventoryService }, { token: i2$1.InventoryBinaryService }, { token: i2.AlertService }, { token: i3.TranslateService }], target: i0.ɵɵFactoryTarget.Injectable }); }
55
+ /**
56
+ * Fetches markdown content from a URL.
57
+ * For internal URLs (e.g., `/readme.md`), uses FetchClient with the app's context path.
58
+ * For external URLs, uses XMLHttpRequest to avoid CORS issues with auth headers.
59
+ * @param url - The URL to fetch content from.
60
+ * @returns The markdown content as a string, or empty string on error.
61
+ */
62
+ async getContentFromUrl(url) {
63
+ if (url?.toLowerCase() === '/readme.md') {
64
+ return this.getContentFromInternalUrl(url);
65
+ }
66
+ return this.getContentFromExternalUrl(url);
67
+ }
68
+ async getContentFromInternalUrl(url) {
69
+ try {
70
+ const contextPath = this.appStateService.state.app.contextPath;
71
+ const options = {
72
+ method: 'GET',
73
+ headers: this.headers
74
+ };
75
+ const response = await this.fetchClient.fetch(`/apps/${contextPath}${url}`, options);
76
+ if (response.status === 200) {
77
+ return response.text();
78
+ }
79
+ }
80
+ catch {
81
+ // ignore error, return empty string
82
+ }
83
+ return '';
84
+ }
85
+ getContentFromExternalUrl(url) {
86
+ return new Promise(resolve => {
87
+ const req = new XMLHttpRequest();
88
+ req.onreadystatechange = () => {
89
+ if (req.readyState === 4) {
90
+ if (req.status === 200) {
91
+ resolve(req.response);
92
+ }
93
+ else {
94
+ resolve('');
95
+ }
96
+ }
97
+ };
98
+ req.onerror = () => resolve('');
99
+ req.open('GET', url);
100
+ req.responseType = 'text';
101
+ req.setRequestHeader('Accept', 'text/html');
102
+ req.send();
103
+ });
104
+ }
105
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MarkdownWidgetService, deps: [{ token: i1.FilesService }, { token: i2.InventoryService }, { token: i2.InventoryBinaryService }, { token: i1.AlertService }, { token: i3.TranslateService }, { token: i2.FetchClient }, { token: i1.AppStateService }], target: i0.ɵɵFactoryTarget.Injectable }); }
40
106
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MarkdownWidgetService, providedIn: 'root' }); }
41
107
  }
42
108
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MarkdownWidgetService, decorators: [{
43
109
  type: Injectable,
44
110
  args: [{ providedIn: 'root' }]
45
- }], ctorParameters: () => [{ type: i2.FilesService }, { type: i2$1.InventoryService }, { type: i2$1.InventoryBinaryService }, { type: i2.AlertService }, { type: i3.TranslateService }] });
111
+ }], ctorParameters: () => [{ type: i1.FilesService }, { type: i2.InventoryService }, { type: i2.InventoryBinaryService }, { type: i1.AlertService }, { type: i3.TranslateService }, { type: i2.FetchClient }, { type: i1.AppStateService }] });
46
112
 
47
113
  class MarkdownWidgetConfigComponent {
114
+ set markdownPreviewTemplate(template) {
115
+ this.widgetConfigService.setPreview(template || null);
116
+ }
48
117
  constructor(formBuilder, form, alert, markdownService) {
49
118
  this.formBuilder = formBuilder;
50
119
  this.form = form;
51
120
  this.alert = alert;
52
121
  this.markdownService = markdownService;
53
122
  this.uploadChoice = 'uploadUrl';
54
- this.loading = false;
123
+ this.previewMarkdown = '';
124
+ this.widgetConfigService = inject(WidgetConfigService);
125
+ this.destroy$ = new Subject();
55
126
  }
56
127
  async onBeforeSave(config) {
57
128
  if (this.formGroup.invalid) {
@@ -89,11 +160,56 @@ class MarkdownWidgetConfigComponent {
89
160
  this.formGroup.patchValue({
90
161
  droppedFile: [{ file: this.fileFromConfig, name: this.fileFromConfig.name }]
91
162
  });
163
+ await this.updatePreviewFromFile(this.fileFromConfig);
164
+ }
165
+ else if (this.config.contentUrl) {
166
+ this.previewMarkdown = await this.markdownService.getContentFromUrl(this.config.contentUrl);
92
167
  }
168
+ this.formGroup.valueChanges.pipe(takeUntil(this.destroy$)).subscribe(async (value) => {
169
+ await this.updatePreview(value);
170
+ });
171
+ }
172
+ ngOnDestroy() {
173
+ this.destroy$.next();
174
+ this.destroy$.complete();
93
175
  }
94
176
  onChange(value) {
95
177
  this.uploadChoice = value;
96
- this.formGroup.controls['uploadChoice'].patchValue(value);
178
+ // Ensure dropped file has 'name' property for drop area to display filename
179
+ const droppedFile = this.formGroup.value.droppedFile;
180
+ if (value === 'uploadBinary' && droppedFile?.[0]?.file) {
181
+ const normalizedFile = droppedFile.map(item => ({
182
+ ...item,
183
+ name: item.name || item.file?.name
184
+ }));
185
+ this.formGroup.controls['droppedFile'].setValue(normalizedFile);
186
+ }
187
+ }
188
+ async updatePreview(formValue) {
189
+ const choice = formValue.uploadChoice || this.uploadChoice;
190
+ if (choice === 'uploadBinary') {
191
+ const file = this.getFileFromFormValue(formValue);
192
+ if (file) {
193
+ await this.updatePreviewFromFile(file);
194
+ }
195
+ else {
196
+ this.previewMarkdown = '';
197
+ }
198
+ }
199
+ else if (choice === 'uploadUrl' && formValue.contentUrl) {
200
+ this.previewMarkdown = await this.markdownService.getContentFromUrl(formValue.contentUrl);
201
+ }
202
+ else {
203
+ this.previewMarkdown = '';
204
+ }
205
+ }
206
+ async updatePreviewFromFile(file) {
207
+ try {
208
+ this.previewMarkdown = await file.text();
209
+ }
210
+ catch {
211
+ this.previewMarkdown = '';
212
+ }
97
213
  }
98
214
  getFileFromFormValue(formValue) {
99
215
  const binary = formValue?.droppedFile || [];
@@ -101,16 +217,13 @@ class MarkdownWidgetConfigComponent {
101
217
  }
102
218
  initForm() {
103
219
  this.formGroup = this.formBuilder.group({
104
- contentUrl: ['', [Validators.maxLength(2000)]],
105
- droppedFile: [
106
- null,
107
- [
108
- Validators.minLength(1),
109
- Validators.maxLength(1),
110
- C8yValidators.filesValidator({ maximumFileSizeInKb: 1000 })
111
- ]
112
- ],
113
- uploadChoice: [this.config.markdownBinaryId ? 'uploadBinary' : 'uploadUrl', []]
220
+ contentUrl: this.formBuilder.nonNullable.control('', [Validators.maxLength(2000)]),
221
+ droppedFile: this.formBuilder.control(null, [
222
+ Validators.minLength(1),
223
+ Validators.maxLength(1),
224
+ C8yValidators.filesValidator({ maximumFileSizeInKb: 1000 })
225
+ ]),
226
+ uploadChoice: this.formBuilder.nonNullable.control(this.config.markdownBinaryId ? 'uploadBinary' : 'uploadUrl')
114
227
  }, { validators: this.requireEitherBinaryOrUrl() });
115
228
  this.form.form.addControl('config', this.formGroup);
116
229
  this.formGroup.patchValue(this.config);
@@ -161,72 +274,48 @@ class MarkdownWidgetConfigComponent {
161
274
  }
162
275
  return removedError;
163
276
  }
164
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MarkdownWidgetConfigComponent, deps: [{ token: i1.FormBuilder }, { token: i1.NgForm }, { token: i2.AlertService }, { token: MarkdownWidgetService }], target: i0.ɵɵFactoryTarget.Component }); }
165
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: MarkdownWidgetConfigComponent, isStandalone: true, selector: "c8y-markdown-widget-config", inputs: { config: "config" }, ngImport: i0, template: "<fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Source' | translate }}</legend>\n <form [formGroup]=\"formGroup\">\n <div class=\"form-group\">\n <label\n class=\"c8y-radio radio-inline\"\n title=\"{{ 'Upload a binary' | translate }}\"\n >\n <input\n name=\"uploadChoice\"\n type=\"radio\"\n value=\"uploadBinary\"\n #radio\n formControlName=\"uploadChoice\"\n (change)=\"onChange($event.target.value)\"\n />\n <span></span>\n <span>{{ 'Upload a binary' | translate }}</span>\n </label>\n <label\n class=\"c8y-radio radio-inline m-l-8\"\n title=\"{{ 'Provide a file path' | translate }}\"\n >\n <input\n name=\"uploadChoice\"\n type=\"radio\"\n value=\"uploadUrl\"\n #radio\n formControlName=\"uploadChoice\"\n (change)=\"onChange($event.target.value)\"\n />\n <span></span>\n <span>\n {{ 'Provide a file path' | translate }}\n </span>\n </label>\n </div>\n @switch (uploadChoice) {\n @case ('uploadBinary') {\n <c8y-form-group class=\"m-b-24\">\n <c8y-drop-area\n class=\"drop-area-sm\"\n [title]=\"'Drop file or click to browse' | translate\"\n formControlName=\"droppedFile\"\n [maxAllowedFiles]=\"1\"\n [accept]=\"'md'\"\n ></c8y-drop-area>\n </c8y-form-group>\n }\n @case ('uploadUrl') {\n <c8y-form-group class=\"m-b-24\">\n <div class=\"input-group\">\n <span class=\"input-group-addon\">\n <i c8yIcon=\"globe\"></i>\n </span>\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g.' | translate }} http://example.com/binary.zip\"\n type=\"text\"\n formControlName=\"contentUrl\"\n />\n </div>\n </c8y-form-group>\n }\n }\n </form>\n</fieldset>\n", dependencies: [{ kind: "ngmodule", type: CoreModule }, { kind: "directive", type: i2.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "component", type: i2.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: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { 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.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "component", type: i2.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i2.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { 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: "ngmodule", type: ReactiveFormsModule }, { kind: "pipe", type: i2.C8yTranslatePipe, name: "translate" }] }); }
277
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MarkdownWidgetConfigComponent, deps: [{ token: i1$1.FormBuilder }, { token: i1$1.NgForm }, { token: i1.AlertService }, { token: MarkdownWidgetService }], target: i0.ɵɵFactoryTarget.Component }); }
278
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: MarkdownWidgetConfigComponent, isStandalone: true, selector: "c8y-markdown-widget-config", inputs: { config: "config" }, viewQueries: [{ propertyName: "markdownPreviewTemplate", first: true, predicate: ["markdownPreview"], descendants: true }], ngImport: i0, template: "<fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Source' | translate }}</legend>\n <form [formGroup]=\"formGroup\">\n <div class=\"form-group\">\n <label\n class=\"c8y-radio radio-inline\"\n title=\"{{ 'Upload a binary' | translate }}\"\n >\n <input\n name=\"uploadChoice\"\n type=\"radio\"\n value=\"uploadBinary\"\n #radio\n formControlName=\"uploadChoice\"\n (change)=\"onChange($event.target.value)\"\n />\n <span></span>\n <span>{{ 'Upload a binary' | translate }}</span>\n </label>\n <label\n class=\"c8y-radio radio-inline m-l-8\"\n title=\"{{ 'Provide a file path' | translate }}\"\n >\n <input\n name=\"uploadChoice\"\n type=\"radio\"\n value=\"uploadUrl\"\n #radio\n formControlName=\"uploadChoice\"\n (change)=\"onChange($event.target.value)\"\n />\n <span></span>\n <span>\n {{ 'Provide a file path' | translate }}\n </span>\n </label>\n </div>\n @switch (uploadChoice) {\n @case ('uploadBinary') {\n <c8y-form-group class=\"m-b-24\">\n <c8y-drop-area\n class=\"drop-area-sm\"\n [title]=\"'Drop file or click to browse' | translate\"\n formControlName=\"droppedFile\"\n [maxAllowedFiles]=\"1\"\n [accept]=\"'md'\"\n ></c8y-drop-area>\n </c8y-form-group>\n }\n @case ('uploadUrl') {\n <c8y-form-group class=\"m-b-24\">\n <div class=\"input-group\">\n <span class=\"input-group-addon\">\n <i c8yIcon=\"globe\"></i>\n </span>\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g.' | translate }} http://example.com/binary.zip\"\n type=\"text\"\n formControlName=\"contentUrl\"\n />\n </div>\n </c8y-form-group>\n }\n }\n </form>\n</fieldset>\n\n<ng-template #markdownPreview>\n @if (previewMarkdown) {\n <div\n class=\"p-16 p-t-0 markdown-content fit-h overflow-auto\"\n [innerHTML]=\"previewMarkdown | markdownToHtml | async\"\n ></div>\n } @else {\n <div class=\"fit-h d-flex d-col j-c-center a-i-center\">\n <c8y-ui-empty-state\n [icon]=\"'file-text'\"\n [title]=\"'No content to preview' | translate\"\n [subtitle]=\"'Upload a file or provide a URL to see the preview' | translate\"\n [horizontal]=\"false\"\n ></c8y-ui-empty-state>\n </div>\n }\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.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$1.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { 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: EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: MarkdownToHtmlPipe, name: "markdownToHtml" }] }); }
166
279
  }
167
280
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MarkdownWidgetConfigComponent, decorators: [{
168
281
  type: Component,
169
- args: [{ selector: 'c8y-markdown-widget-config', standalone: true, imports: [CoreModule, ReactiveFormsModule], template: "<fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Source' | translate }}</legend>\n <form [formGroup]=\"formGroup\">\n <div class=\"form-group\">\n <label\n class=\"c8y-radio radio-inline\"\n title=\"{{ 'Upload a binary' | translate }}\"\n >\n <input\n name=\"uploadChoice\"\n type=\"radio\"\n value=\"uploadBinary\"\n #radio\n formControlName=\"uploadChoice\"\n (change)=\"onChange($event.target.value)\"\n />\n <span></span>\n <span>{{ 'Upload a binary' | translate }}</span>\n </label>\n <label\n class=\"c8y-radio radio-inline m-l-8\"\n title=\"{{ 'Provide a file path' | translate }}\"\n >\n <input\n name=\"uploadChoice\"\n type=\"radio\"\n value=\"uploadUrl\"\n #radio\n formControlName=\"uploadChoice\"\n (change)=\"onChange($event.target.value)\"\n />\n <span></span>\n <span>\n {{ 'Provide a file path' | translate }}\n </span>\n </label>\n </div>\n @switch (uploadChoice) {\n @case ('uploadBinary') {\n <c8y-form-group class=\"m-b-24\">\n <c8y-drop-area\n class=\"drop-area-sm\"\n [title]=\"'Drop file or click to browse' | translate\"\n formControlName=\"droppedFile\"\n [maxAllowedFiles]=\"1\"\n [accept]=\"'md'\"\n ></c8y-drop-area>\n </c8y-form-group>\n }\n @case ('uploadUrl') {\n <c8y-form-group class=\"m-b-24\">\n <div class=\"input-group\">\n <span class=\"input-group-addon\">\n <i c8yIcon=\"globe\"></i>\n </span>\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g.' | translate }} http://example.com/binary.zip\"\n type=\"text\"\n formControlName=\"contentUrl\"\n />\n </div>\n </c8y-form-group>\n }\n }\n </form>\n</fieldset>\n" }]
170
- }], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i1.NgForm }, { type: i2.AlertService }, { type: MarkdownWidgetService }], propDecorators: { config: [{
282
+ args: [{ selector: 'c8y-markdown-widget-config', standalone: true, imports: [
283
+ ReactiveFormsModule,
284
+ AsyncPipe,
285
+ C8yTranslatePipe,
286
+ MarkdownToHtmlPipe,
287
+ FormGroupComponent,
288
+ DropAreaComponent,
289
+ EmptyStateComponent,
290
+ IconDirective
291
+ ], template: "<fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Source' | translate }}</legend>\n <form [formGroup]=\"formGroup\">\n <div class=\"form-group\">\n <label\n class=\"c8y-radio radio-inline\"\n title=\"{{ 'Upload a binary' | translate }}\"\n >\n <input\n name=\"uploadChoice\"\n type=\"radio\"\n value=\"uploadBinary\"\n #radio\n formControlName=\"uploadChoice\"\n (change)=\"onChange($event.target.value)\"\n />\n <span></span>\n <span>{{ 'Upload a binary' | translate }}</span>\n </label>\n <label\n class=\"c8y-radio radio-inline m-l-8\"\n title=\"{{ 'Provide a file path' | translate }}\"\n >\n <input\n name=\"uploadChoice\"\n type=\"radio\"\n value=\"uploadUrl\"\n #radio\n formControlName=\"uploadChoice\"\n (change)=\"onChange($event.target.value)\"\n />\n <span></span>\n <span>\n {{ 'Provide a file path' | translate }}\n </span>\n </label>\n </div>\n @switch (uploadChoice) {\n @case ('uploadBinary') {\n <c8y-form-group class=\"m-b-24\">\n <c8y-drop-area\n class=\"drop-area-sm\"\n [title]=\"'Drop file or click to browse' | translate\"\n formControlName=\"droppedFile\"\n [maxAllowedFiles]=\"1\"\n [accept]=\"'md'\"\n ></c8y-drop-area>\n </c8y-form-group>\n }\n @case ('uploadUrl') {\n <c8y-form-group class=\"m-b-24\">\n <div class=\"input-group\">\n <span class=\"input-group-addon\">\n <i c8yIcon=\"globe\"></i>\n </span>\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g.' | translate }} http://example.com/binary.zip\"\n type=\"text\"\n formControlName=\"contentUrl\"\n />\n </div>\n </c8y-form-group>\n }\n }\n </form>\n</fieldset>\n\n<ng-template #markdownPreview>\n @if (previewMarkdown) {\n <div\n class=\"p-16 p-t-0 markdown-content fit-h overflow-auto\"\n [innerHTML]=\"previewMarkdown | markdownToHtml | async\"\n ></div>\n } @else {\n <div class=\"fit-h d-flex d-col j-c-center a-i-center\">\n <c8y-ui-empty-state\n [icon]=\"'file-text'\"\n [title]=\"'No content to preview' | translate\"\n [subtitle]=\"'Upload a file or provide a URL to see the preview' | translate\"\n [horizontal]=\"false\"\n ></c8y-ui-empty-state>\n </div>\n }\n</ng-template>\n" }]
292
+ }], ctorParameters: () => [{ type: i1$1.FormBuilder }, { type: i1$1.NgForm }, { type: i1.AlertService }, { type: MarkdownWidgetService }], propDecorators: { config: [{
171
293
  type: Input
294
+ }], markdownPreviewTemplate: [{
295
+ type: ViewChild,
296
+ args: ['markdownPreview']
172
297
  }] } });
173
298
 
174
299
  class MarkdownWidgetViewComponent {
175
- constructor(appState, client, markdownWidgetService) {
176
- this.appState = appState;
177
- this.client = client;
300
+ constructor(markdownWidgetService) {
178
301
  this.markdownWidgetService = markdownWidgetService;
179
- this.headers = { 'Content-Type': 'text/markdown', responseType: 'blob' };
180
302
  }
181
303
  async ngOnInit() {
182
- this.contextPath = this.appState.state.app.contextPath;
183
304
  if (this.config.markdownBinaryId) {
184
- const readmeContent = await (await this.markdownWidgetService.getFile(this.config.markdownBinaryId)).text();
185
- this.markdown = readmeContent;
305
+ const file = await this.markdownWidgetService.getFile(this.config.markdownBinaryId);
306
+ this.markdown = await file?.text();
186
307
  }
187
- else if (this.config.contentUrl?.toLowerCase() === '/readme.md') {
188
- this.markdown = await this.getReadmeFileContent();
189
- }
190
- else {
191
- this.setContentFromUrl(this.config.contentUrl);
308
+ else if (this.config.contentUrl) {
309
+ this.markdown = await this.markdownWidgetService.getContentFromUrl(this.config.contentUrl);
192
310
  }
193
311
  }
194
- setContentFromUrl(url) {
195
- const req = new XMLHttpRequest();
196
- req.onreadystatechange = () => this.render(req);
197
- req.addEventListener('load', () => this.render(req));
198
- req.open('GET', url);
199
- req.responseType = 'text';
200
- req.setRequestHeader('Accept', 'text/html');
201
- req.send();
202
- }
203
- async render(req) {
204
- if (req.readyState === 4 && req.status === 200) {
205
- this.markdown = req.response;
206
- }
207
- }
208
- async getReadmeFileContent() {
209
- const readmeFile = await this.getReadmeFile();
210
- if (readmeFile.status === 200) {
211
- return await readmeFile.text();
212
- }
213
- return '';
214
- }
215
- async getReadmeFile() {
216
- const options = {
217
- method: 'GET',
218
- headers: this.headers
219
- };
220
- const result = await this.client.fetch(`/apps/${this.contextPath}${this.config.contentUrl}`, options);
221
- return result;
222
- }
223
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MarkdownWidgetViewComponent, deps: [{ token: i2.AppStateService }, { token: i2$1.FetchClient }, { token: MarkdownWidgetService }], target: i0.ɵɵFactoryTarget.Component }); }
312
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MarkdownWidgetViewComponent, deps: [{ token: MarkdownWidgetService }], target: i0.ɵɵFactoryTarget.Component }); }
224
313
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: MarkdownWidgetViewComponent, isStandalone: true, selector: "c8y-markdown-widget-view", inputs: { config: "config" }, ngImport: i0, template: "<div id=\"helpContent\" class=\"p-16 p-t-0 markdown-content\" [innerHTML]=\"markdown | markdownToHtml | async\"></div>\n", dependencies: [{ kind: "pipe", type: MarkdownToHtmlPipe, name: "markdownToHtml" }, { kind: "pipe", type: AsyncPipe, name: "async" }] }); }
225
314
  }
226
315
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MarkdownWidgetViewComponent, decorators: [{
227
316
  type: Component,
228
317
  args: [{ selector: 'c8y-markdown-widget-view', standalone: true, imports: [MarkdownToHtmlPipe, AsyncPipe], template: "<div id=\"helpContent\" class=\"p-16 p-t-0 markdown-content\" [innerHTML]=\"markdown | markdownToHtml | async\"></div>\n" }]
229
- }], ctorParameters: () => [{ type: i2.AppStateService }, { type: i2$1.FetchClient }, { type: MarkdownWidgetService }], propDecorators: { config: [{
318
+ }], ctorParameters: () => [{ type: MarkdownWidgetService }], propDecorators: { config: [{
230
319
  type: Input
231
320
  }] } });
232
321
 
@@ -1 +1 @@
1
- {"version":3,"file":"c8y-ngx-components-widgets-implementations-markdown.mjs","sources":["../../widgets/implementations/markdown/markdown-widget.service.ts","../../widgets/implementations/markdown/markdown-widget-config/markdown-widget-config.component.ts","../../widgets/implementations/markdown/markdown-widget-config/markdown-widget-config.component.html","../../widgets/implementations/markdown/markdown-widget-view/markdown-widget-view.component.ts","../../widgets/implementations/markdown/markdown-widget-view/markdown-widget-view.component.html","../../widgets/implementations/markdown/c8y-ngx-components-widgets-implementations-markdown.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\nimport { IManagedObjectBinary, InventoryBinaryService, InventoryService } from '@c8y/client';\nimport { gettext } from '@c8y/ngx-components/gettext';\nimport { FilesService, AlertService } from '@c8y/ngx-components';\nimport { TranslateService } from '@ngx-translate/core';\n\n@Injectable({ providedIn: 'root' })\nexport class MarkdownWidgetService {\n constructor(\n private fileService: FilesService,\n private inventory: InventoryService,\n private binary: InventoryBinaryService,\n private alert: AlertService,\n private translate: TranslateService\n ) {}\n\n async getFile(markdownBinaryId: string | null): Promise<File> {\n if (!markdownBinaryId) {\n return null;\n }\n\n try {\n const { data: markdownBinaryMo } = await this.inventory.detail(markdownBinaryId);\n const file = await this.fileService.getFile(markdownBinaryMo as IManagedObjectBinary);\n return file;\n } catch (e) {\n const text = this.translate.instant(\n gettext('Unable to retrieve binary with ID: {{ markdownBinaryId }}'),\n { markdownBinaryId }\n );\n this.alert.danger(text, e?.data);\n }\n\n return null;\n }\n\n async uploadFile(file: File): Promise<string> {\n const { data: mo } = await this.binary.create(file);\n return mo.id;\n }\n}\n","import { Component, Input, OnInit } from '@angular/core';\nimport { gettext } from '@c8y/ngx-components/gettext';\nimport { AlertService, C8yValidators, CoreModule, OnBeforeSave } from '@c8y/ngx-components';\nimport {\n AbstractControl,\n FormBuilder,\n FormGroup,\n NgForm,\n ReactiveFormsModule,\n ValidationErrors,\n ValidatorFn,\n Validators\n} from '@angular/forms';\nimport { MarkdownWidgetConfig } from '../markdown-widget.model';\nimport { MarkdownWidgetService } from '../markdown-widget.service';\n\n@Component({\n selector: 'c8y-markdown-widget-config',\n templateUrl: './markdown-widget-config.component.html',\n standalone: true,\n imports: [CoreModule, ReactiveFormsModule]\n})\nexport class MarkdownWidgetConfigComponent implements OnInit, OnBeforeSave {\n @Input() config: MarkdownWidgetConfig;\n formGroup: FormGroup;\n fileFromConfig: File;\n uploadChoice: 'uploadBinary' | 'uploadUrl' = 'uploadUrl';\n loading = false;\n\n constructor(\n private formBuilder: FormBuilder,\n private form: NgForm,\n private alert: AlertService,\n private markdownService: MarkdownWidgetService\n ) {}\n\n async onBeforeSave(config?: MarkdownWidgetConfig): Promise<boolean> {\n if (this.formGroup.invalid) {\n return false;\n }\n if (this.uploadChoice === 'uploadUrl') {\n Object.assign(config, {\n contentUrl: this.formGroup.value.contentUrl,\n markdownBinaryId: null\n });\n return true;\n }\n const fileFromForm = this.getFileFromFormValue(this.formGroup.value);\n if (fileFromForm && fileFromForm !== this.fileFromConfig) {\n try {\n const markdownBinaryId = await this.markdownService.uploadFile(fileFromForm);\n Object.assign(config, { markdownBinaryId, contentUrl: null });\n return true;\n } catch (e) {\n this.alert.danger(gettext('Unable to upload Markdown file.'), e?.data);\n return false;\n }\n }\n if (!fileFromForm) {\n Object.assign(config, { contentUrl: '/readme.md', markdownBinaryId: null });\n }\n return true;\n }\n\n async ngOnInit() {\n this.initForm();\n if (this.config.markdownBinaryId) {\n this.uploadChoice = 'uploadBinary';\n this.fileFromConfig = await this.markdownService.getFile(this.config.markdownBinaryId);\n this.formGroup.patchValue({\n droppedFile: [{ file: this.fileFromConfig, name: this.fileFromConfig.name }]\n });\n }\n }\n\n onChange(value: 'uploadBinary' | 'uploadUrl') {\n this.uploadChoice = value;\n this.formGroup.controls['uploadChoice'].patchValue(value);\n }\n\n private getFileFromFormValue(formValue: any): File | null {\n const binary: any[] = formValue?.droppedFile || [];\n return binary[0]?.file || null;\n }\n\n private initForm(): void {\n this.formGroup = this.formBuilder.group(\n {\n contentUrl: ['', [Validators.maxLength(2000)]],\n droppedFile: [\n null,\n [\n Validators.minLength(1),\n Validators.maxLength(1),\n C8yValidators.filesValidator({ maximumFileSizeInKb: 1000 })\n ]\n ],\n uploadChoice: [this.config.markdownBinaryId ? 'uploadBinary' : 'uploadUrl', []]\n },\n { validators: this.requireEitherBinaryOrUrl() }\n );\n this.form.form.addControl('config', this.formGroup);\n this.formGroup.patchValue(this.config);\n }\n\n private requireEitherBinaryOrUrl(): ValidatorFn {\n return (control: AbstractControl): ValidationErrors | null => {\n const url = control.get(`contentUrl`);\n const uploadBinary = control.get(`droppedFile`);\n\n const urlDefined = url && url.value !== undefined && url.value !== null;\n const uploadBinaryDefined =\n uploadBinary && uploadBinary.value !== undefined && uploadBinary.value !== null;\n\n const errors = {};\n if (this.uploadChoice === 'uploadBinary' && !uploadBinaryDefined) {\n // sets error\n const error = { required: true };\n uploadBinary.setErrors(Object.assign({}, uploadBinary.errors || {}, error));\n Object.assign(errors, error);\n } else {\n // remove previous error\n this.removeErrors(uploadBinary, ['required']);\n }\n\n if (this.uploadChoice === 'uploadUrl' && (!urlDefined || url.value === '')) {\n // sets error\n const error = { required: true };\n url.setErrors(Object.assign({}, url.errors || {}, error));\n Object.assign(errors, error);\n } else {\n // remove previous error\n this.removeErrors(url, ['required']);\n }\n\n return Object.keys(errors).length ? errors : null;\n };\n }\n\n private removeErrors(control: AbstractControl, errors: string[]): boolean {\n if (!control || !control.errors) {\n return false;\n }\n let removedError = false;\n for (const error of errors) {\n if (control.errors[error]) {\n removedError = true;\n delete control.errors[error];\n }\n }\n if (removedError) {\n control.setErrors(\n Object.keys(control.errors).length ? Object.assign({}, control.errors) : null\n );\n }\n return removedError;\n }\n}\n","<fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Source' | translate }}</legend>\n <form [formGroup]=\"formGroup\">\n <div class=\"form-group\">\n <label\n class=\"c8y-radio radio-inline\"\n title=\"{{ 'Upload a binary' | translate }}\"\n >\n <input\n name=\"uploadChoice\"\n type=\"radio\"\n value=\"uploadBinary\"\n #radio\n formControlName=\"uploadChoice\"\n (change)=\"onChange($event.target.value)\"\n />\n <span></span>\n <span>{{ 'Upload a binary' | translate }}</span>\n </label>\n <label\n class=\"c8y-radio radio-inline m-l-8\"\n title=\"{{ 'Provide a file path' | translate }}\"\n >\n <input\n name=\"uploadChoice\"\n type=\"radio\"\n value=\"uploadUrl\"\n #radio\n formControlName=\"uploadChoice\"\n (change)=\"onChange($event.target.value)\"\n />\n <span></span>\n <span>\n {{ 'Provide a file path' | translate }}\n </span>\n </label>\n </div>\n @switch (uploadChoice) {\n @case ('uploadBinary') {\n <c8y-form-group class=\"m-b-24\">\n <c8y-drop-area\n class=\"drop-area-sm\"\n [title]=\"'Drop file or click to browse' | translate\"\n formControlName=\"droppedFile\"\n [maxAllowedFiles]=\"1\"\n [accept]=\"'md'\"\n ></c8y-drop-area>\n </c8y-form-group>\n }\n @case ('uploadUrl') {\n <c8y-form-group class=\"m-b-24\">\n <div class=\"input-group\">\n <span class=\"input-group-addon\">\n <i c8yIcon=\"globe\"></i>\n </span>\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g.' | translate }} http://example.com/binary.zip\"\n type=\"text\"\n formControlName=\"contentUrl\"\n />\n </div>\n </c8y-form-group>\n }\n }\n </form>\n</fieldset>\n","import { Component, Input, OnInit } from '@angular/core';\nimport { IFetchOptions, FetchClient, IFetchResponse } from '@c8y/client';\nimport { AppStateService, MarkdownToHtmlPipe } from '@c8y/ngx-components';\nimport { MarkdownWidgetConfig } from '../markdown-widget.model';\nimport { MarkdownWidgetService } from '../markdown-widget.service';\nimport { AsyncPipe } from '@angular/common';\n\n@Component({\n selector: 'c8y-markdown-widget-view',\n templateUrl: './markdown-widget-view.component.html',\n standalone: true,\n imports: [MarkdownToHtmlPipe, AsyncPipe]\n})\nexport class MarkdownWidgetViewComponent implements OnInit {\n @Input() config: MarkdownWidgetConfig;\n loading: boolean;\n markdown: string;\n contextPath: string;\n private readonly headers: any = { 'Content-Type': 'text/markdown', responseType: 'blob' };\n\n constructor(\n private appState: AppStateService,\n private client: FetchClient,\n private markdownWidgetService: MarkdownWidgetService\n ) {}\n\n async ngOnInit() {\n this.contextPath = this.appState.state.app.contextPath;\n if (this.config.markdownBinaryId) {\n const readmeContent = await (\n await this.markdownWidgetService.getFile(this.config.markdownBinaryId)\n ).text();\n this.markdown = readmeContent;\n } else if (this.config.contentUrl?.toLowerCase() === '/readme.md') {\n this.markdown = await this.getReadmeFileContent();\n } else {\n this.setContentFromUrl(this.config.contentUrl);\n }\n }\n\n setContentFromUrl(url: string) {\n const req = new XMLHttpRequest();\n\n req.onreadystatechange = () => this.render(req);\n req.addEventListener('load', () => this.render(req));\n req.open('GET', url);\n req.responseType = 'text';\n req.setRequestHeader('Accept', 'text/html');\n req.send();\n }\n\n private async render(req: XMLHttpRequest) {\n if (req.readyState === 4 && req.status === 200) {\n this.markdown = req.response;\n }\n }\n\n private async getReadmeFileContent(): Promise<string> {\n const readmeFile = await this.getReadmeFile();\n\n if (readmeFile.status === 200) {\n return await readmeFile.text();\n }\n return '';\n }\n\n private async getReadmeFile(): Promise<IFetchResponse> {\n const options: IFetchOptions = {\n method: 'GET',\n headers: this.headers\n };\n const result: IFetchResponse = await this.client.fetch(\n `/apps/${this.contextPath}${this.config.contentUrl}`,\n options\n );\n return result;\n }\n}\n","<div id=\"helpContent\" class=\"p-16 p-t-0 markdown-content\" [innerHTML]=\"markdown | markdownToHtml | async\"></div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":["i1","i2","i3.MarkdownWidgetService"],"mappings":"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"}
1
+ {"version":3,"file":"c8y-ngx-components-widgets-implementations-markdown.mjs","sources":["../../widgets/implementations/markdown/markdown-widget.service.ts","../../widgets/implementations/markdown/markdown-widget-config/markdown-widget-config.component.ts","../../widgets/implementations/markdown/markdown-widget-config/markdown-widget-config.component.html","../../widgets/implementations/markdown/markdown-widget-view/markdown-widget-view.component.ts","../../widgets/implementations/markdown/markdown-widget-view/markdown-widget-view.component.html","../../widgets/implementations/markdown/c8y-ngx-components-widgets-implementations-markdown.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\nimport {\n FetchClient,\n IFetchOptions,\n IManagedObjectBinary,\n InventoryBinaryService,\n InventoryService\n} from '@c8y/client';\nimport { AlertService, AppStateService, FilesService } from '@c8y/ngx-components';\nimport { gettext } from '@c8y/ngx-components/gettext';\nimport { TranslateService } from '@ngx-translate/core';\n\n@Injectable({ providedIn: 'root' })\nexport class MarkdownWidgetService {\n private readonly headers = { 'Content-Type': 'text/markdown' };\n\n constructor(\n private fileService: FilesService,\n private inventory: InventoryService,\n private binary: InventoryBinaryService,\n private alert: AlertService,\n private translate: TranslateService,\n private fetchClient: FetchClient,\n private appStateService: AppStateService\n ) {}\n\n /**\n * Retrieves a markdown file from the inventory by its binary ID.\n * @param markdownBinaryId - The ID of the binary managed object.\n * @returns The file if found, otherwise null.\n */\n async getFile(markdownBinaryId: string | null): Promise<File> {\n if (!markdownBinaryId) {\n return null;\n }\n\n try {\n const { data: markdownBinaryMo } = await this.inventory.detail(markdownBinaryId);\n const file = await this.fileService.getFile(markdownBinaryMo as IManagedObjectBinary);\n return file;\n } catch (e) {\n const text = this.translate.instant(\n gettext('Unable to retrieve binary with ID: {{ markdownBinaryId }}'),\n { markdownBinaryId }\n );\n this.alert.danger(text, e?.data);\n }\n\n return null;\n }\n\n /**\n * Uploads a markdown file to the inventory as a binary.\n * @param file - The file to upload.\n * @returns The ID of the created binary managed object.\n */\n async uploadFile(file: File): Promise<string> {\n const { data: mo } = await this.binary.create(file);\n return mo.id;\n }\n\n /**\n * Fetches markdown content from a URL.\n * For internal URLs (e.g., `/readme.md`), uses FetchClient with the app's context path.\n * For external URLs, uses XMLHttpRequest to avoid CORS issues with auth headers.\n * @param url - The URL to fetch content from.\n * @returns The markdown content as a string, or empty string on error.\n */\n async getContentFromUrl(url: string): Promise<string> {\n if (url?.toLowerCase() === '/readme.md') {\n return this.getContentFromInternalUrl(url);\n }\n return this.getContentFromExternalUrl(url);\n }\n\n private async getContentFromInternalUrl(url: string): Promise<string> {\n try {\n const contextPath = this.appStateService.state.app.contextPath;\n const options: IFetchOptions = {\n method: 'GET',\n headers: this.headers\n };\n const response = await this.fetchClient.fetch(`/apps/${contextPath}${url}`, options);\n if (response.status === 200) {\n return response.text();\n }\n } catch {\n // ignore error, return empty string\n }\n return '';\n }\n\n private getContentFromExternalUrl(url: string): Promise<string> {\n return new Promise(resolve => {\n const req = new XMLHttpRequest();\n req.onreadystatechange = () => {\n if (req.readyState === 4) {\n if (req.status === 200) {\n resolve(req.response);\n } else {\n resolve('');\n }\n }\n };\n req.onerror = () => resolve('');\n req.open('GET', url);\n req.responseType = 'text';\n req.setRequestHeader('Accept', 'text/html');\n req.send();\n });\n }\n}\n","import { Component, inject, Input, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core';\nimport { gettext } from '@c8y/ngx-components/gettext';\nimport {\n AlertService,\n C8yTranslatePipe,\n C8yValidators,\n DropAreaComponent,\n EmptyStateComponent,\n FormGroupComponent,\n IconDirective,\n MarkdownToHtmlPipe,\n OnBeforeSave\n} from '@c8y/ngx-components';\nimport {\n AbstractControl,\n FormBuilder,\n NgForm,\n ReactiveFormsModule,\n ValidationErrors,\n ValidatorFn,\n Validators\n} from '@angular/forms';\nimport {\n DroppedFileItem,\n MarkdownWidgetConfig,\n MarkdownWidgetFormGroup,\n MarkdownWidgetFormValue\n} from '../markdown-widget.model';\nimport { MarkdownWidgetService } from '../markdown-widget.service';\nimport { WidgetConfigService } from '@c8y/ngx-components/context-dashboard';\nimport { AsyncPipe } from '@angular/common';\nimport { Subject } from 'rxjs';\nimport { takeUntil } from 'rxjs/operators';\n\n@Component({\n selector: 'c8y-markdown-widget-config',\n templateUrl: './markdown-widget-config.component.html',\n standalone: true,\n imports: [\n ReactiveFormsModule,\n AsyncPipe,\n C8yTranslatePipe,\n MarkdownToHtmlPipe,\n FormGroupComponent,\n DropAreaComponent,\n EmptyStateComponent,\n IconDirective\n ]\n})\nexport class MarkdownWidgetConfigComponent implements OnInit, OnDestroy, OnBeforeSave {\n @Input() config: MarkdownWidgetConfig;\n @ViewChild('markdownPreview')\n set markdownPreviewTemplate(template: TemplateRef<any>) {\n this.widgetConfigService.setPreview(template || null);\n }\n\n formGroup: MarkdownWidgetFormGroup;\n fileFromConfig: File;\n uploadChoice: 'uploadBinary' | 'uploadUrl' = 'uploadUrl';\n previewMarkdown = '';\n\n private widgetConfigService = inject(WidgetConfigService);\n private destroy$ = new Subject<void>();\n\n constructor(\n private formBuilder: FormBuilder,\n private form: NgForm,\n private alert: AlertService,\n private markdownService: MarkdownWidgetService\n ) {}\n\n async onBeforeSave(config?: MarkdownWidgetConfig): Promise<boolean> {\n if (this.formGroup.invalid) {\n return false;\n }\n if (this.uploadChoice === 'uploadUrl') {\n Object.assign(config, {\n contentUrl: this.formGroup.value.contentUrl,\n markdownBinaryId: null\n });\n return true;\n }\n const fileFromForm = this.getFileFromFormValue(this.formGroup.value);\n if (fileFromForm && fileFromForm !== this.fileFromConfig) {\n try {\n const markdownBinaryId = await this.markdownService.uploadFile(fileFromForm);\n Object.assign(config, { markdownBinaryId, contentUrl: null });\n return true;\n } catch (e) {\n this.alert.danger(gettext('Unable to upload Markdown file.'), e?.data);\n return false;\n }\n }\n if (!fileFromForm) {\n Object.assign(config, { contentUrl: '/readme.md', markdownBinaryId: null });\n }\n return true;\n }\n\n async ngOnInit() {\n this.initForm();\n if (this.config.markdownBinaryId) {\n this.uploadChoice = 'uploadBinary';\n this.fileFromConfig = await this.markdownService.getFile(this.config.markdownBinaryId);\n this.formGroup.patchValue({\n droppedFile: [{ file: this.fileFromConfig, name: this.fileFromConfig.name }]\n });\n await this.updatePreviewFromFile(this.fileFromConfig);\n } else if (this.config.contentUrl) {\n this.previewMarkdown = await this.markdownService.getContentFromUrl(this.config.contentUrl);\n }\n\n this.formGroup.valueChanges.pipe(takeUntil(this.destroy$)).subscribe(async value => {\n await this.updatePreview(value);\n });\n }\n\n ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n onChange(value: 'uploadBinary' | 'uploadUrl') {\n this.uploadChoice = value;\n\n // Ensure dropped file has 'name' property for drop area to display filename\n const droppedFile = this.formGroup.value.droppedFile;\n if (value === 'uploadBinary' && droppedFile?.[0]?.file) {\n const normalizedFile = droppedFile.map(item => ({\n ...item,\n name: item.name || item.file?.name\n }));\n this.formGroup.controls['droppedFile'].setValue(normalizedFile);\n }\n }\n\n private async updatePreview(formValue: Partial<MarkdownWidgetFormValue>): Promise<void> {\n const choice = formValue.uploadChoice || this.uploadChoice;\n if (choice === 'uploadBinary') {\n const file = this.getFileFromFormValue(formValue);\n if (file) {\n await this.updatePreviewFromFile(file);\n } else {\n this.previewMarkdown = '';\n }\n } else if (choice === 'uploadUrl' && formValue.contentUrl) {\n this.previewMarkdown = await this.markdownService.getContentFromUrl(formValue.contentUrl);\n } else {\n this.previewMarkdown = '';\n }\n }\n\n private async updatePreviewFromFile(file: File): Promise<void> {\n try {\n this.previewMarkdown = await file.text();\n } catch {\n this.previewMarkdown = '';\n }\n }\n\n private getFileFromFormValue(formValue: Partial<MarkdownWidgetFormValue>): File | null {\n const binary = formValue?.droppedFile || [];\n return binary[0]?.file || null;\n }\n\n private initForm(): void {\n this.formGroup = this.formBuilder.group(\n {\n contentUrl: this.formBuilder.nonNullable.control('', [Validators.maxLength(2000)]),\n droppedFile: this.formBuilder.control<DroppedFileItem[] | null>(null, [\n Validators.minLength(1),\n Validators.maxLength(1),\n C8yValidators.filesValidator({ maximumFileSizeInKb: 1000 })\n ]),\n uploadChoice: this.formBuilder.nonNullable.control<'uploadBinary' | 'uploadUrl'>(\n this.config.markdownBinaryId ? 'uploadBinary' : 'uploadUrl'\n )\n },\n { validators: this.requireEitherBinaryOrUrl() }\n ) as MarkdownWidgetFormGroup;\n this.form.form.addControl('config', this.formGroup);\n this.formGroup.patchValue(this.config);\n }\n\n private requireEitherBinaryOrUrl(): ValidatorFn {\n return (control: AbstractControl): ValidationErrors | null => {\n const url = control.get(`contentUrl`);\n const uploadBinary = control.get(`droppedFile`);\n\n const urlDefined = url && url.value !== undefined && url.value !== null;\n const uploadBinaryDefined =\n uploadBinary && uploadBinary.value !== undefined && uploadBinary.value !== null;\n\n const errors = {};\n if (this.uploadChoice === 'uploadBinary' && !uploadBinaryDefined) {\n // sets error\n const error = { required: true };\n uploadBinary.setErrors(Object.assign({}, uploadBinary.errors || {}, error));\n Object.assign(errors, error);\n } else {\n // remove previous error\n this.removeErrors(uploadBinary, ['required']);\n }\n\n if (this.uploadChoice === 'uploadUrl' && (!urlDefined || url.value === '')) {\n // sets error\n const error = { required: true };\n url.setErrors(Object.assign({}, url.errors || {}, error));\n Object.assign(errors, error);\n } else {\n // remove previous error\n this.removeErrors(url, ['required']);\n }\n\n return Object.keys(errors).length ? errors : null;\n };\n }\n\n private removeErrors(control: AbstractControl, errors: string[]): boolean {\n if (!control || !control.errors) {\n return false;\n }\n let removedError = false;\n for (const error of errors) {\n if (control.errors[error]) {\n removedError = true;\n delete control.errors[error];\n }\n }\n if (removedError) {\n control.setErrors(\n Object.keys(control.errors).length ? Object.assign({}, control.errors) : null\n );\n }\n return removedError;\n }\n}\n","<fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Source' | translate }}</legend>\n <form [formGroup]=\"formGroup\">\n <div class=\"form-group\">\n <label\n class=\"c8y-radio radio-inline\"\n title=\"{{ 'Upload a binary' | translate }}\"\n >\n <input\n name=\"uploadChoice\"\n type=\"radio\"\n value=\"uploadBinary\"\n #radio\n formControlName=\"uploadChoice\"\n (change)=\"onChange($event.target.value)\"\n />\n <span></span>\n <span>{{ 'Upload a binary' | translate }}</span>\n </label>\n <label\n class=\"c8y-radio radio-inline m-l-8\"\n title=\"{{ 'Provide a file path' | translate }}\"\n >\n <input\n name=\"uploadChoice\"\n type=\"radio\"\n value=\"uploadUrl\"\n #radio\n formControlName=\"uploadChoice\"\n (change)=\"onChange($event.target.value)\"\n />\n <span></span>\n <span>\n {{ 'Provide a file path' | translate }}\n </span>\n </label>\n </div>\n @switch (uploadChoice) {\n @case ('uploadBinary') {\n <c8y-form-group class=\"m-b-24\">\n <c8y-drop-area\n class=\"drop-area-sm\"\n [title]=\"'Drop file or click to browse' | translate\"\n formControlName=\"droppedFile\"\n [maxAllowedFiles]=\"1\"\n [accept]=\"'md'\"\n ></c8y-drop-area>\n </c8y-form-group>\n }\n @case ('uploadUrl') {\n <c8y-form-group class=\"m-b-24\">\n <div class=\"input-group\">\n <span class=\"input-group-addon\">\n <i c8yIcon=\"globe\"></i>\n </span>\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g.' | translate }} http://example.com/binary.zip\"\n type=\"text\"\n formControlName=\"contentUrl\"\n />\n </div>\n </c8y-form-group>\n }\n }\n </form>\n</fieldset>\n\n<ng-template #markdownPreview>\n @if (previewMarkdown) {\n <div\n class=\"p-16 p-t-0 markdown-content fit-h overflow-auto\"\n [innerHTML]=\"previewMarkdown | markdownToHtml | async\"\n ></div>\n } @else {\n <div class=\"fit-h d-flex d-col j-c-center a-i-center\">\n <c8y-ui-empty-state\n [icon]=\"'file-text'\"\n [title]=\"'No content to preview' | translate\"\n [subtitle]=\"'Upload a file or provide a URL to see the preview' | translate\"\n [horizontal]=\"false\"\n ></c8y-ui-empty-state>\n </div>\n }\n</ng-template>\n","import { Component, Input, OnInit } from '@angular/core';\nimport { MarkdownToHtmlPipe } from '@c8y/ngx-components';\nimport { MarkdownWidgetConfig } from '../markdown-widget.model';\nimport { MarkdownWidgetService } from '../markdown-widget.service';\nimport { AsyncPipe } from '@angular/common';\n\n@Component({\n selector: 'c8y-markdown-widget-view',\n templateUrl: './markdown-widget-view.component.html',\n standalone: true,\n imports: [MarkdownToHtmlPipe, AsyncPipe]\n})\nexport class MarkdownWidgetViewComponent implements OnInit {\n @Input() config: MarkdownWidgetConfig;\n markdown: string;\n\n constructor(private markdownWidgetService: MarkdownWidgetService) {}\n\n async ngOnInit() {\n if (this.config.markdownBinaryId) {\n const file = await this.markdownWidgetService.getFile(this.config.markdownBinaryId);\n this.markdown = await file?.text();\n } else if (this.config.contentUrl) {\n this.markdown = await this.markdownWidgetService.getContentFromUrl(this.config.contentUrl);\n }\n }\n}\n","<div id=\"helpContent\" class=\"p-16 p-t-0 markdown-content\" [innerHTML]=\"markdown | markdownToHtml | async\"></div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":["i1","i2","i3.MarkdownWidgetService","i1.MarkdownWidgetService"],"mappings":"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"}