@spike-rabbit/dashboards-ng 49.0.0 → 49.13.0
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.
- package/LICENSE.md +1 -1
- package/README.md +190 -44
- package/fesm2022/spike-rabbit-dashboards-ng-module-federation.mjs +15 -22
- package/fesm2022/spike-rabbit-dashboards-ng-module-federation.mjs.map +1 -1
- package/fesm2022/spike-rabbit-dashboards-ng-native-federation-mf-bridge.mjs +31 -0
- package/fesm2022/spike-rabbit-dashboards-ng-native-federation-mf-bridge.mjs.map +1 -0
- package/fesm2022/spike-rabbit-dashboards-ng-native-federation.mjs +30 -0
- package/fesm2022/spike-rabbit-dashboards-ng-native-federation.mjs.map +1 -0
- package/fesm2022/spike-rabbit-dashboards-ng-translate.mjs +1 -1
- package/fesm2022/spike-rabbit-dashboards-ng-translate.mjs.map +1 -1
- package/fesm2022/spike-rabbit-dashboards-ng.mjs +936 -712
- package/fesm2022/spike-rabbit-dashboards-ng.mjs.map +1 -1
- package/module-federation/package.json +2 -1
- package/native-federation/mf-bridge/package.json +4 -0
- package/native-federation/package.json +4 -0
- package/package.json +40 -16
- package/translate/package.json +2 -1
- package/types/spike-rabbit-dashboards-ng-native-federation-mf-bridge.d.ts +9 -0
- package/types/spike-rabbit-dashboards-ng-native-federation.d.ts +3 -0
- package/{translate/index.d.ts → types/spike-rabbit-dashboards-ng-translate.d.ts} +8 -1
- package/{index.d.ts → types/spike-rabbit-dashboards-ng.d.ts} +337 -189
- package/template-i18n.json +0 -35
- /package/{module-federation/index.d.ts → types/spike-rabbit-dashboards-ng-module-federation.d.ts} +0 -0
|
@@ -1,26 +1,31 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { NgTemplateOutlet, isPlatformBrowser, AsyncPipe } from '@angular/common';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
|
-
import { InjectionToken, inject, input, model, output, computed,
|
|
3
|
+
import { InjectionToken, inject, input, model, output, viewChild, computed, ChangeDetectionStrategy, Component, Injectable, ElementRef, Renderer2, DOCUMENT, Directive, EventEmitter, createNgModule, Injector, EnvironmentInjector, DestroyRef, signal, ViewContainerRef, isSignal, viewChildren, NgZone, PLATFORM_ID, afterNextRender, inputBinding, outputBinding, NgModule } from '@angular/core';
|
|
4
4
|
import { SiDashboardCardComponent, SiDashboardComponent } from '@spike-rabbit/element-ng/dashboard';
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { takeUntil, take, map } from 'rxjs/operators';
|
|
5
|
+
import { t, SiTranslatePipe, injectSiTranslateService } from '@spike-rabbit/element-translate-ng/translate';
|
|
6
|
+
import { BehaviorSubject, of, Subject, throwError, ReplaySubject, first, combineLatest } from 'rxjs';
|
|
7
|
+
import { take, takeUntil } from 'rxjs/operators';
|
|
9
8
|
import { BOOTSTRAP_BREAKPOINTS, SiResponsiveContainerDirective } from '@spike-rabbit/element-ng/resize-observer';
|
|
10
9
|
import { ActivatedRoute, RouterLink } from '@angular/router';
|
|
10
|
+
import { elementEdit } from '@siemens/element-icons';
|
|
11
11
|
import { SiContentActionBarComponent } from '@spike-rabbit/element-ng/content-action-bar';
|
|
12
|
+
import { addIcons, SiIconComponent } from '@spike-rabbit/element-ng/icon';
|
|
12
13
|
import { SiLinkDirective } from '@spike-rabbit/element-ng/link';
|
|
13
|
-
import {
|
|
14
|
+
import { SiLoadingButtonComponent, SiLoadingSpinnerDirective } from '@spike-rabbit/element-ng/loading-spinner';
|
|
15
|
+
import { SiTooltipDirective } from '@spike-rabbit/element-ng/tooltip';
|
|
14
16
|
import { SiModalService } from '@spike-rabbit/element-ng/modal';
|
|
15
17
|
import { GridStack } from 'gridstack';
|
|
18
|
+
import { LiveAnnouncer } from '@angular/cdk/a11y';
|
|
19
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
16
20
|
import { SiActionDialogService } from '@spike-rabbit/element-ng/action-modal';
|
|
21
|
+
import { CdkListbox, CdkOption } from '@angular/cdk/listbox';
|
|
17
22
|
import { SiCircleStatusComponent } from '@spike-rabbit/element-ng/circle-status';
|
|
18
23
|
import { SiEmptyStateComponent } from '@spike-rabbit/element-ng/empty-state';
|
|
19
24
|
import { SiSearchBarComponent } from '@spike-rabbit/element-ng/search-bar';
|
|
20
25
|
export * from '@spike-rabbit/dashboards-ng/translate';
|
|
21
26
|
|
|
22
27
|
/**
|
|
23
|
-
* Copyright (c) Siemens 2016 -
|
|
28
|
+
* Copyright (c) Siemens 2016 - 2026
|
|
24
29
|
* SPDX-License-Identifier: MIT
|
|
25
30
|
*/
|
|
26
31
|
/**
|
|
@@ -36,7 +41,7 @@ const DEFAULT_GRIDSTACK_OPTIONS = {
|
|
|
36
41
|
};
|
|
37
42
|
|
|
38
43
|
/**
|
|
39
|
-
* Copyright (c) Siemens 2016 -
|
|
44
|
+
* Copyright (c) Siemens 2016 - 2026
|
|
40
45
|
* SPDX-License-Identifier: MIT
|
|
41
46
|
*/
|
|
42
47
|
/**
|
|
@@ -48,12 +53,21 @@ const SI_DASHBOARD_CONFIGURATION = new InjectionToken('Injection token to config
|
|
|
48
53
|
factory: () => ({ grid: { gridStackOptions: DEFAULT_GRIDSTACK_OPTIONS } })
|
|
49
54
|
});
|
|
50
55
|
/**
|
|
51
|
-
*
|
|
56
|
+
* Injection token to configure global toolbar menu items.
|
|
52
57
|
*/
|
|
53
|
-
const
|
|
58
|
+
const SI_DASHBOARD_TOOLBAR_ITEMS = new InjectionToken('Injection token to configure global toolbar menu items.');
|
|
59
|
+
/**
|
|
60
|
+
* provider function to configure global toolbar menu items.
|
|
61
|
+
* @param toolbarItems - primary and secondary edit actions which are common to all
|
|
62
|
+
* dashboard instances on application.
|
|
63
|
+
*/
|
|
64
|
+
const provideDashboardToolbarItems = (toolbarItems) => ({
|
|
65
|
+
provide: SI_DASHBOARD_TOOLBAR_ITEMS,
|
|
66
|
+
useValue: { primary: toolbarItems?.primary ?? [], secondary: toolbarItems?.secondary ?? [] }
|
|
67
|
+
});
|
|
54
68
|
|
|
55
69
|
/**
|
|
56
|
-
* Copyright (c) Siemens 2016 -
|
|
70
|
+
* Copyright (c) Siemens 2016 - 2026
|
|
57
71
|
* SPDX-License-Identifier: MIT
|
|
58
72
|
*/
|
|
59
73
|
/**
|
|
@@ -71,6 +85,9 @@ class SiWidgetStorage {
|
|
|
71
85
|
/**
|
|
72
86
|
* Optional method to provide primary and secondary toolbar menu items.
|
|
73
87
|
* @param dashboardId - The id of the dashboard if present to allow dashboard specific menu items.
|
|
88
|
+
*
|
|
89
|
+
* @deprecated use {@link provideDashboardToolbarItems} to provide common toolbar items.
|
|
90
|
+
* Additionally configure individual dashboard actions via {@link SiFlexibleDashboardComponent.primaryEditActions} and {@link SiFlexibleDashboardComponent.secondaryEditActions} respectively.
|
|
74
91
|
*/
|
|
75
92
|
getToolbarMenuItems;
|
|
76
93
|
}
|
|
@@ -116,26 +133,19 @@ class SiDefaultWidgetStorage extends SiWidgetStorage {
|
|
|
116
133
|
}
|
|
117
134
|
return this.map.get(dashboardId);
|
|
118
135
|
}
|
|
119
|
-
save(
|
|
120
|
-
const newWidgets =
|
|
121
|
-
if (widget.id === undefined) {
|
|
122
|
-
return { ...widget, id: Math.random().toString(36).substring(2, 9) };
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
return widget;
|
|
126
|
-
}
|
|
127
|
-
});
|
|
136
|
+
save(modifiedWidgets, addedWidgets, removedWidgets, dashboardId) {
|
|
137
|
+
const newWidgets = [...addedWidgets, ...modifiedWidgets];
|
|
128
138
|
this.update(newWidgets, dashboardId);
|
|
129
139
|
return of(newWidgets);
|
|
130
140
|
}
|
|
131
|
-
update(
|
|
141
|
+
update(widgetConfigs, dashboardId) {
|
|
132
142
|
const widgets$ = this.load(dashboardId);
|
|
133
|
-
widgets$.next(
|
|
143
|
+
widgets$.next(widgetConfigs);
|
|
134
144
|
if (!dashboardId) {
|
|
135
|
-
this.storage.setItem(STORAGE_KEY, JSON.stringify(
|
|
145
|
+
this.storage.setItem(STORAGE_KEY, JSON.stringify(widgetConfigs));
|
|
136
146
|
}
|
|
137
147
|
else {
|
|
138
|
-
this.storage.setItem(`${STORAGE_KEY}-${dashboardId}`, JSON.stringify(
|
|
148
|
+
this.storage.setItem(`${STORAGE_KEY}-${dashboardId}`, JSON.stringify(widgetConfigs));
|
|
139
149
|
}
|
|
140
150
|
}
|
|
141
151
|
loadFromStorage(dashboardId) {
|
|
@@ -155,7 +165,7 @@ class SiDefaultWidgetStorage extends SiWidgetStorage {
|
|
|
155
165
|
}
|
|
156
166
|
|
|
157
167
|
/**
|
|
158
|
-
* Copyright (c) Siemens 2016 -
|
|
168
|
+
* Copyright (c) Siemens 2016 - 2026
|
|
159
169
|
* SPDX-License-Identifier: MIT
|
|
160
170
|
*/
|
|
161
171
|
/**
|
|
@@ -166,69 +176,84 @@ class SiDefaultWidgetStorage extends SiWidgetStorage {
|
|
|
166
176
|
* the cancel and save buttons, as well as displaying primary and secondary actions.
|
|
167
177
|
*/
|
|
168
178
|
class SiDashboardToolbarComponent {
|
|
179
|
+
icons = addIcons({ elementEdit });
|
|
169
180
|
/**
|
|
170
181
|
* Set primary actions that are in `editable` mode first visible or in
|
|
171
182
|
* the expanded content action bar of the toolbar.
|
|
172
183
|
*
|
|
173
184
|
* @defaultValue []
|
|
174
185
|
*/
|
|
175
|
-
primaryEditActions = input([]
|
|
186
|
+
primaryEditActions = input([], /* @ts-ignore */
|
|
187
|
+
...(ngDevMode ? [{ debugName: "primaryEditActions" }] : /* istanbul ignore next */ []));
|
|
176
188
|
/**
|
|
177
189
|
* Set secondary actions that are in `editable` mode second visible or in
|
|
178
190
|
* the dropdown part of the content action bar of the toolbar.
|
|
179
191
|
*
|
|
180
192
|
* @defaultValue [] */
|
|
181
|
-
secondaryEditActions = input([]
|
|
193
|
+
secondaryEditActions = input([], /* @ts-ignore */
|
|
194
|
+
...(ngDevMode ? [{ debugName: "secondaryEditActions" }] : /* istanbul ignore next */ []));
|
|
182
195
|
/**
|
|
183
196
|
* Input to disable the save button. Note, the input `disabled` disables all
|
|
184
197
|
* actions and the save button of the toolbar.
|
|
185
198
|
*
|
|
186
199
|
* @defaultValue true
|
|
187
200
|
*/
|
|
188
|
-
disableSaveButton = model(true
|
|
201
|
+
disableSaveButton = model(true, /* @ts-ignore */
|
|
202
|
+
...(ngDevMode ? [{ debugName: "disableSaveButton" }] : /* istanbul ignore next */ []));
|
|
189
203
|
/**
|
|
190
204
|
* Input to disable all actions and buttons of the toolbar.
|
|
191
205
|
*
|
|
192
206
|
* @defaultValue false */
|
|
193
|
-
disabled = input(false
|
|
207
|
+
disabled = input(false, /* @ts-ignore */
|
|
208
|
+
...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
194
209
|
/**
|
|
195
210
|
* Input option to set the `editable` mode. When editable
|
|
196
211
|
* the toolbar shows a cancel and save button. Otherwise,
|
|
197
212
|
* it displays an edit button.
|
|
198
213
|
*
|
|
199
214
|
* @defaultValue false */
|
|
200
|
-
editable = model(false
|
|
215
|
+
editable = model(false, /* @ts-ignore */
|
|
216
|
+
...(ngDevMode ? [{ debugName: "editable" }] : /* istanbul ignore next */ []));
|
|
201
217
|
/**
|
|
202
218
|
* Option to hide the dashboard edit button.
|
|
203
219
|
*
|
|
204
220
|
* @defaultValue false
|
|
205
221
|
*/
|
|
206
|
-
hideEditButton = input(false
|
|
222
|
+
hideEditButton = input(false, /* @ts-ignore */
|
|
223
|
+
...(ngDevMode ? [{ debugName: "hideEditButton" }] : /* istanbul ignore next */ []));
|
|
207
224
|
/**
|
|
208
225
|
* Option to display the edit button as a text button instead, only if the window is larger than xs {@link SiResponsiveContainerDirective}.
|
|
209
226
|
*
|
|
210
227
|
* @defaultValue false
|
|
211
228
|
*/
|
|
212
|
-
showEditButtonLabel = input(false
|
|
229
|
+
showEditButtonLabel = input(false, /* @ts-ignore */
|
|
230
|
+
...(ngDevMode ? [{ debugName: "showEditButtonLabel" }] : /* istanbul ignore next */ []));
|
|
231
|
+
grid = input.required(/* @ts-ignore */
|
|
232
|
+
...(ngDevMode ? [{ debugName: "grid" }] : /* istanbul ignore next */ []));
|
|
213
233
|
/**
|
|
214
234
|
* Emits on save button click.
|
|
215
235
|
*/
|
|
216
236
|
save = output();
|
|
217
|
-
/**
|
|
218
|
-
* Emits on cancel button click.
|
|
219
|
-
*/
|
|
220
|
-
// eslint-disable-next-line @angular-eslint/no-output-native
|
|
221
|
-
cancel = output();
|
|
222
237
|
labelEdit = t(() => $localize `:@@DASHBOARD.EDIT:Edit`);
|
|
223
238
|
labelCancel = t(() => $localize `:@@DASHBOARD.CANCEL:Cancel`);
|
|
224
239
|
labelSave = t(() => $localize `:@@DASHBOARD.SAVE:Save`);
|
|
225
240
|
activatedRoute = inject(ActivatedRoute, { optional: true });
|
|
226
|
-
dashboardToolbarContainer;
|
|
227
|
-
showContentActionBar = computed(() => this.primaryEditActions()?.length + this.secondaryEditActions()?.length > 3
|
|
241
|
+
dashboardToolbarContainer = viewChild.required(SiResponsiveContainerDirective);
|
|
242
|
+
showContentActionBar = computed(() => this.primaryEditActions()?.length + this.secondaryEditActions()?.length > 3, /* @ts-ignore */
|
|
243
|
+
...(ngDevMode ? [{ debugName: "showContentActionBar" }] : /* istanbul ignore next */ []));
|
|
228
244
|
editActions = computed(() => [
|
|
229
245
|
...this.primaryEditActions(),
|
|
230
246
|
...this.secondaryEditActions()
|
|
231
|
-
]
|
|
247
|
+
], /* @ts-ignore */
|
|
248
|
+
...(ngDevMode ? [{ debugName: "editActions" }] : /* istanbul ignore next */ []));
|
|
249
|
+
primaryEditActionsComputed = computed(() => {
|
|
250
|
+
return this.primaryEditActions().map(item => this.proxyMenuItemAction(item));
|
|
251
|
+
}, /* @ts-ignore */
|
|
252
|
+
...(ngDevMode ? [{ debugName: "primaryEditActionsComputed" }] : /* istanbul ignore next */ []));
|
|
253
|
+
secondaryEditActionsComputed = computed(() => {
|
|
254
|
+
return this.secondaryEditActions().map(item => this.proxyMenuItemAction(item));
|
|
255
|
+
}, /* @ts-ignore */
|
|
256
|
+
...(ngDevMode ? [{ debugName: "secondaryEditActionsComputed" }] : /* istanbul ignore next */ []));
|
|
232
257
|
onEdit() {
|
|
233
258
|
this.editable.set(true);
|
|
234
259
|
}
|
|
@@ -239,113 +264,137 @@ class SiDashboardToolbarComponent {
|
|
|
239
264
|
}
|
|
240
265
|
onCancel() {
|
|
241
266
|
if (this.editable()) {
|
|
242
|
-
this.
|
|
267
|
+
this.editable.set(false);
|
|
243
268
|
}
|
|
244
269
|
}
|
|
245
270
|
isToolbarItem(item) {
|
|
246
271
|
return 'label' in item;
|
|
247
272
|
}
|
|
248
|
-
showEditButtonLabelDesktop() {
|
|
249
|
-
return this.showEditButtonLabel() && !this.dashboardToolbarContainer?.xs();
|
|
273
|
+
showEditButtonLabelDesktop = computed(() => {
|
|
274
|
+
return this.showEditButtonLabel() && !this.dashboardToolbarContainer()?.xs();
|
|
275
|
+
}, /* @ts-ignore */
|
|
276
|
+
...(ngDevMode ? [{ debugName: "showEditButtonLabelDesktop" }] : /* istanbul ignore next */ []));
|
|
277
|
+
proxyMenuItemAction(item) {
|
|
278
|
+
if (item.type === 'action' && typeof item.action === 'function') {
|
|
279
|
+
item.action = item.action.bind(this, this.grid());
|
|
280
|
+
}
|
|
281
|
+
return item;
|
|
250
282
|
}
|
|
251
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
252
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
283
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SiDashboardToolbarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
284
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: SiDashboardToolbarComponent, isStandalone: true, selector: "si-dashboard-toolbar", inputs: { primaryEditActions: { classPropertyName: "primaryEditActions", publicName: "primaryEditActions", isSignal: true, isRequired: false, transformFunction: null }, secondaryEditActions: { classPropertyName: "secondaryEditActions", publicName: "secondaryEditActions", isSignal: true, isRequired: false, transformFunction: null }, disableSaveButton: { classPropertyName: "disableSaveButton", publicName: "disableSaveButton", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, hideEditButton: { classPropertyName: "hideEditButton", publicName: "hideEditButton", isSignal: true, isRequired: false, transformFunction: null }, showEditButtonLabel: { classPropertyName: "showEditButtonLabel", publicName: "showEditButtonLabel", isSignal: true, isRequired: false, transformFunction: null }, grid: { classPropertyName: "grid", publicName: "grid", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { disableSaveButton: "disableSaveButtonChange", editable: "editableChange", save: "save" }, host: { classAttribute: "d-flex flex-column flex-grow-1" }, viewQueries: [{ propertyName: "dashboardToolbarContainer", first: true, predicate: SiResponsiveContainerDirective, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"row mb-3\" siResponsiveContainer>\n <div class=\"col-12 d-flex justify-content-between\">\n @if (!editable()) {\n <ng-content select=\"[filters-slot]\" />\n @if (!hideEditButton()) {\n @let showEditButtonLabelDesktop = this.showEditButtonLabelDesktop();\n <button\n type=\"button\"\n class=\"btn ms-auto\"\n [class]=\"showEditButtonLabelDesktop ? 'btn-secondary' : 'btn-icon btn-tertiary'\"\n [attr.aria-label]=\"labelEdit | translate\"\n [siTooltip]=\"labelEdit\"\n [isDisabled]=\"showEditButtonLabelDesktop\"\n [disabled]=\"disabled()\"\n (click)=\"onEdit()\"\n >\n @if (showEditButtonLabelDesktop) {\n {{ labelEdit | translate }}\n } @else {\n <si-icon [icon]=\"icons.elementEdit\" />\n }\n </button>\n }\n } @else {\n @if (showContentActionBar()) {\n <si-content-action-bar\n toggleItemLabel=\"toggle\"\n viewType=\"expanded\"\n [disabled]=\"disabled()\"\n [primaryActions]=\"primaryEditActionsComputed()\"\n [secondaryActions]=\"secondaryEditActionsComputed()\"\n />\n } @else {\n <div>\n @for (action of editActions(); track $index) {\n @if (isToolbarItem(action)) {\n @if (disabled()) {\n <!-- Links do not support disabled, so we need to render a button instead in this case. -->\n <button type=\"button\" class=\"btn btn-secondary me-4\" disabled>\n {{ action.label! | translate }}\n </button>\n } @else {\n @switch (action.type) {\n @case ('action') {\n <button\n type=\"button\"\n class=\"btn btn-secondary me-4\"\n (click)=\"action.action(grid())\"\n >\n {{ action.label! | translate }}\n </button>\n }\n @case ('router-link') {\n <a\n class=\"btn btn-secondary me-4\"\n [routerLink]=\"action.routerLink\"\n [queryParams]=\"action.extras?.queryParams\"\n [queryParamsHandling]=\"action.extras?.queryParamsHandling\"\n [fragment]=\"action.extras?.fragment\"\n [state]=\"action.extras?.state\"\n [relativeTo]=\"action.extras?.relativeTo ?? activatedRoute\"\n [preserveFragment]=\"action.extras?.preserveFragment\"\n [skipLocationChange]=\"action.extras?.skipLocationChange\"\n [replaceUrl]=\"action.extras?.replaceUrl\"\n >\n {{ action.label! | translate }}\n </a>\n }\n @case ('link') {\n <a class=\"btn btn-secondary me-4\" [href]=\"action.href\" [target]=\"action.target\">\n {{ action.label! | translate }}\n </a>\n }\n }\n }\n } @else {\n <button\n type=\"button\"\n class=\"btn btn-secondary me-4\"\n [disabled]=\"disabled()\"\n [siLink]=\"action\"\n >\n {{ action.title! | translate }}\n </button>\n }\n }\n </div>\n }\n <div>\n <button\n type=\"button\"\n class=\"btn btn-secondary me-4\"\n [disabled]=\"disabled()\"\n (click)=\"onCancel()\"\n >\n {{ labelCancel | translate }}\n </button>\n <si-loading-button\n class=\"align-top\"\n buttonClass=\"btn btn-primary\"\n [disabled]=\"disableSaveButton()\"\n [loading]=\"disabled()\"\n (click)=\"onSave()\"\n >\n {{ labelSave | translate }}\n </si-loading-button>\n </div>\n }\n </div>\n</div>\n", dependencies: [{ kind: "component", type: SiContentActionBarComponent, selector: "si-content-action-bar", inputs: ["primaryActions", "secondaryActions", "actionParam", "viewType", "toggleItemLabel", "preventIconsInDropdownMenus", "disabled"] }, { kind: "directive", type: SiLinkDirective, selector: "[siLink]", inputs: ["siLink", "siLinkDefaultTarget", "actionParam", "activeClass", "exactMatch", "ariaCurrent"], outputs: ["activeChange"], exportAs: ["siLink"] }, { kind: "component", type: SiLoadingButtonComponent, selector: "si-loading-button", inputs: ["disabled", "loading", "type", "ariaLabel", "ariaLabelledBy", "buttonClass"] }, { kind: "component", type: SiIconComponent, selector: "si-icon", inputs: ["icon"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "directive", type: SiResponsiveContainerDirective, selector: "[siResponsiveContainer]", inputs: ["resizeThrottle", "breakpoints"], exportAs: ["siResponsiveContainer"] }, { kind: "directive", type: SiTooltipDirective, selector: "[siTooltip]", inputs: ["siTooltip", "placement", "isDisabled", "tooltipScrollStrategy", "tooltipContext"] }, { kind: "pipe", type: SiTranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.Eager });
|
|
253
285
|
}
|
|
254
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
286
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SiDashboardToolbarComponent, decorators: [{
|
|
255
287
|
type: Component,
|
|
256
288
|
args: [{ selector: 'si-dashboard-toolbar', imports: [
|
|
257
289
|
SiContentActionBarComponent,
|
|
258
290
|
SiLinkDirective,
|
|
259
|
-
|
|
260
|
-
|
|
291
|
+
SiLoadingButtonComponent,
|
|
292
|
+
SiIconComponent,
|
|
261
293
|
RouterLink,
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
294
|
+
SiResponsiveContainerDirective,
|
|
295
|
+
SiTranslatePipe,
|
|
296
|
+
SiTooltipDirective
|
|
297
|
+
], changeDetection: ChangeDetectionStrategy.Eager, host: {
|
|
298
|
+
class: 'd-flex flex-column flex-grow-1'
|
|
299
|
+
}, template: "<div class=\"row mb-3\" siResponsiveContainer>\n <div class=\"col-12 d-flex justify-content-between\">\n @if (!editable()) {\n <ng-content select=\"[filters-slot]\" />\n @if (!hideEditButton()) {\n @let showEditButtonLabelDesktop = this.showEditButtonLabelDesktop();\n <button\n type=\"button\"\n class=\"btn ms-auto\"\n [class]=\"showEditButtonLabelDesktop ? 'btn-secondary' : 'btn-icon btn-tertiary'\"\n [attr.aria-label]=\"labelEdit | translate\"\n [siTooltip]=\"labelEdit\"\n [isDisabled]=\"showEditButtonLabelDesktop\"\n [disabled]=\"disabled()\"\n (click)=\"onEdit()\"\n >\n @if (showEditButtonLabelDesktop) {\n {{ labelEdit | translate }}\n } @else {\n <si-icon [icon]=\"icons.elementEdit\" />\n }\n </button>\n }\n } @else {\n @if (showContentActionBar()) {\n <si-content-action-bar\n toggleItemLabel=\"toggle\"\n viewType=\"expanded\"\n [disabled]=\"disabled()\"\n [primaryActions]=\"primaryEditActionsComputed()\"\n [secondaryActions]=\"secondaryEditActionsComputed()\"\n />\n } @else {\n <div>\n @for (action of editActions(); track $index) {\n @if (isToolbarItem(action)) {\n @if (disabled()) {\n <!-- Links do not support disabled, so we need to render a button instead in this case. -->\n <button type=\"button\" class=\"btn btn-secondary me-4\" disabled>\n {{ action.label! | translate }}\n </button>\n } @else {\n @switch (action.type) {\n @case ('action') {\n <button\n type=\"button\"\n class=\"btn btn-secondary me-4\"\n (click)=\"action.action(grid())\"\n >\n {{ action.label! | translate }}\n </button>\n }\n @case ('router-link') {\n <a\n class=\"btn btn-secondary me-4\"\n [routerLink]=\"action.routerLink\"\n [queryParams]=\"action.extras?.queryParams\"\n [queryParamsHandling]=\"action.extras?.queryParamsHandling\"\n [fragment]=\"action.extras?.fragment\"\n [state]=\"action.extras?.state\"\n [relativeTo]=\"action.extras?.relativeTo ?? activatedRoute\"\n [preserveFragment]=\"action.extras?.preserveFragment\"\n [skipLocationChange]=\"action.extras?.skipLocationChange\"\n [replaceUrl]=\"action.extras?.replaceUrl\"\n >\n {{ action.label! | translate }}\n </a>\n }\n @case ('link') {\n <a class=\"btn btn-secondary me-4\" [href]=\"action.href\" [target]=\"action.target\">\n {{ action.label! | translate }}\n </a>\n }\n }\n }\n } @else {\n <button\n type=\"button\"\n class=\"btn btn-secondary me-4\"\n [disabled]=\"disabled()\"\n [siLink]=\"action\"\n >\n {{ action.title! | translate }}\n </button>\n }\n }\n </div>\n }\n <div>\n <button\n type=\"button\"\n class=\"btn btn-secondary me-4\"\n [disabled]=\"disabled()\"\n (click)=\"onCancel()\"\n >\n {{ labelCancel | translate }}\n </button>\n <si-loading-button\n class=\"align-top\"\n buttonClass=\"btn btn-primary\"\n [disabled]=\"disableSaveButton()\"\n [loading]=\"disabled()\"\n (click)=\"onSave()\"\n >\n {{ labelSave | translate }}\n </si-loading-button>\n </div>\n }\n </div>\n</div>\n" }]
|
|
300
|
+
}], propDecorators: { primaryEditActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "primaryEditActions", required: false }] }], secondaryEditActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "secondaryEditActions", required: false }] }], disableSaveButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "disableSaveButton", required: false }] }, { type: i0.Output, args: ["disableSaveButtonChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], editable: [{ type: i0.Input, args: [{ isSignal: true, alias: "editable", required: false }] }, { type: i0.Output, args: ["editableChange"] }], hideEditButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideEditButton", required: false }] }], showEditButtonLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "showEditButtonLabel", required: false }] }], grid: [{ type: i0.Input, args: [{ isSignal: true, alias: "grid", required: true }] }], save: [{ type: i0.Output, args: ["save"] }], dashboardToolbarContainer: [{ type: i0.ViewChild, args: [i0.forwardRef(() => SiResponsiveContainerDirective), { isSignal: true }] }] } });
|
|
269
301
|
|
|
270
302
|
/**
|
|
271
|
-
* Copyright (c) Siemens 2016 -
|
|
303
|
+
* Copyright (c) Siemens 2016 - 2026
|
|
272
304
|
* SPDX-License-Identifier: MIT
|
|
273
305
|
*/
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
306
|
+
/**
|
|
307
|
+
* Injection token to optionally inject the {@link SiWidgetIdProvider} implementation
|
|
308
|
+
* to provide custom widget id generation logic. The default implementation
|
|
309
|
+
* is {@link SiWidgetDefaultIdProvider}.
|
|
310
|
+
*
|
|
311
|
+
* * @example
|
|
312
|
+
* The following shows how to provide your own widget id provider.
|
|
313
|
+
* ```
|
|
314
|
+
* providers: [..., { provide: SI_WIDGET_ID_PROVIDER, useClass: CustomWidgetIdProvider }]
|
|
315
|
+
* ```
|
|
316
|
+
*
|
|
317
|
+
*/
|
|
318
|
+
const SI_WIDGET_ID_PROVIDER = new InjectionToken('Injection token to configure your widget id provider.', { providedIn: 'root', factory: () => inject(SiWidgetDefaultIdProvider) });
|
|
319
|
+
/**
|
|
320
|
+
* Abstract class to provide widget id generation logic.
|
|
321
|
+
*/
|
|
322
|
+
class SiWidgetIdProvider {
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* The default implementation of the {@link SiWidgetIdProvider} which
|
|
326
|
+
* generates random widget ids.
|
|
327
|
+
*/
|
|
328
|
+
class SiWidgetDefaultIdProvider extends SiWidgetIdProvider {
|
|
277
329
|
/**
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
*
|
|
281
|
-
*
|
|
330
|
+
* Generates a unique widget id.
|
|
331
|
+
*
|
|
332
|
+
* The method uses `crypto.randomUUID()` which generates a RFC4122 version 4 UUID
|
|
333
|
+
* (a cryptographically secure random identifier with 122 bits of entropy).
|
|
334
|
+
*
|
|
335
|
+
* @param widget - The widget instance config without any id.
|
|
336
|
+
* @param dashboardId - The id of the dashboard where the widget is added.
|
|
337
|
+
* @returns A unique widget id string in the format `crypto.randomUUID()`.
|
|
282
338
|
*/
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
return this.widgetCatalog.find(widget => widget.id === id);
|
|
339
|
+
generateWidgetId(widget, dashboardId) {
|
|
340
|
+
return crypto.randomUUID();
|
|
286
341
|
}
|
|
287
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
288
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
342
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SiWidgetDefaultIdProvider, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
343
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SiWidgetDefaultIdProvider, providedIn: 'root' });
|
|
289
344
|
}
|
|
290
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
291
|
-
type: Injectable
|
|
345
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SiWidgetDefaultIdProvider, decorators: [{
|
|
346
|
+
type: Injectable,
|
|
347
|
+
args: [{ providedIn: 'root' }]
|
|
292
348
|
}] });
|
|
293
349
|
|
|
294
350
|
/**
|
|
295
|
-
* Copyright (c) Siemens 2016 -
|
|
351
|
+
* Copyright (c) Siemens 2016 - 2026
|
|
296
352
|
* SPDX-License-Identifier: MIT
|
|
297
353
|
*/
|
|
298
354
|
class SiWebComponentWrapperBaseComponent {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
}
|
|
309
|
-
elementTagName;
|
|
310
|
-
url;
|
|
311
|
-
webComponentHost;
|
|
355
|
+
config = input.required(/* @ts-ignore */
|
|
356
|
+
...(ngDevMode ? [{ debugName: "config" }] : /* istanbul ignore next */ []));
|
|
357
|
+
elementTagName = input.required(/* @ts-ignore */
|
|
358
|
+
...(ngDevMode ? [{ debugName: "elementTagName" }] : /* istanbul ignore next */ []));
|
|
359
|
+
url = input.required(/* @ts-ignore */
|
|
360
|
+
...(ngDevMode ? [{ debugName: "url" }] : /* istanbul ignore next */ []));
|
|
361
|
+
webComponentHost = viewChild.required('webComponentHost', {
|
|
362
|
+
read: ElementRef
|
|
363
|
+
});
|
|
312
364
|
webComponent;
|
|
313
365
|
renderer2 = inject(Renderer2);
|
|
314
366
|
document = inject(DOCUMENT);
|
|
367
|
+
ngOnChanges(changes) {
|
|
368
|
+
if (changes.config && this.webComponent) {
|
|
369
|
+
this.webComponent.config = this.config();
|
|
370
|
+
}
|
|
371
|
+
}
|
|
315
372
|
ngAfterViewInit() {
|
|
316
|
-
if (!this.isScriptLoaded(this.url)) {
|
|
373
|
+
if (!this.isScriptLoaded(this.url())) {
|
|
317
374
|
const script = this.renderer2.createElement('script');
|
|
318
|
-
script.src = this.url;
|
|
375
|
+
script.src = this.url();
|
|
319
376
|
this.renderer2.appendChild(this.document.body, script);
|
|
320
377
|
}
|
|
321
|
-
this.webComponent = this.renderer2.createElement(this.elementTagName);
|
|
322
|
-
this.webComponent
|
|
378
|
+
this.webComponent = this.renderer2.createElement(this.elementTagName());
|
|
379
|
+
if (this.webComponent) {
|
|
380
|
+
this.webComponent.config = this.config();
|
|
381
|
+
}
|
|
323
382
|
}
|
|
324
383
|
isScriptLoaded(url) {
|
|
325
384
|
const script = document.querySelector(`script[src='${url}']`);
|
|
326
385
|
return script ? true : false;
|
|
327
386
|
}
|
|
328
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
329
|
-
static
|
|
387
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SiWebComponentWrapperBaseComponent, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
388
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "22.0.5", type: SiWebComponentWrapperBaseComponent, isStandalone: true, inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null }, elementTagName: { classPropertyName: "elementTagName", publicName: "elementTagName", isSignal: true, isRequired: true, transformFunction: null }, url: { classPropertyName: "url", publicName: "url", isSignal: true, isRequired: true, transformFunction: null } }, viewQueries: [{ propertyName: "webComponentHost", first: true, predicate: ["webComponentHost"], descendants: true, read: ElementRef, isSignal: true }], usesOnChanges: true, ngImport: i0 });
|
|
330
389
|
}
|
|
331
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
332
|
-
type:
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
}], propDecorators: { config: [{
|
|
337
|
-
type: Input
|
|
338
|
-
}], elementTagName: [{
|
|
339
|
-
type: Input
|
|
340
|
-
}], url: [{
|
|
341
|
-
type: Input
|
|
342
|
-
}], webComponentHost: [{
|
|
343
|
-
type: ViewChild,
|
|
344
|
-
args: ['webComponentHost', { static: true, read: ElementRef }]
|
|
345
|
-
}] } });
|
|
390
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SiWebComponentWrapperBaseComponent, decorators: [{
|
|
391
|
+
type: Directive
|
|
392
|
+
}], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: true }] }], elementTagName: [{ type: i0.Input, args: [{ isSignal: true, alias: "elementTagName", required: true }] }], url: [{ type: i0.Input, args: [{ isSignal: true, alias: "url", required: true }] }], webComponentHost: [{ type: i0.ViewChild, args: ['webComponentHost', { ...{
|
|
393
|
+
read: ElementRef
|
|
394
|
+
}, isSignal: true }] }] } });
|
|
346
395
|
|
|
347
396
|
/**
|
|
348
|
-
* Copyright (c) Siemens 2016 -
|
|
397
|
+
* Copyright (c) Siemens 2016 - 2026
|
|
349
398
|
* SPDX-License-Identifier: MIT
|
|
350
399
|
*/
|
|
351
400
|
class SiWebComponentEditorWrapperComponent extends SiWebComponentWrapperBaseComponent {
|
|
@@ -356,20 +405,24 @@ class SiWebComponentEditorWrapperComponent extends SiWebComponentWrapperBaseComp
|
|
|
356
405
|
* This will be set when setupWidgetInstanceEditor is executed
|
|
357
406
|
*/
|
|
358
407
|
statusChangesHandler;
|
|
359
|
-
webComponentEventListener = (event) => this.configChange.next(event.detail);
|
|
408
|
+
webComponentEventListener = (event) => event.detail && this.configChange.next(event.detail);
|
|
360
409
|
webComponentStateChangeListener = (event) => {
|
|
361
|
-
|
|
362
|
-
|
|
410
|
+
if (event.detail) {
|
|
411
|
+
this.state = event.detail;
|
|
412
|
+
this.stateChange.next(event.detail);
|
|
413
|
+
}
|
|
363
414
|
};
|
|
364
415
|
webComponentStatusChangesListener = (event) => {
|
|
365
|
-
|
|
416
|
+
if (event.detail) {
|
|
417
|
+
this.statusChangesHandler?.(event.detail);
|
|
418
|
+
}
|
|
366
419
|
};
|
|
367
420
|
ngAfterViewInit() {
|
|
368
421
|
super.ngAfterViewInit();
|
|
369
422
|
this.webComponent?.addEventListener('stateChange', this.webComponentStateChangeListener);
|
|
370
423
|
this.webComponent?.addEventListener('statusChanges', this.webComponentStatusChangesListener);
|
|
371
424
|
this.webComponent?.addEventListener('configChange', this.webComponentEventListener);
|
|
372
|
-
this.webComponentHost.nativeElement.appendChild(this.webComponent);
|
|
425
|
+
this.webComponentHost().nativeElement.appendChild(this.webComponent);
|
|
373
426
|
}
|
|
374
427
|
ngOnDestroy() {
|
|
375
428
|
this.webComponent?.removeEventListener('stateChange', this.webComponentStateChangeListener);
|
|
@@ -382,16 +435,16 @@ class SiWebComponentEditorWrapperComponent extends SiWebComponentWrapperBaseComp
|
|
|
382
435
|
previous() {
|
|
383
436
|
this.webComponent?.dispatchEvent(new CustomEvent('previous'));
|
|
384
437
|
}
|
|
385
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
386
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "
|
|
438
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SiWebComponentEditorWrapperComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
439
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.5", type: SiWebComponentEditorWrapperComponent, isStandalone: true, selector: "si-web-component-editor-wrapper", usesInheritance: true, ngImport: i0, template: "<div #webComponentHost class=\"h-100\"></div>\n", changeDetection: i0.ChangeDetectionStrategy.Eager });
|
|
387
440
|
}
|
|
388
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
441
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SiWebComponentEditorWrapperComponent, decorators: [{
|
|
389
442
|
type: Component,
|
|
390
|
-
args: [{ selector: 'si-web-component-editor-wrapper', template: "<div #webComponentHost class=\"h-100\"></div>\n" }]
|
|
443
|
+
args: [{ selector: 'si-web-component-editor-wrapper', changeDetection: ChangeDetectionStrategy.Eager, template: "<div #webComponentHost class=\"h-100\"></div>\n" }]
|
|
391
444
|
}] });
|
|
392
445
|
|
|
393
446
|
/**
|
|
394
|
-
* Copyright (c) Siemens 2016 -
|
|
447
|
+
* Copyright (c) Siemens 2016 - 2026
|
|
395
448
|
* SPDX-License-Identifier: MIT
|
|
396
449
|
*/
|
|
397
450
|
class SiWebComponentWrapperComponent extends SiWebComponentWrapperBaseComponent {
|
|
@@ -401,8 +454,8 @@ class SiWebComponentWrapperComponent extends SiWebComponentWrapperBaseComponent
|
|
|
401
454
|
}
|
|
402
455
|
set editable(editable) {
|
|
403
456
|
this._editable = editable;
|
|
404
|
-
if (this.
|
|
405
|
-
this.
|
|
457
|
+
if (this.webComponent) {
|
|
458
|
+
this.webComponent.editable = editable;
|
|
406
459
|
}
|
|
407
460
|
}
|
|
408
461
|
primaryActions;
|
|
@@ -410,11 +463,11 @@ class SiWebComponentWrapperComponent extends SiWebComponentWrapperBaseComponent
|
|
|
410
463
|
primaryEditActions;
|
|
411
464
|
secondaryEditActions;
|
|
412
465
|
configChange = new EventEmitter();
|
|
413
|
-
webComponentEventListener = (event) => this.configChanged(event.detail);
|
|
466
|
+
webComponentEventListener = (event) => event.detail && this.configChanged(event.detail);
|
|
414
467
|
ngAfterViewInit() {
|
|
415
468
|
super.ngAfterViewInit();
|
|
416
469
|
this.webComponent?.addEventListener('configChange', this.webComponentEventListener);
|
|
417
|
-
this.webComponentHost.nativeElement.appendChild(this.webComponent);
|
|
470
|
+
this.webComponentHost().nativeElement.appendChild(this.webComponent);
|
|
418
471
|
}
|
|
419
472
|
ngOnDestroy() {
|
|
420
473
|
this.webComponent?.removeEventListener('configChange', this.webComponentEventListener);
|
|
@@ -426,20 +479,19 @@ class SiWebComponentWrapperComponent extends SiWebComponentWrapperBaseComponent
|
|
|
426
479
|
this.secondaryEditActions = event.secondaryEditActions ?? this.secondaryEditActions;
|
|
427
480
|
this.configChange.emit(event);
|
|
428
481
|
}
|
|
429
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
430
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "
|
|
482
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SiWebComponentWrapperComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
483
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.5", type: SiWebComponentWrapperComponent, isStandalone: true, selector: "si-web-component-wrapper", usesInheritance: true, ngImport: i0, template: "<div #webComponentHost class=\"h-100\"></div>\n", changeDetection: i0.ChangeDetectionStrategy.Eager });
|
|
431
484
|
}
|
|
432
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
485
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SiWebComponentWrapperComponent, decorators: [{
|
|
433
486
|
type: Component,
|
|
434
|
-
args: [{ selector: 'si-web-component-wrapper', template: "<div #webComponentHost class=\"h-100\"></div>\n" }]
|
|
435
|
-
}]
|
|
436
|
-
type: Input
|
|
437
|
-
}] } });
|
|
487
|
+
args: [{ selector: 'si-web-component-wrapper', changeDetection: ChangeDetectionStrategy.Eager, template: "<div #webComponentHost class=\"h-100\"></div>\n" }]
|
|
488
|
+
}] });
|
|
438
489
|
|
|
439
490
|
/**
|
|
440
|
-
* Copyright (c) Siemens 2016 -
|
|
491
|
+
* Copyright (c) Siemens 2016 - 2026
|
|
441
492
|
* SPDX-License-Identifier: MIT
|
|
442
493
|
*/
|
|
494
|
+
/** @internal */
|
|
443
495
|
const widgetFactoryRegistry = {
|
|
444
496
|
_factories: {},
|
|
445
497
|
register(name, factoryFn) {
|
|
@@ -469,29 +521,50 @@ const setupComponent = (widgetComponentFactory, host, injector, envInjector, com
|
|
|
469
521
|
return throwError(() => new Error(`Unknown widget factory type ${widgetComponentFactory.factoryType}.`));
|
|
470
522
|
}
|
|
471
523
|
};
|
|
524
|
+
const loadModuleBasedComponent = (factory, componentName, host, injector, envInjector, result) => {
|
|
525
|
+
const loader = factory.moduleLoader;
|
|
526
|
+
const componentKey = factory[componentName];
|
|
527
|
+
loader(componentKey).then(moduleExports => {
|
|
528
|
+
const ngModuleRef = createNgModule(moduleExports[factory.moduleName], envInjector);
|
|
529
|
+
const componentType = moduleExports[componentKey];
|
|
530
|
+
const widgetInstanceRef = host.createComponent(componentType, { injector, ngModuleRef });
|
|
531
|
+
result.next(widgetInstanceRef);
|
|
532
|
+
result.complete();
|
|
533
|
+
}, rejection => {
|
|
534
|
+
const errorMsg = `Loading widget module ${factory.moduleName} failed`;
|
|
535
|
+
const msg = rejection ? `${errorMsg} with ${JSON.stringify(rejection.toString())}` : errorMsg;
|
|
536
|
+
result.error(msg);
|
|
537
|
+
result.complete();
|
|
538
|
+
});
|
|
539
|
+
};
|
|
540
|
+
const loadStandaloneComponent = (factory, componentName, host, injector, result) => {
|
|
541
|
+
const loader = factory.componentLoader;
|
|
542
|
+
const componentKey = factory[componentName];
|
|
543
|
+
loader(componentKey).then(componentType => {
|
|
544
|
+
const widgetInstanceRef = host.createComponent(componentType, { injector });
|
|
545
|
+
result.next(widgetInstanceRef);
|
|
546
|
+
result.complete();
|
|
547
|
+
}, rejection => {
|
|
548
|
+
const errorMsg = `Loading widget component ${componentKey} failed`;
|
|
549
|
+
const msg = rejection ? `${errorMsg} with ${JSON.stringify(rejection.toString())}` : errorMsg;
|
|
550
|
+
result.error(msg);
|
|
551
|
+
result.complete();
|
|
552
|
+
});
|
|
553
|
+
};
|
|
472
554
|
const loadAndAttachComponent = (factory, componentName, host, injector, envInjector) => {
|
|
473
555
|
const result = new Subject();
|
|
474
|
-
if (factory[componentName]) {
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
result.next(widgetInstanceRef);
|
|
484
|
-
result.complete();
|
|
485
|
-
}, rejection => {
|
|
486
|
-
const msg = rejection
|
|
487
|
-
? `Loading widget module ${factory.moduleName} failed with ${JSON.stringify(rejection.toString())}`
|
|
488
|
-
: `Loading widget module ${factory.moduleName} failed`;
|
|
489
|
-
result.error(msg);
|
|
490
|
-
result.complete();
|
|
491
|
-
});
|
|
556
|
+
if (!factory[componentName]) {
|
|
557
|
+
result.error(`Provided component factory has no ${componentName} component configuration`);
|
|
558
|
+
return result;
|
|
559
|
+
}
|
|
560
|
+
if (factory.moduleName) {
|
|
561
|
+
loadModuleBasedComponent(factory, componentName, host, injector, envInjector, result);
|
|
562
|
+
}
|
|
563
|
+
else if (factory.componentLoader) {
|
|
564
|
+
loadStandaloneComponent(factory, componentName, host, injector, result);
|
|
492
565
|
}
|
|
493
566
|
else {
|
|
494
|
-
result.error(
|
|
567
|
+
result.error('Invalid factory configuration: missing moduleLoader or componentLoader');
|
|
495
568
|
}
|
|
496
569
|
return result;
|
|
497
570
|
};
|
|
@@ -505,8 +578,8 @@ const loadAndAttachWebComponentWrapper = (factory, componentName, host) => {
|
|
|
505
578
|
else {
|
|
506
579
|
widgetInstanceRef = host.createComponent(SiWebComponentEditorWrapperComponent);
|
|
507
580
|
}
|
|
508
|
-
widgetInstanceRef.
|
|
509
|
-
widgetInstanceRef.
|
|
581
|
+
widgetInstanceRef.setInput('elementTagName', factory[componentName]);
|
|
582
|
+
widgetInstanceRef.setInput('url', factory.url);
|
|
510
583
|
result.next(widgetInstanceRef);
|
|
511
584
|
result.complete();
|
|
512
585
|
}
|
|
@@ -517,19 +590,40 @@ const loadAndAttachWebComponentWrapper = (factory, componentName, host) => {
|
|
|
517
590
|
};
|
|
518
591
|
|
|
519
592
|
/**
|
|
520
|
-
* Copyright (c) Siemens 2016 -
|
|
593
|
+
* Copyright (c) Siemens 2016 - 2026
|
|
521
594
|
* SPDX-License-Identifier: MIT
|
|
522
595
|
*/
|
|
523
596
|
class SiWidgetHostComponent {
|
|
524
597
|
siModal = inject(SiActionDialogService);
|
|
525
|
-
gridService = inject(SiGridService);
|
|
526
598
|
injector = inject(Injector);
|
|
527
599
|
envInjector = inject(EnvironmentInjector);
|
|
528
|
-
|
|
529
|
-
|
|
600
|
+
destroyRef = inject(DestroyRef);
|
|
601
|
+
translateService = injectSiTranslateService();
|
|
602
|
+
liveAnnouncer = inject(LiveAnnouncer);
|
|
603
|
+
/** @internal */
|
|
604
|
+
elementRef = inject(ElementRef).nativeElement;
|
|
605
|
+
/** @defaultValue false */
|
|
606
|
+
keyboardActive = signal(false, /* @ts-ignore */
|
|
607
|
+
...(ngDevMode ? [{ debugName: "keyboardActive" }] : /* istanbul ignore next */ []));
|
|
608
|
+
widgetConfig = input.required(/* @ts-ignore */
|
|
609
|
+
...(ngDevMode ? [{ debugName: "widgetConfig" }] : /* istanbul ignore next */ []));
|
|
610
|
+
grid = input(/* @ts-ignore */
|
|
611
|
+
...(ngDevMode ? [undefined, { debugName: "grid" }] : /* istanbul ignore next */ []));
|
|
612
|
+
/**
|
|
613
|
+
* The component factory for this widget's type.
|
|
614
|
+
*/
|
|
615
|
+
componentFactory = input(/* @ts-ignore */
|
|
616
|
+
...(ngDevMode ? [undefined, { debugName: "componentFactory" }] : /* istanbul ignore next */ []));
|
|
617
|
+
/**
|
|
618
|
+
* Sets the widget host into editable mode.
|
|
619
|
+
*
|
|
620
|
+
* @defaultValue false
|
|
621
|
+
*/
|
|
622
|
+
editable = input(false, /* @ts-ignore */
|
|
623
|
+
...(ngDevMode ? [{ debugName: "editable" }] : /* istanbul ignore next */ []));
|
|
530
624
|
remove = output();
|
|
531
625
|
edit = output();
|
|
532
|
-
|
|
626
|
+
gridEvent = output();
|
|
533
627
|
card = viewChild.required('card');
|
|
534
628
|
widgetHost = viewChild.required('widgetHost', { read: ViewContainerRef });
|
|
535
629
|
labelEdit = t(() => $localize `:@@DASHBOARD.WIDGET.EDIT:Edit`);
|
|
@@ -540,19 +634,36 @@ class SiWidgetHostComponent {
|
|
|
540
634
|
labelDialogHeading = t(() => $localize `:@@DASHBOARD.REMOVE_WIDGET_CONFIRMATION_DIALOG.HEADING:Remove widget`);
|
|
541
635
|
labelDialogRemove = t(() => $localize `:@@DASHBOARD.REMOVE_WIDGET_CONFIRMATION_DIALOG.REMOVE:Remove`);
|
|
542
636
|
labelDialogCancel = t(() => $localize `:@@DASHBOARD.REMOVE_WIDGET_CONFIRMATION_DIALOG.CANCEL:Cancel`);
|
|
637
|
+
a11yWidgetMovedMessage = t(() => $localize `:@@DASHBOARD.WIDGET.A11Y.MOVED:Widget moved to column {{column}}, row {{row}}.`);
|
|
638
|
+
a11yWidgetResizedMessage = t(() => $localize `:@@DASHBOARD.WIDGET.A11Y.RESIZED:Widget resized to {{columns}} columns wide, {{rows}} rows tall.`);
|
|
639
|
+
a11yWidgetActivatedMessage = t(() => $localize `:@@DASHBOARD.WIDGET.A11Y.ACTIVATED:Widget activated. Use arrow keys to move, Shift+arrow keys to resize, Escape to exit.`);
|
|
640
|
+
a11yWidgetDeactivatedMessage = t(() => $localize `:@@DASHBOARD.WIDGET.A11Y.DEACTIVATED:Widget deactivated.`);
|
|
641
|
+
a11yWidgetDescription = t(() => $localize `:@@DASHBOARD.WIDGET.A11Y.DESCRIPTION:Press Enter or Space to activate. Then use arrow keys to move, Shift+arrow keys to resize, Escape to exit.`);
|
|
642
|
+
widgetAriaLabel = computed(() => {
|
|
643
|
+
const widgetHeading = this.widgetConfig().heading;
|
|
644
|
+
if (widgetHeading) {
|
|
645
|
+
return this.translateService.translateSync(widgetHeading);
|
|
646
|
+
}
|
|
647
|
+
return null;
|
|
648
|
+
}, /* @ts-ignore */
|
|
649
|
+
...(ngDevMode ? [{ debugName: "widgetAriaLabel" }] : /* istanbul ignore next */ []));
|
|
543
650
|
widgetInstance;
|
|
544
651
|
widgetRef;
|
|
545
652
|
/** @defaultValue [] */
|
|
546
|
-
primaryActions = []
|
|
653
|
+
primaryActions = signal([], /* @ts-ignore */
|
|
654
|
+
...(ngDevMode ? [{ debugName: "primaryActions" }] : /* istanbul ignore next */ []));
|
|
547
655
|
/** @defaultValue [] */
|
|
548
|
-
secondaryActions = []
|
|
656
|
+
secondaryActions = signal([], /* @ts-ignore */
|
|
657
|
+
...(ngDevMode ? [{ debugName: "secondaryActions" }] : /* istanbul ignore next */ []));
|
|
549
658
|
/** @defaultValue 'expanded' */
|
|
550
|
-
actionBarViewType = 'expanded'
|
|
551
|
-
|
|
659
|
+
actionBarViewType = signal('expanded', /* @ts-ignore */
|
|
660
|
+
...(ngDevMode ? [{ debugName: "actionBarViewType" }] : /* istanbul ignore next */ []));
|
|
552
661
|
/** @defaultValue [] */
|
|
553
|
-
editablePrimaryActions = []
|
|
662
|
+
editablePrimaryActions = signal([], /* @ts-ignore */
|
|
663
|
+
...(ngDevMode ? [{ debugName: "editablePrimaryActions" }] : /* istanbul ignore next */ []));
|
|
554
664
|
/** @defaultValue [] */
|
|
555
|
-
editableSecondaryActions = []
|
|
665
|
+
editableSecondaryActions = signal([], /* @ts-ignore */
|
|
666
|
+
...(ngDevMode ? [{ debugName: "editableSecondaryActions" }] : /* istanbul ignore next */ []));
|
|
556
667
|
/**
|
|
557
668
|
* @defaultValue
|
|
558
669
|
* ```
|
|
@@ -591,13 +702,30 @@ class SiWidgetHostComponent {
|
|
|
591
702
|
iconOnly: true,
|
|
592
703
|
action: () => this.onRemove()
|
|
593
704
|
};
|
|
594
|
-
widgetInstanceFooter
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
705
|
+
widgetInstanceFooter = signal(undefined, /* @ts-ignore */
|
|
706
|
+
...(ngDevMode ? [{ debugName: "widgetInstanceFooter" }] : /* istanbul ignore next */ []));
|
|
707
|
+
accentLine = computed(() => {
|
|
708
|
+
const { accentLine } = this.widgetConfig();
|
|
709
|
+
return accentLine ? 'accent-' + accentLine : '';
|
|
710
|
+
}, /* @ts-ignore */
|
|
711
|
+
...(ngDevMode ? [{ debugName: "accentLine" }] : /* istanbul ignore next */ []));
|
|
599
712
|
ngOnChanges(changes) {
|
|
600
713
|
if (changes.widgetConfig) {
|
|
714
|
+
const options = {
|
|
715
|
+
...this.widgetConfig(),
|
|
716
|
+
w: this.widgetConfig().width,
|
|
717
|
+
h: this.widgetConfig().height,
|
|
718
|
+
x: this.widgetConfig().x,
|
|
719
|
+
y: this.widgetConfig().y,
|
|
720
|
+
minW: this.widgetConfig().minWidth,
|
|
721
|
+
minH: this.widgetConfig().minHeight
|
|
722
|
+
};
|
|
723
|
+
if (!changes.widgetConfig.firstChange) {
|
|
724
|
+
this.grid()?.update(this.elementRef, options);
|
|
725
|
+
}
|
|
726
|
+
else {
|
|
727
|
+
this.grid()?.makeWidget(this.elementRef, options);
|
|
728
|
+
}
|
|
601
729
|
if (this.widgetRef) {
|
|
602
730
|
if (isSignal(this.widgetRef.instance.config)) {
|
|
603
731
|
this.widgetRef.setInput('config', this.widgetConfig());
|
|
@@ -607,21 +735,148 @@ class SiWidgetHostComponent {
|
|
|
607
735
|
}
|
|
608
736
|
}
|
|
609
737
|
}
|
|
738
|
+
if (changes.editable && !changes.editable.firstChange) {
|
|
739
|
+
this.setupEditable(this.editable());
|
|
740
|
+
}
|
|
610
741
|
}
|
|
611
742
|
ngOnInit() {
|
|
612
743
|
this.attachWidgetInstance();
|
|
613
|
-
this.editable$
|
|
614
|
-
.pipe(takeUntil(this.unsubscribe))
|
|
615
|
-
.subscribe(editable => this.setupEditable(editable));
|
|
616
744
|
}
|
|
617
|
-
|
|
618
|
-
this.
|
|
619
|
-
|
|
745
|
+
onToggleActive(event) {
|
|
746
|
+
if (!this.editable() || event.target !== event.currentTarget) {
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
event.preventDefault();
|
|
750
|
+
const active = !this.keyboardActive();
|
|
751
|
+
this.keyboardActive.set(active);
|
|
752
|
+
this.announceTranslated(active ? this.a11yWidgetActivatedMessage : this.a11yWidgetDeactivatedMessage, {});
|
|
753
|
+
}
|
|
754
|
+
onDeactivate(event) {
|
|
755
|
+
if (!this.keyboardActive()) {
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
event.preventDefault();
|
|
759
|
+
this.keyboardActive.set(false);
|
|
760
|
+
this.announceTranslated(this.a11yWidgetDeactivatedMessage, {});
|
|
761
|
+
}
|
|
762
|
+
onFocusOut() {
|
|
763
|
+
if (this.keyboardActive()) {
|
|
764
|
+
this.keyboardActive.set(false);
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
onArrowKey(event) {
|
|
768
|
+
if (!this.keyboardActive()) {
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
const el = this.elementRef;
|
|
772
|
+
const node = el?.gridstackNode;
|
|
773
|
+
if (!node?.grid) {
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
const keyEvent = event;
|
|
777
|
+
if (keyEvent.shiftKey) {
|
|
778
|
+
this.handleResize(keyEvent, node);
|
|
779
|
+
}
|
|
780
|
+
else {
|
|
781
|
+
this.handleMove(keyEvent, node);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
handleResize(event, node) {
|
|
785
|
+
const el = this.elementRef;
|
|
786
|
+
let { w, h } = node;
|
|
787
|
+
switch (event.key) {
|
|
788
|
+
case 'ArrowRight':
|
|
789
|
+
w = (w ?? 1) + 1;
|
|
790
|
+
break;
|
|
791
|
+
case 'ArrowLeft':
|
|
792
|
+
w = Math.max(node.minW ?? 1, (w ?? 1) - 1);
|
|
793
|
+
break;
|
|
794
|
+
case 'ArrowDown':
|
|
795
|
+
h = (h ?? 1) + 1;
|
|
796
|
+
break;
|
|
797
|
+
case 'ArrowUp':
|
|
798
|
+
h = Math.max(node.minH ?? 1, (h ?? 1) - 1);
|
|
799
|
+
break;
|
|
800
|
+
default:
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
if (w === node.w && h === node.h) {
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
event.preventDefault();
|
|
807
|
+
const grid = node.grid;
|
|
808
|
+
grid.update(el, { w, h });
|
|
809
|
+
this.gridEvent.emit(new Event('resizestop'));
|
|
810
|
+
this.announceTranslated(this.a11yWidgetResizedMessage, { columns: w, rows: h });
|
|
811
|
+
}
|
|
812
|
+
handleMove(event, node) {
|
|
813
|
+
const el = this.elementRef;
|
|
814
|
+
const { x, y } = node;
|
|
815
|
+
const grid = node.grid;
|
|
816
|
+
let newX = x ?? 0;
|
|
817
|
+
let newY = y ?? 0;
|
|
818
|
+
switch (event.key) {
|
|
819
|
+
case 'ArrowRight':
|
|
820
|
+
do {
|
|
821
|
+
newX++;
|
|
822
|
+
grid.update(el, { x: newX, y });
|
|
823
|
+
} while (node.x === x && newX < grid.getColumn());
|
|
824
|
+
break;
|
|
825
|
+
case 'ArrowLeft':
|
|
826
|
+
if (newX <= 0) {
|
|
827
|
+
break;
|
|
828
|
+
}
|
|
829
|
+
do {
|
|
830
|
+
newX--;
|
|
831
|
+
grid.update(el, { x: newX, y });
|
|
832
|
+
} while (node.x === x && newX > 0);
|
|
833
|
+
break;
|
|
834
|
+
case 'ArrowDown':
|
|
835
|
+
do {
|
|
836
|
+
newY++;
|
|
837
|
+
grid.update(el, { x, y: newY });
|
|
838
|
+
} while (node.y === y && newY < grid.getRow());
|
|
839
|
+
break;
|
|
840
|
+
case 'ArrowUp':
|
|
841
|
+
if (newY <= 0) {
|
|
842
|
+
break;
|
|
843
|
+
}
|
|
844
|
+
do {
|
|
845
|
+
newY--;
|
|
846
|
+
grid.update(el, { x, y: newY });
|
|
847
|
+
} while (node.y === y && newY > 0);
|
|
848
|
+
break;
|
|
849
|
+
default:
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
event.preventDefault();
|
|
853
|
+
this.gridEvent.emit(new Event('dragstop'));
|
|
854
|
+
this.scrollIntoViewAfterTransition(el);
|
|
855
|
+
this.announceTranslated(this.a11yWidgetMovedMessage, {
|
|
856
|
+
column: (node.x ?? 0) + 1,
|
|
857
|
+
row: (node.y ?? 0) + 1
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
scrollIntoViewAfterTransition(el) {
|
|
861
|
+
const onEnd = () => {
|
|
862
|
+
el.removeEventListener('transitionend', onEnd);
|
|
863
|
+
el.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
|
|
864
|
+
};
|
|
865
|
+
el.addEventListener('transitionend', onEnd, { once: true });
|
|
866
|
+
}
|
|
867
|
+
announce(message) {
|
|
868
|
+
this.liveAnnouncer.announce(message, 'assertive');
|
|
869
|
+
}
|
|
870
|
+
announceTranslated(message, params) {
|
|
871
|
+
this.translateService
|
|
872
|
+
.translateAsync(message, params)
|
|
873
|
+
.pipe(first())
|
|
874
|
+
.subscribe(msg => this.announce(msg));
|
|
620
875
|
}
|
|
621
876
|
attachWidgetInstance() {
|
|
622
|
-
const
|
|
623
|
-
if (
|
|
624
|
-
setupWidgetInstance(
|
|
877
|
+
const componentFactory = this.componentFactory();
|
|
878
|
+
if (componentFactory) {
|
|
879
|
+
setupWidgetInstance(componentFactory, this.widgetHost(), this.injector, this.envInjector).subscribe({
|
|
625
880
|
next: (widgetRef) => {
|
|
626
881
|
this.widgetInstance = widgetRef.instance;
|
|
627
882
|
this.widgetRef = widgetRef;
|
|
@@ -630,8 +885,8 @@ class SiWidgetHostComponent {
|
|
|
630
885
|
// on web component, who pushes their configuration through an event after being attached
|
|
631
886
|
// to the DOM.
|
|
632
887
|
this.widgetInstance.configChange
|
|
633
|
-
.pipe(
|
|
634
|
-
.subscribe(event => setTimeout(() => this.setupEditable(this.editable
|
|
888
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
889
|
+
.subscribe(event => setTimeout(() => this.setupEditable(this.editable(), event)));
|
|
635
890
|
}
|
|
636
891
|
if (isSignal(this.widgetInstance.config)) {
|
|
637
892
|
this.widgetRef.setInput('config', this.widgetConfig());
|
|
@@ -639,19 +894,20 @@ class SiWidgetHostComponent {
|
|
|
639
894
|
else {
|
|
640
895
|
this.widgetInstance.config = this.widgetConfig();
|
|
641
896
|
}
|
|
642
|
-
this.widgetInstanceFooter
|
|
643
|
-
this.setupEditable(this.
|
|
644
|
-
this.initCompleted.emit();
|
|
897
|
+
this.widgetInstanceFooter.set(this.widgetInstance.footer);
|
|
898
|
+
this.setupEditable(this.editable());
|
|
645
899
|
},
|
|
646
900
|
error: error => console.error('Error: ', error)
|
|
647
901
|
});
|
|
648
902
|
}
|
|
649
903
|
else {
|
|
650
904
|
console.error(`Cannot find widget with id ${this.widgetConfig().widgetId}`);
|
|
651
|
-
this.initCompleted.emit();
|
|
652
905
|
}
|
|
653
906
|
}
|
|
654
907
|
setupEditable(editable, widgetConfig) {
|
|
908
|
+
if (!editable) {
|
|
909
|
+
this.keyboardActive.set(false);
|
|
910
|
+
}
|
|
655
911
|
widgetConfig ??= {
|
|
656
912
|
primaryActions: this.widgetInstance?.primaryActions,
|
|
657
913
|
secondaryActions: this.widgetInstance?.secondaryActions,
|
|
@@ -659,34 +915,37 @@ class SiWidgetHostComponent {
|
|
|
659
915
|
secondaryEditActions: this.widgetInstance?.secondaryEditActions
|
|
660
916
|
};
|
|
661
917
|
if (editable) {
|
|
662
|
-
this.editablePrimaryActions
|
|
918
|
+
this.editablePrimaryActions.set([]);
|
|
663
919
|
if (this.isEditable()) {
|
|
664
|
-
this.editablePrimaryActions.
|
|
920
|
+
this.editablePrimaryActions.set([...this.editablePrimaryActions(), this.editAction]);
|
|
665
921
|
}
|
|
666
922
|
if (!this.widgetConfig().isNotRemovable) {
|
|
667
|
-
this.editablePrimaryActions.
|
|
923
|
+
this.editablePrimaryActions.set([...this.editablePrimaryActions(), this.removeAction]);
|
|
668
924
|
}
|
|
669
925
|
if (widgetConfig.primaryEditActions) {
|
|
670
|
-
this.primaryActions
|
|
926
|
+
this.primaryActions.set([
|
|
927
|
+
...widgetConfig.primaryEditActions,
|
|
928
|
+
...this.editablePrimaryActions()
|
|
929
|
+
]);
|
|
671
930
|
}
|
|
672
931
|
else {
|
|
673
|
-
this.primaryActions
|
|
932
|
+
this.primaryActions.set(this.editablePrimaryActions());
|
|
674
933
|
}
|
|
675
934
|
if (widgetConfig.secondaryEditActions) {
|
|
676
|
-
this.secondaryActions
|
|
935
|
+
this.secondaryActions.set([
|
|
677
936
|
...widgetConfig.secondaryEditActions,
|
|
678
|
-
...this.editableSecondaryActions
|
|
679
|
-
];
|
|
937
|
+
...this.editableSecondaryActions()
|
|
938
|
+
]);
|
|
680
939
|
}
|
|
681
940
|
else {
|
|
682
|
-
this.secondaryActions
|
|
941
|
+
this.secondaryActions.set(this.editableSecondaryActions());
|
|
683
942
|
}
|
|
684
|
-
this.actionBarViewType
|
|
943
|
+
this.actionBarViewType.set('expanded');
|
|
685
944
|
}
|
|
686
945
|
else {
|
|
687
|
-
this.actionBarViewType
|
|
688
|
-
this.primaryActions
|
|
689
|
-
this.secondaryActions
|
|
946
|
+
this.actionBarViewType.set(this.widgetConfig().actionBarViewType ?? 'expanded');
|
|
947
|
+
this.primaryActions.set(widgetConfig.primaryActions ?? []);
|
|
948
|
+
this.secondaryActions.set(widgetConfig.secondaryActions ?? []);
|
|
690
949
|
}
|
|
691
950
|
if (this.widgetInstance?.editable !== undefined) {
|
|
692
951
|
if (isSignal(this.widgetInstance.editable)) {
|
|
@@ -709,8 +968,7 @@ class SiWidgetHostComponent {
|
|
|
709
968
|
}
|
|
710
969
|
isEditable() {
|
|
711
970
|
const widgetConfig = this.widgetConfig();
|
|
712
|
-
return
|
|
713
|
-
!!this.gridService.getWidget(widgetConfig.widgetId)?.componentFactory?.editorComponentName);
|
|
971
|
+
return !widgetConfig.immutable && !!this.componentFactory()?.editorComponentName;
|
|
714
972
|
}
|
|
715
973
|
onEdit() {
|
|
716
974
|
this.edit.emit(this.widgetConfig());
|
|
@@ -730,18 +988,24 @@ class SiWidgetHostComponent {
|
|
|
730
988
|
}
|
|
731
989
|
});
|
|
732
990
|
}
|
|
733
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
734
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
991
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SiWidgetHostComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
992
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: SiWidgetHostComponent, isStandalone: true, selector: "si-widget-host", inputs: { widgetConfig: { classPropertyName: "widgetConfig", publicName: "widgetConfig", isSignal: true, isRequired: true, transformFunction: null }, grid: { classPropertyName: "grid", publicName: "grid", isSignal: true, isRequired: false, transformFunction: null }, componentFactory: { classPropertyName: "componentFactory", publicName: "componentFactory", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { remove: "remove", edit: "edit", gridEvent: "gridEvent" }, host: { properties: { "attr.role": "editable() ? null : 'listitem'", "attr.aria-roledescription": "editable() ? null : 'widget'", "attr.aria-label": "editable() || !widgetAriaLabel() ? null : widgetAriaLabel()" }, classAttribute: "grid-stack-item" }, viewQueries: [{ propertyName: "card", first: true, predicate: ["card"], descendants: true, isSignal: true }, { propertyName: "widgetHost", first: true, predicate: ["widgetHost"], descendants: true, read: ViewContainerRef, isSignal: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"grid-stack-item-content p-4\">\n @if (editable()) {\n <div class=\"resize-handle\">\n <svg\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <g>\n <path d=\"M16 16H18V18H16V16Z\" fill=\"currentColor\" />\n <path d=\"M11 16H13V18H11V16Z\" fill=\"currentColor\" />\n <path d=\"M11 11H13V13H11V11Z\" fill=\"currentColor\" />\n <path d=\"M6 16H8V18H6V16Z\" fill=\"currentColor\" />\n <path d=\"M16 11H18V13H16V11Z\" fill=\"currentColor\" />\n <path d=\"M16 6H18V8H16V6Z\" fill=\"currentColor\" />\n </g>\n </svg>\n </div>\n <div class=\"draggable-overlay\"></div>\n }\n <si-dashboard-card\n #card\n class=\"h-100 dragging-card\"\n [class]=\"accentLine()\"\n [class.elevation-2]=\"keyboardActive()\"\n [class.si-widget-keyboard-active]=\"keyboardActive()\"\n [attr.tabindex]=\"editable() ? 0 : null\"\n [attr.role]=\"editable() ? 'application' : null\"\n [attr.aria-roledescription]=\"editable() ? 'widget' : null\"\n [attr.aria-label]=\"editable() ? widgetConfig().heading : null\"\n [attr.aria-description]=\"editable() ? (a11yWidgetDescription | translate) : null\"\n [expandText]=\"labelExpand\"\n [restoreText]=\"labelRestore\"\n [heading]=\"widgetConfig().heading\"\n [primaryActions]=\"primaryActions()\"\n [secondaryActions]=\"secondaryActions()\"\n [actionBarViewType]=\"actionBarViewType()\"\n [enableExpandInteraction]=\"widgetConfig().expandable\"\n [imgAlt]=\"widgetConfig().image?.alt\"\n [imgDir]=\"widgetConfig().image?.dir\"\n [imgObjectFit]=\"widgetConfig().image?.objectFit\"\n [imgObjectPosition]=\"widgetConfig().image?.objectPosition\"\n [imgSrc]=\"widgetConfig().image?.src\"\n (keydown.enter)=\"onToggleActive($event)\"\n (keydown.space)=\"onToggleActive($event)\"\n (keydown.escape)=\"onDeactivate($event)\"\n (keydown.arrowRight)=\"onArrowKey($event)\"\n (keydown.arrowLeft)=\"onArrowKey($event)\"\n (keydown.arrowUp)=\"onArrowKey($event)\"\n (keydown.arrowDown)=\"onArrowKey($event)\"\n (keydown.shift.arrowRight)=\"onArrowKey($event)\"\n (keydown.shift.arrowLeft)=\"onArrowKey($event)\"\n (keydown.shift.arrowUp)=\"onArrowKey($event)\"\n (keydown.shift.arrowDown)=\"onArrowKey($event)\"\n (focusout)=\"onFocusOut()\"\n >\n <div class=\"card-body overflow-auto\" body>\n <ng-container #widgetHost />\n </div>\n @let footer = widgetInstanceFooter();\n @if (footer) {\n <div class=\"card-footer\" footer>\n <ng-container *ngTemplateOutlet=\"footer\" />\n </div>\n }\n </si-dashboard-card>\n</div>\n", styles: [":host{display:flex}::ng-deep body:not(:has(.ui-draggable-dragging)) .draggable-overlay{cursor:grab}::ng-deep si-content-action-bar{z-index:500}::ng-deep .ui-droppable.ui-droppable-over>*:not(.ui-droppable){cursor:move!important;pointer-events:inherit!important}::ng-deep .ui-draggable-dragging .dragging-card,::ng-deep .ui-resizable-resizing .dragging-card{box-shadow:0 0 8px var(--element-box-shadow-color-1),0 8px 8px var(--element-box-shadow-color-2)!important}::ng-deep .ui-draggable-dragging>.grid-stack-item-content,::ng-deep .ui-resizable-resizing>.grid-stack-item-content{box-shadow:none;opacity:1}::ng-deep .ui-resizable-handle{display:block!important}::ng-deep .ui-resizable-se{background-image:none!important;transform:none!important;inset-block-end:6px!important;inset-inline-end:6px!important}.resize-handle{inset-block-end:6px;color:var(--element-ui-2);pointer-events:none;position:absolute;inset-inline-end:6px;text-align:center;z-index:10}.draggable-overlay{inset:8px;position:absolute;z-index:100}\n"], dependencies: [{ kind: "component", type: SiDashboardCardComponent, selector: "si-dashboard-card", inputs: ["restoreText", "expandText", "enableExpandInteraction", "showMenubar"], outputs: ["expandChange"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: SiTranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
735
993
|
}
|
|
736
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
994
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SiWidgetHostComponent, decorators: [{
|
|
737
995
|
type: Component,
|
|
738
|
-
args: [{ selector: 'si-widget-host', imports: [SiDashboardCardComponent,
|
|
739
|
-
class: 'grid-stack-item'
|
|
740
|
-
|
|
741
|
-
|
|
996
|
+
args: [{ selector: 'si-widget-host', imports: [SiDashboardCardComponent, NgTemplateOutlet, SiTranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
997
|
+
class: 'grid-stack-item',
|
|
998
|
+
// In non-edit mode, expose each widget as a labeled list item so screen
|
|
999
|
+
// readers group the widgets and let users jump between them. In edit mode
|
|
1000
|
+
// the inner card carries the interactive `application` role instead.
|
|
1001
|
+
'[attr.role]': "editable() ? null : 'listitem'",
|
|
1002
|
+
'[attr.aria-roledescription]': "editable() ? null : 'widget'",
|
|
1003
|
+
'[attr.aria-label]': 'editable() || !widgetAriaLabel() ? null : widgetAriaLabel()'
|
|
1004
|
+
}, template: "<div class=\"grid-stack-item-content p-4\">\n @if (editable()) {\n <div class=\"resize-handle\">\n <svg\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <g>\n <path d=\"M16 16H18V18H16V16Z\" fill=\"currentColor\" />\n <path d=\"M11 16H13V18H11V16Z\" fill=\"currentColor\" />\n <path d=\"M11 11H13V13H11V11Z\" fill=\"currentColor\" />\n <path d=\"M6 16H8V18H6V16Z\" fill=\"currentColor\" />\n <path d=\"M16 11H18V13H16V11Z\" fill=\"currentColor\" />\n <path d=\"M16 6H18V8H16V6Z\" fill=\"currentColor\" />\n </g>\n </svg>\n </div>\n <div class=\"draggable-overlay\"></div>\n }\n <si-dashboard-card\n #card\n class=\"h-100 dragging-card\"\n [class]=\"accentLine()\"\n [class.elevation-2]=\"keyboardActive()\"\n [class.si-widget-keyboard-active]=\"keyboardActive()\"\n [attr.tabindex]=\"editable() ? 0 : null\"\n [attr.role]=\"editable() ? 'application' : null\"\n [attr.aria-roledescription]=\"editable() ? 'widget' : null\"\n [attr.aria-label]=\"editable() ? widgetConfig().heading : null\"\n [attr.aria-description]=\"editable() ? (a11yWidgetDescription | translate) : null\"\n [expandText]=\"labelExpand\"\n [restoreText]=\"labelRestore\"\n [heading]=\"widgetConfig().heading\"\n [primaryActions]=\"primaryActions()\"\n [secondaryActions]=\"secondaryActions()\"\n [actionBarViewType]=\"actionBarViewType()\"\n [enableExpandInteraction]=\"widgetConfig().expandable\"\n [imgAlt]=\"widgetConfig().image?.alt\"\n [imgDir]=\"widgetConfig().image?.dir\"\n [imgObjectFit]=\"widgetConfig().image?.objectFit\"\n [imgObjectPosition]=\"widgetConfig().image?.objectPosition\"\n [imgSrc]=\"widgetConfig().image?.src\"\n (keydown.enter)=\"onToggleActive($event)\"\n (keydown.space)=\"onToggleActive($event)\"\n (keydown.escape)=\"onDeactivate($event)\"\n (keydown.arrowRight)=\"onArrowKey($event)\"\n (keydown.arrowLeft)=\"onArrowKey($event)\"\n (keydown.arrowUp)=\"onArrowKey($event)\"\n (keydown.arrowDown)=\"onArrowKey($event)\"\n (keydown.shift.arrowRight)=\"onArrowKey($event)\"\n (keydown.shift.arrowLeft)=\"onArrowKey($event)\"\n (keydown.shift.arrowUp)=\"onArrowKey($event)\"\n (keydown.shift.arrowDown)=\"onArrowKey($event)\"\n (focusout)=\"onFocusOut()\"\n >\n <div class=\"card-body overflow-auto\" body>\n <ng-container #widgetHost />\n </div>\n @let footer = widgetInstanceFooter();\n @if (footer) {\n <div class=\"card-footer\" footer>\n <ng-container *ngTemplateOutlet=\"footer\" />\n </div>\n }\n </si-dashboard-card>\n</div>\n", styles: [":host{display:flex}::ng-deep body:not(:has(.ui-draggable-dragging)) .draggable-overlay{cursor:grab}::ng-deep si-content-action-bar{z-index:500}::ng-deep .ui-droppable.ui-droppable-over>*:not(.ui-droppable){cursor:move!important;pointer-events:inherit!important}::ng-deep .ui-draggable-dragging .dragging-card,::ng-deep .ui-resizable-resizing .dragging-card{box-shadow:0 0 8px var(--element-box-shadow-color-1),0 8px 8px var(--element-box-shadow-color-2)!important}::ng-deep .ui-draggable-dragging>.grid-stack-item-content,::ng-deep .ui-resizable-resizing>.grid-stack-item-content{box-shadow:none;opacity:1}::ng-deep .ui-resizable-handle{display:block!important}::ng-deep .ui-resizable-se{background-image:none!important;transform:none!important;inset-block-end:6px!important;inset-inline-end:6px!important}.resize-handle{inset-block-end:6px;color:var(--element-ui-2);pointer-events:none;position:absolute;inset-inline-end:6px;text-align:center;z-index:10}.draggable-overlay{inset:8px;position:absolute;z-index:100}\n"] }]
|
|
1005
|
+
}], propDecorators: { widgetConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "widgetConfig", required: true }] }], grid: [{ type: i0.Input, args: [{ isSignal: true, alias: "grid", required: false }] }], componentFactory: [{ type: i0.Input, args: [{ isSignal: true, alias: "componentFactory", required: false }] }], editable: [{ type: i0.Input, args: [{ isSignal: true, alias: "editable", required: false }] }], remove: [{ type: i0.Output, args: ["remove"] }], edit: [{ type: i0.Output, args: ["edit"] }], gridEvent: [{ type: i0.Output, args: ["gridEvent"] }], card: [{ type: i0.ViewChild, args: ['card', { isSignal: true }] }], widgetHost: [{ type: i0.ViewChild, args: ['widgetHost', { ...{ read: ViewContainerRef }, isSignal: true }] }] } });
|
|
742
1006
|
|
|
743
1007
|
/**
|
|
744
|
-
* Copyright (c) Siemens 2016 -
|
|
1008
|
+
* Copyright (c) Siemens 2016 - 2026
|
|
745
1009
|
* SPDX-License-Identifier: MIT
|
|
746
1010
|
*/
|
|
747
1011
|
class SiGridstackWrapperComponent {
|
|
@@ -750,17 +1014,27 @@ class SiGridstackWrapperComponent {
|
|
|
750
1014
|
*
|
|
751
1015
|
* @defaultValue []
|
|
752
1016
|
*/
|
|
753
|
-
widgetConfigs = input([]
|
|
1017
|
+
widgetConfigs = input([], /* @ts-ignore */
|
|
1018
|
+
...(ngDevMode ? [{ debugName: "widgetConfigs" }] : /* istanbul ignore next */ []));
|
|
754
1019
|
/**
|
|
755
1020
|
* Whenever gridstack allows to drag, resize or delete the grid item
|
|
756
1021
|
*
|
|
757
1022
|
* @defaultValue false
|
|
758
1023
|
*/
|
|
759
|
-
editable = input(false
|
|
1024
|
+
editable = input(false, /* @ts-ignore */
|
|
1025
|
+
...(ngDevMode ? [{ debugName: "editable" }] : /* istanbul ignore next */ []));
|
|
760
1026
|
/**
|
|
761
1027
|
* Module configuration
|
|
762
1028
|
*/
|
|
763
|
-
gridConfig = input(
|
|
1029
|
+
gridConfig = input(/* @ts-ignore */
|
|
1030
|
+
...(ngDevMode ? [undefined, { debugName: "gridConfig" }] : /* istanbul ignore next */ []));
|
|
1031
|
+
/**
|
|
1032
|
+
* Map of widget id to widget definition, passed through to widget hosts.
|
|
1033
|
+
*
|
|
1034
|
+
* @defaultValue new Map()
|
|
1035
|
+
*/
|
|
1036
|
+
widgetCatalogMap = input(new Map(), /* @ts-ignore */
|
|
1037
|
+
...(ngDevMode ? [{ debugName: "widgetCatalogMap" }] : /* istanbul ignore next */ []));
|
|
764
1038
|
/**
|
|
765
1039
|
* Emits dashboard grid events.
|
|
766
1040
|
*/
|
|
@@ -773,44 +1047,38 @@ class SiGridstackWrapperComponent {
|
|
|
773
1047
|
* Emits the id of a widget instance that shall be edited.
|
|
774
1048
|
*/
|
|
775
1049
|
widgetInstanceEdit = output();
|
|
776
|
-
|
|
1050
|
+
widgetHosts = viewChildren(SiWidgetHostComponent, /* @ts-ignore */
|
|
1051
|
+
...(ngDevMode ? [{ debugName: "widgetHosts" }] : /* istanbul ignore next */ []));
|
|
1052
|
+
widgetHostsMap = computed(() => new Map(this.widgetHosts().map(host => [host.widgetConfig().id, host])), /* @ts-ignore */
|
|
1053
|
+
...(ngDevMode ? [{ debugName: "widgetHostsMap" }] : /* istanbul ignore next */ []));
|
|
1054
|
+
a11yWidgetsListLabel = t(() => $localize `:@@DASHBOARD.A11Y.WIDGETS_LIST:Dashboard widgets`);
|
|
777
1055
|
grid;
|
|
778
|
-
markedForRender = [];
|
|
779
|
-
gridItems = [];
|
|
780
|
-
itemIdAttr = 'item-id';
|
|
781
|
-
widgetIdSubscriptionMap = new Map();
|
|
782
1056
|
ngZone = inject(NgZone);
|
|
783
1057
|
elementRef = inject(ElementRef);
|
|
1058
|
+
injector = inject(Injector);
|
|
1059
|
+
platformId = inject(PLATFORM_ID);
|
|
1060
|
+
isBrowser = isPlatformBrowser(this.platformId);
|
|
1061
|
+
destroyRef = inject(DestroyRef);
|
|
784
1062
|
ngOnChanges(changes) {
|
|
785
|
-
if (changes.widgetConfigs) {
|
|
786
|
-
const
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
1063
|
+
if (changes.widgetConfigs && !changes.widgetConfigs.firstChange) {
|
|
1064
|
+
const configs = changes.widgetConfigs.currentValue;
|
|
1065
|
+
const newIds = new Set(configs.map(w => w.id));
|
|
1066
|
+
const hostMap = this.widgetHostsMap();
|
|
1067
|
+
const hasAdds = configs.some(w => !hostMap.has(w.id));
|
|
1068
|
+
if (hasAdds) {
|
|
1069
|
+
this.startBatch();
|
|
790
1070
|
}
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
if (toBeAdded) {
|
|
797
|
-
this.mount(toBeAdded);
|
|
798
|
-
}
|
|
799
|
-
if (toBeRemoved) {
|
|
800
|
-
this.unmount(toBeRemoved);
|
|
1071
|
+
if (this.isBrowser) {
|
|
1072
|
+
for (const [id, host] of hostMap) {
|
|
1073
|
+
if (!newIds.has(id)) {
|
|
1074
|
+
this.grid?.removeWidget(host.elementRef);
|
|
1075
|
+
}
|
|
801
1076
|
}
|
|
802
1077
|
}
|
|
803
|
-
// Detect changes
|
|
804
|
-
this.updateViewComponents(currentValue);
|
|
805
|
-
this.updateLayout(currentValue);
|
|
806
|
-
this.grid?.batchUpdate(false);
|
|
807
1078
|
}
|
|
808
|
-
if (changes.editable) {
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
this.grid.enableMove(currentValue);
|
|
812
|
-
this.grid.enableResize(currentValue);
|
|
813
|
-
}
|
|
1079
|
+
if (changes.editable && !changes.editable.firstChange) {
|
|
1080
|
+
this.grid?.enableMove(changes.editable.currentValue);
|
|
1081
|
+
this.grid?.enableResize(changes.editable.currentValue);
|
|
814
1082
|
}
|
|
815
1083
|
}
|
|
816
1084
|
ngOnInit() {
|
|
@@ -824,107 +1092,32 @@ class SiGridstackWrapperComponent {
|
|
|
824
1092
|
...initialViewMode
|
|
825
1093
|
};
|
|
826
1094
|
this.grid = GridStack.init(options, this.elementRef.nativeElement.firstChild);
|
|
1095
|
+
this.startBatch();
|
|
827
1096
|
this.hookEvents(this.grid);
|
|
828
|
-
this.
|
|
829
|
-
}
|
|
830
|
-
ngOnDestroy() {
|
|
831
|
-
this.widgetIdSubscriptionMap.forEach(subscriptions => {
|
|
832
|
-
subscriptions.forEach(subscription => subscription.unsubscribe());
|
|
833
|
-
});
|
|
834
|
-
this.widgetIdSubscriptionMap.clear();
|
|
835
|
-
}
|
|
836
|
-
mount(items) {
|
|
837
|
-
if (items.length > 0) {
|
|
838
|
-
items.forEach(item => {
|
|
839
|
-
this.addToView(item);
|
|
840
|
-
});
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
unmount(items) {
|
|
844
|
-
if (items.length > 0) {
|
|
845
|
-
items.forEach(item => {
|
|
846
|
-
this.removeFromView(item.id);
|
|
847
|
-
});
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
getLayout() {
|
|
851
|
-
const gridItems = this.grid?.getGridItems();
|
|
852
|
-
if (gridItems.length > 0) {
|
|
853
|
-
const positions = gridItems.map(gridItemHTMLElement => {
|
|
854
|
-
const id = gridItemHTMLElement.getAttribute('item-id');
|
|
855
|
-
const x = Number(gridItemHTMLElement.getAttribute('gs-x')) || 0;
|
|
856
|
-
const y = Number(gridItemHTMLElement.getAttribute('gs-y')) || 0;
|
|
857
|
-
const width = Number(gridItemHTMLElement.getAttribute('gs-w')) || 0;
|
|
858
|
-
const height = Number(gridItemHTMLElement.getAttribute('gs-h')) || 0;
|
|
859
|
-
return { id, x, y, width, height };
|
|
860
|
-
});
|
|
861
|
-
return positions;
|
|
862
|
-
}
|
|
863
|
-
else {
|
|
864
|
-
return [];
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
updateLayout(widgets) {
|
|
868
|
-
const tmp = widgets.map(w => ({ ...w, w: w.width, h: w.height }));
|
|
869
|
-
if (this.grid) {
|
|
870
|
-
const gridItems = this.grid.getGridItems();
|
|
871
|
-
gridItems.forEach(gridItem => {
|
|
872
|
-
const config = tmp.find(widget => widget.id === gridItem.getAttribute('item-id'));
|
|
873
|
-
this.grid.update(gridItem, { ...config });
|
|
874
|
-
});
|
|
875
|
-
}
|
|
876
|
-
}
|
|
877
|
-
addToView(item) {
|
|
878
|
-
const componentRef = this.gridstackContainer().createComponent(SiWidgetHostComponent);
|
|
879
|
-
componentRef.setInput('widgetConfig', item);
|
|
880
|
-
const subscriptions = [];
|
|
881
|
-
const subscriptionRemove = componentRef.instance.remove.subscribe(widgetId => {
|
|
882
|
-
const widgetSubscriptions = this.widgetIdSubscriptionMap.get(widgetId);
|
|
883
|
-
if (widgetSubscriptions) {
|
|
884
|
-
widgetSubscriptions.forEach(sub => sub.unsubscribe());
|
|
885
|
-
}
|
|
886
|
-
this.widgetInstanceRemove.emit(widgetId);
|
|
887
|
-
});
|
|
888
|
-
subscriptions.push(subscriptionRemove);
|
|
889
|
-
const subscriptionEdit = componentRef.instance.edit.subscribe(widgetConfig => {
|
|
890
|
-
this.widgetInstanceEdit.emit(widgetConfig);
|
|
891
|
-
});
|
|
892
|
-
subscriptions.push(subscriptionEdit);
|
|
893
|
-
this.widgetIdSubscriptionMap.set(item.id, subscriptions);
|
|
894
|
-
const element = componentRef.location.nativeElement;
|
|
895
|
-
element.setAttribute(this.itemIdAttr, item.id);
|
|
896
|
-
this.gridItems.push({ id: item.id, component: componentRef });
|
|
897
|
-
this.grid.makeWidget(element, {
|
|
898
|
-
w: item.width,
|
|
899
|
-
h: item.height,
|
|
900
|
-
x: item.x,
|
|
901
|
-
y: item.y,
|
|
902
|
-
minW: item.minWidth,
|
|
903
|
-
minH: item.minHeight
|
|
904
|
-
});
|
|
1097
|
+
this.destroyRef.onDestroy(() => this.grid?.destroy());
|
|
905
1098
|
}
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
this.grid.removeWidget(toRemove);
|
|
911
|
-
const index = this.gridItems.findIndex(i => i.id === widgetId);
|
|
912
|
-
this.gridItems[index].component.destroy();
|
|
913
|
-
this.gridItems.splice(index, 1);
|
|
914
|
-
}
|
|
1099
|
+
startBatch() {
|
|
1100
|
+
this.grid?.batchUpdate(true);
|
|
1101
|
+
// this ensures that si-widget-host components are rendered before turning off batch mode.
|
|
1102
|
+
afterNextRender(() => this.grid?.batchUpdate(false), { injector: this.injector });
|
|
915
1103
|
}
|
|
916
1104
|
/**
|
|
917
|
-
*
|
|
918
|
-
*
|
|
1105
|
+
*
|
|
1106
|
+
* Returns the position of a specific widget in the grid.
|
|
919
1107
|
*/
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
if (gridItem) {
|
|
925
|
-
gridItem.component.setInput('widgetConfig', config);
|
|
926
|
-
}
|
|
1108
|
+
getWidgetLayout(widgetId) {
|
|
1109
|
+
const gridItem = this.widgetHostsMap().get(widgetId);
|
|
1110
|
+
if (!gridItem) {
|
|
1111
|
+
return undefined;
|
|
927
1112
|
}
|
|
1113
|
+
const element = gridItem.elementRef;
|
|
1114
|
+
return {
|
|
1115
|
+
id: widgetId,
|
|
1116
|
+
x: Number(element.getAttribute('gs-x')) || 0,
|
|
1117
|
+
y: Number(element.getAttribute('gs-y')) || 0,
|
|
1118
|
+
width: Number(element.getAttribute('gs-w')) || 0,
|
|
1119
|
+
height: Number(element.getAttribute('gs-h')) || 0
|
|
1120
|
+
};
|
|
928
1121
|
}
|
|
929
1122
|
hookEvents(grid) {
|
|
930
1123
|
grid.on('added removed dragstop resizestop disable enable dropped resize resizestart drag dragstart change', (event) => {
|
|
@@ -936,16 +1129,96 @@ class SiGridstackWrapperComponent {
|
|
|
936
1129
|
});
|
|
937
1130
|
});
|
|
938
1131
|
}
|
|
939
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
940
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.
|
|
1132
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SiGridstackWrapperComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1133
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: SiGridstackWrapperComponent, isStandalone: true, selector: "si-gridstack-wrapper", inputs: { widgetConfigs: { classPropertyName: "widgetConfigs", publicName: "widgetConfigs", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, gridConfig: { classPropertyName: "gridConfig", publicName: "gridConfig", isSignal: true, isRequired: false, transformFunction: null }, widgetCatalogMap: { classPropertyName: "widgetCatalogMap", publicName: "widgetCatalogMap", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { gridEvent: "gridEvent", widgetInstanceRemove: "widgetInstanceRemove", widgetInstanceEdit: "widgetInstanceEdit" }, viewQueries: [{ propertyName: "widgetHosts", predicate: SiWidgetHostComponent, descendants: true, isSignal: true }], usesOnChanges: true, ngImport: i0, template: "<div\n class=\"grid-stack\"\n [attr.role]=\"editable() ? null : 'list'\"\n [attr.aria-label]=\"editable() ? null : (a11yWidgetsListLabel | translate)\"\n>\n @if (grid) {\n @for (item of widgetConfigs(); track item.id) {\n <si-widget-host\n [grid]=\"grid\"\n [attr.item-id]=\"item.id\"\n [editable]=\"editable()\"\n [widgetConfig]=\"item\"\n [componentFactory]=\"widgetCatalogMap().get(item.widgetId)?.componentFactory\"\n (remove)=\"widgetInstanceRemove.emit(item.id)\"\n (edit)=\"widgetInstanceEdit.emit(item)\"\n />\n }\n }\n</div>\n", styles: ["::ng-deep .si-grid-stack-placeholder{background-color:var(--element-base-information);opacity:.3}:host{display:flex;flex:1 1 0;flex-direction:column;margin-inline-start:-8px;margin-inline-end:-8px;margin-block-start:-8px}.grid-stack{display:block}\n"], dependencies: [{ kind: "component", type: SiWidgetHostComponent, selector: "si-widget-host", inputs: ["widgetConfig", "grid", "componentFactory", "editable"], outputs: ["remove", "edit", "gridEvent"] }, { kind: "pipe", type: SiTranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.Eager });
|
|
941
1134
|
}
|
|
942
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1135
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SiGridstackWrapperComponent, decorators: [{
|
|
943
1136
|
type: Component,
|
|
944
|
-
args: [{ selector: 'si-gridstack-wrapper', template: "<div
|
|
945
|
-
}] });
|
|
1137
|
+
args: [{ selector: 'si-gridstack-wrapper', imports: [SiWidgetHostComponent, SiTranslatePipe], changeDetection: ChangeDetectionStrategy.Eager, template: "<div\n class=\"grid-stack\"\n [attr.role]=\"editable() ? null : 'list'\"\n [attr.aria-label]=\"editable() ? null : (a11yWidgetsListLabel | translate)\"\n>\n @if (grid) {\n @for (item of widgetConfigs(); track item.id) {\n <si-widget-host\n [grid]=\"grid\"\n [attr.item-id]=\"item.id\"\n [editable]=\"editable()\"\n [widgetConfig]=\"item\"\n [componentFactory]=\"widgetCatalogMap().get(item.widgetId)?.componentFactory\"\n (remove)=\"widgetInstanceRemove.emit(item.id)\"\n (edit)=\"widgetInstanceEdit.emit(item)\"\n />\n }\n }\n</div>\n", styles: ["::ng-deep .si-grid-stack-placeholder{background-color:var(--element-base-information);opacity:.3}:host{display:flex;flex:1 1 0;flex-direction:column;margin-inline-start:-8px;margin-inline-end:-8px;margin-block-start:-8px}.grid-stack{display:block}\n"] }]
|
|
1138
|
+
}], propDecorators: { widgetConfigs: [{ type: i0.Input, args: [{ isSignal: true, alias: "widgetConfigs", required: false }] }], editable: [{ type: i0.Input, args: [{ isSignal: true, alias: "editable", required: false }] }], gridConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "gridConfig", required: false }] }], widgetCatalogMap: [{ type: i0.Input, args: [{ isSignal: true, alias: "widgetCatalogMap", required: false }] }], gridEvent: [{ type: i0.Output, args: ["gridEvent"] }], widgetInstanceRemove: [{ type: i0.Output, args: ["widgetInstanceRemove"] }], widgetInstanceEdit: [{ type: i0.Output, args: ["widgetInstanceEdit"] }], widgetHosts: [{ type: i0.ViewChildren, args: [i0.forwardRef(() => SiWidgetHostComponent), { isSignal: true }] }] } });
|
|
1139
|
+
|
|
1140
|
+
/**
|
|
1141
|
+
* Copyright (c) Siemens 2016 - 2026
|
|
1142
|
+
* SPDX-License-Identifier: MIT
|
|
1143
|
+
*/
|
|
1144
|
+
/**
|
|
1145
|
+
* Base class encapsulating common widget editor lifecycle management.
|
|
1146
|
+
* Both `SiWidgetCatalogComponent` and `SiWidgetInstanceEditorDialogComponent`
|
|
1147
|
+
* extend this class to share editor setup, teardown, and status handling logic.
|
|
1148
|
+
*/
|
|
1149
|
+
class SiWidgetEditorBase {
|
|
1150
|
+
/** Indicates if the current config is valid or not. */
|
|
1151
|
+
invalidConfig = signal(false, /* @ts-ignore */
|
|
1152
|
+
...(ngDevMode ? [{ debugName: "invalidConfig" }] : /* istanbul ignore next */ []));
|
|
1153
|
+
/** Marks the widget configuration as modified. */
|
|
1154
|
+
widgetConfigModified = signal(false, /* @ts-ignore */
|
|
1155
|
+
...(ngDevMode ? [{ debugName: "widgetConfigModified" }] : /* istanbul ignore next */ []));
|
|
1156
|
+
editorWizardState = signal(undefined, /* @ts-ignore */
|
|
1157
|
+
...(ngDevMode ? [{ debugName: "editorWizardState" }] : /* istanbul ignore next */ []));
|
|
1158
|
+
widgetInstanceEditor;
|
|
1159
|
+
subscriptions = [];
|
|
1160
|
+
editorHost = viewChild.required('editorHost', { read: ViewContainerRef });
|
|
1161
|
+
injector = inject(Injector);
|
|
1162
|
+
envInjector = inject(EnvironmentInjector);
|
|
1163
|
+
ngOnDestroy() {
|
|
1164
|
+
this.tearDownEditor();
|
|
1165
|
+
}
|
|
1166
|
+
loadWidgetEditor(widgetComponentFactory, host) {
|
|
1167
|
+
return setupWidgetEditor(widgetComponentFactory, host, this.injector, this.envInjector);
|
|
1168
|
+
}
|
|
1169
|
+
initializeEditor(componentRef, config) {
|
|
1170
|
+
this.widgetInstanceEditor = componentRef.instance;
|
|
1171
|
+
if (isSignal(this.widgetInstanceEditor.config)) {
|
|
1172
|
+
componentRef.setInput('config', config);
|
|
1173
|
+
}
|
|
1174
|
+
else {
|
|
1175
|
+
this.widgetInstanceEditor.config = config;
|
|
1176
|
+
}
|
|
1177
|
+
// To be used by webcomponent wrapper
|
|
1178
|
+
if ('statusChangesHandler' in this.widgetInstanceEditor) {
|
|
1179
|
+
this.widgetInstanceEditor.statusChangesHandler = this.handleStatusChanges.bind(this);
|
|
1180
|
+
}
|
|
1181
|
+
if (this.widgetInstanceEditor.statusChanges) {
|
|
1182
|
+
this.subscriptions.push(this.widgetInstanceEditor.statusChanges.subscribe(statusChanges => this.handleStatusChanges(statusChanges)));
|
|
1183
|
+
}
|
|
1184
|
+
else if (this.widgetInstanceEditor.configChange) {
|
|
1185
|
+
this.subscriptions.push(this.widgetInstanceEditor.configChange.subscribe(() => this.widgetConfigModified.set(true)));
|
|
1186
|
+
}
|
|
1187
|
+
if (this.isEditorWizard(this.widgetInstanceEditor)) {
|
|
1188
|
+
this.editorWizardState.set(this.widgetInstanceEditor.state);
|
|
1189
|
+
if (this.widgetInstanceEditor.stateChange) {
|
|
1190
|
+
this.subscriptions.push(this.widgetInstanceEditor.stateChange.subscribe(state => this.editorWizardState.set(state)));
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
tearDownEditor() {
|
|
1195
|
+
this.invalidConfig.set(false);
|
|
1196
|
+
this.widgetConfigModified.set(false);
|
|
1197
|
+
this.editorWizardState.set(undefined);
|
|
1198
|
+
this.widgetInstanceEditor = undefined;
|
|
1199
|
+
this.subscriptions.forEach(s => s.unsubscribe());
|
|
1200
|
+
this.subscriptions = [];
|
|
1201
|
+
}
|
|
1202
|
+
isEditorWizard(editor) {
|
|
1203
|
+
return !!editor && 'state' in editor;
|
|
1204
|
+
}
|
|
1205
|
+
handleStatusChanges(statusChanges) {
|
|
1206
|
+
if (statusChanges.invalid !== undefined) {
|
|
1207
|
+
this.invalidConfig.set(statusChanges.invalid);
|
|
1208
|
+
}
|
|
1209
|
+
if (statusChanges.modified !== undefined) {
|
|
1210
|
+
this.widgetConfigModified.set(statusChanges.modified);
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SiWidgetEditorBase, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1214
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "22.0.5", type: SiWidgetEditorBase, isStandalone: true, viewQueries: [{ propertyName: "editorHost", first: true, predicate: ["editorHost"], descendants: true, read: ViewContainerRef, isSignal: true }], ngImport: i0 });
|
|
1215
|
+
}
|
|
1216
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SiWidgetEditorBase, decorators: [{
|
|
1217
|
+
type: Directive
|
|
1218
|
+
}], propDecorators: { editorHost: [{ type: i0.ViewChild, args: ['editorHost', { ...{ read: ViewContainerRef }, isSignal: true }] }] } });
|
|
946
1219
|
|
|
947
1220
|
/**
|
|
948
|
-
* Copyright (c) Siemens 2016 -
|
|
1221
|
+
* Copyright (c) Siemens 2016 - 2026
|
|
949
1222
|
* SPDX-License-Identifier: MIT
|
|
950
1223
|
*/
|
|
951
1224
|
/**
|
|
@@ -954,17 +1227,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.6", ngImpor
|
|
|
954
1227
|
* into its content. The dialog component is accountable for interacting with the dashboard
|
|
955
1228
|
* and offers options for saving changes or terminating the editing process.
|
|
956
1229
|
*/
|
|
957
|
-
class SiWidgetInstanceEditorDialogComponent {
|
|
1230
|
+
class SiWidgetInstanceEditorDialogComponent extends SiWidgetEditorBase {
|
|
958
1231
|
/**
|
|
959
1232
|
* Input for the widget instance configuration. It is used to populate the
|
|
960
1233
|
* widget editor.
|
|
961
1234
|
*/
|
|
962
|
-
widgetConfig = model.required(
|
|
1235
|
+
widgetConfig = model.required(/* @ts-ignore */
|
|
1236
|
+
...(ngDevMode ? [{ debugName: "widgetConfig" }] : /* istanbul ignore next */ []));
|
|
963
1237
|
/**
|
|
964
1238
|
* Input for the widget definition. It is required to retrieve the component
|
|
965
1239
|
* factory to instantiate the widget editor.
|
|
966
1240
|
*/
|
|
967
|
-
widget = input.required(
|
|
1241
|
+
widget = input.required(/* @ts-ignore */
|
|
1242
|
+
...(ngDevMode ? [{ debugName: "widget" }] : /* istanbul ignore next */ []));
|
|
968
1243
|
/**
|
|
969
1244
|
* Emits the edited widget instance configuration if the user confirms by
|
|
970
1245
|
* saving, or `undefined` if the user cancels the dialog.
|
|
@@ -974,10 +1249,8 @@ class SiWidgetInstanceEditorDialogComponent {
|
|
|
974
1249
|
* Emits when the editor instantiation is completed.
|
|
975
1250
|
*/
|
|
976
1251
|
editorSetupCompleted = output();
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
invalidConfig = signal(false);
|
|
980
|
-
showNextButton = computed(() => this.editorWizardState() !== undefined ? true : false);
|
|
1252
|
+
showNextButton = computed(() => this.editorWizardState() !== undefined ? true : false, /* @ts-ignore */
|
|
1253
|
+
...(ngDevMode ? [{ debugName: "showNextButton" }] : /* istanbul ignore next */ []));
|
|
981
1254
|
disableNextButton = computed(() => {
|
|
982
1255
|
const wizardState = this.editorWizardState();
|
|
983
1256
|
if (!wizardState) {
|
|
@@ -992,12 +1265,15 @@ class SiWidgetInstanceEditorDialogComponent {
|
|
|
992
1265
|
else {
|
|
993
1266
|
return false;
|
|
994
1267
|
}
|
|
995
|
-
}
|
|
996
|
-
|
|
1268
|
+
}, /* @ts-ignore */
|
|
1269
|
+
...(ngDevMode ? [{ debugName: "disableNextButton" }] : /* istanbul ignore next */ []));
|
|
1270
|
+
showPreviousButton = computed(() => !!this.editorWizardState(), /* @ts-ignore */
|
|
1271
|
+
...(ngDevMode ? [{ debugName: "showPreviousButton" }] : /* istanbul ignore next */ []));
|
|
997
1272
|
disablePreviousButton = computed(() => {
|
|
998
1273
|
const wizardState = this.editorWizardState();
|
|
999
1274
|
return wizardState ? !wizardState.hasPrevious : true;
|
|
1000
|
-
}
|
|
1275
|
+
}, /* @ts-ignore */
|
|
1276
|
+
...(ngDevMode ? [{ debugName: "disablePreviousButton" }] : /* istanbul ignore next */ []));
|
|
1001
1277
|
labelSave = t(() => $localize `:@@DASHBOARD.WIDGET_EDITOR_DIALOG.SAVE:Save`);
|
|
1002
1278
|
labelCancel = t(() => $localize `:@@DASHBOARD.WIDGET_EDITOR_DIALOG.CANCEL:Cancel`);
|
|
1003
1279
|
labelPrevious = t(() => $localize `:@@DASHBOARD.WIDGET_EDITOR_DIALOG.PREVIOUS:Previous`);
|
|
@@ -1007,60 +1283,13 @@ class SiWidgetInstanceEditorDialogComponent {
|
|
|
1007
1283
|
labelDialogSave = t(() => $localize `:@@DASHBOARD.WIDGET_EDITOR_DIALOG.DISCARD_CONFIG_CHANGE_DIALOG.SAVE:Save`);
|
|
1008
1284
|
labelDialogDiscard = t(() => $localize `:@@DASHBOARD.WIDGET_EDITOR_DIALOG.DISCARD_CONFIG_CHANGE_DIALOG.DISCARD:Discard`);
|
|
1009
1285
|
labelDialogCancel = t(() => $localize `:@@DASHBOARD.WIDGET_EDITOR_DIALOG.DISCARD_CONFIG_CHANGE_DIALOG.CANCEL:Cancel`);
|
|
1010
|
-
/**
|
|
1011
|
-
* Marks the widget configuration as modified. Is set when widget editor instance
|
|
1012
|
-
* emits configChange events. Triggers edit discard confirmation dialog when widget config
|
|
1013
|
-
* is modified but not dialog is canceled.
|
|
1014
|
-
* */
|
|
1015
|
-
widgetConfigModified = signal(false);
|
|
1016
|
-
widgetInstanceEditor;
|
|
1017
|
-
editorWizardState = signal(undefined);
|
|
1018
|
-
subscriptions = [];
|
|
1019
|
-
injector = inject(Injector);
|
|
1020
|
-
envInjector = inject(EnvironmentInjector);
|
|
1021
1286
|
dialogService = inject(SiActionDialogService);
|
|
1022
1287
|
ngOnInit() {
|
|
1023
|
-
|
|
1024
|
-
this.
|
|
1025
|
-
if (isSignal(this.widgetInstanceEditor.config)) {
|
|
1026
|
-
componentRef.setInput('config', this.widgetConfig());
|
|
1027
|
-
}
|
|
1028
|
-
else {
|
|
1029
|
-
this.widgetInstanceEditor.config = this.widgetConfig();
|
|
1030
|
-
}
|
|
1031
|
-
// To be used by webcomponent wrapper
|
|
1032
|
-
if ('statusChangesHandler' in this.widgetInstanceEditor) {
|
|
1033
|
-
this.widgetInstanceEditor.statusChangesHandler = this.handleStatusChanges.bind(this);
|
|
1034
|
-
}
|
|
1035
|
-
let hasStatusChangesEmitter = false;
|
|
1036
|
-
if (this.widgetInstanceEditor.statusChanges) {
|
|
1037
|
-
hasStatusChangesEmitter = true;
|
|
1038
|
-
this.subscriptions.push(this.widgetInstanceEditor.statusChanges.subscribe(statusChanges => {
|
|
1039
|
-
this.handleStatusChanges(statusChanges);
|
|
1040
|
-
}));
|
|
1041
|
-
}
|
|
1042
|
-
if (this.widgetInstanceEditor.configChange) {
|
|
1043
|
-
this.subscriptions.push(this.widgetInstanceEditor.configChange.subscribe(config => {
|
|
1044
|
-
if (!hasStatusChangesEmitter) {
|
|
1045
|
-
this.invalidConfig.set(!!config.invalid);
|
|
1046
|
-
this.widgetConfigModified.set(true);
|
|
1047
|
-
}
|
|
1048
|
-
}));
|
|
1049
|
-
}
|
|
1050
|
-
if (this.isEditorWizdard(this.widgetInstanceEditor)) {
|
|
1051
|
-
this.editorWizardState.set(this.widgetInstanceEditor.state);
|
|
1052
|
-
if (this.widgetInstanceEditor.stateChange) {
|
|
1053
|
-
this.subscriptions.push(this.widgetInstanceEditor.stateChange.subscribe(state => {
|
|
1054
|
-
this.editorWizardState.set(state);
|
|
1055
|
-
}));
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1288
|
+
this.loadWidgetEditor(this.widget().componentFactory, this.editorHost()).subscribe(componentRef => {
|
|
1289
|
+
this.initializeEditor(componentRef, this.widgetConfig());
|
|
1058
1290
|
this.editorSetupCompleted.emit();
|
|
1059
1291
|
});
|
|
1060
1292
|
}
|
|
1061
|
-
ngOnDestroy() {
|
|
1062
|
-
this.tearDownEditor();
|
|
1063
|
-
}
|
|
1064
1293
|
onCancel() {
|
|
1065
1294
|
if (!this.widgetConfigModified()) {
|
|
1066
1295
|
this.closed.emit(undefined);
|
|
@@ -1089,13 +1318,13 @@ class SiWidgetInstanceEditorDialogComponent {
|
|
|
1089
1318
|
}
|
|
1090
1319
|
}
|
|
1091
1320
|
onNext() {
|
|
1092
|
-
if (this.
|
|
1321
|
+
if (this.isEditorWizard(this.widgetInstanceEditor)) {
|
|
1093
1322
|
this.widgetInstanceEditor.next();
|
|
1094
1323
|
this.editorWizardState.set(this.widgetInstanceEditor.state);
|
|
1095
1324
|
}
|
|
1096
1325
|
}
|
|
1097
1326
|
onPrevious() {
|
|
1098
|
-
if (this.
|
|
1327
|
+
if (this.isEditorWizard(this.widgetInstanceEditor) && this.editorWizardState()?.hasPrevious) {
|
|
1099
1328
|
this.widgetInstanceEditor.previous();
|
|
1100
1329
|
this.editorWizardState.set(this.widgetInstanceEditor.state);
|
|
1101
1330
|
}
|
|
@@ -1114,80 +1343,59 @@ class SiWidgetInstanceEditorDialogComponent {
|
|
|
1114
1343
|
}
|
|
1115
1344
|
this.closed.emit(this.widgetConfig());
|
|
1116
1345
|
}
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
this.widgetInstanceEditor = undefined;
|
|
1120
|
-
this.subscriptions.forEach(s => s.unsubscribe());
|
|
1121
|
-
this.subscriptions = [];
|
|
1122
|
-
}
|
|
1123
|
-
isEditorWizdard(editor) {
|
|
1124
|
-
return !!editor && 'state' in editor;
|
|
1125
|
-
}
|
|
1126
|
-
handleStatusChanges(statusChanges) {
|
|
1127
|
-
if (statusChanges.invalid !== undefined) {
|
|
1128
|
-
this.invalidConfig.set(statusChanges.invalid);
|
|
1129
|
-
}
|
|
1130
|
-
if (statusChanges.modified !== undefined) {
|
|
1131
|
-
this.widgetConfigModified.set(statusChanges.modified);
|
|
1132
|
-
}
|
|
1133
|
-
}
|
|
1134
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: SiWidgetInstanceEditorDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1135
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.6", type: SiWidgetInstanceEditorDialogComponent, isStandalone: true, selector: "si-widget-instance-editor-dialog", inputs: { widgetConfig: { classPropertyName: "widgetConfig", publicName: "widgetConfig", isSignal: true, isRequired: true, transformFunction: null }, widget: { classPropertyName: "widget", publicName: "widget", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { widgetConfig: "widgetConfigChange", closed: "closed", editorSetupCompleted: "editorSetupCompleted" }, viewQueries: [{ propertyName: "editorHost", first: true, predicate: ["editorHost"], descendants: true, read: ViewContainerRef, isSignal: true }], ngImport: i0, template: "<div class=\"si-layout-fixed-height mb-8 mx-0 overflow-auto px-2 pt-2\">\n <ng-container #editorHost />\n</div>\n\n<div class=\"editor-footer px-2\">\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"onCancel()\">{{\n labelCancel | translate\n }}</button>\n\n @if (showPreviousButton()) {\n <button\n type=\"button\"\n class=\"btn btn-secondary\"\n [disabled]=\"disablePreviousButton()\"\n (click)=\"onPrevious()\"\n >{{ labelPrevious | translate }}</button\n >\n }\n @if (showNextButton()) {\n <button\n type=\"button\"\n class=\"btn btn-secondary\"\n [disabled]=\"disableNextButton()\"\n (click)=\"onNext()\"\n >{{ labelNext | translate }}</button\n >\n }\n\n <button type=\"button\" class=\"btn btn-primary\" [disabled]=\"invalidConfig()\" (click)=\"onSave()\">{{\n labelSave | translate\n }}</button>\n</div>\n", styles: [":host{display:flex;flex:1 1 0;flex-direction:column;margin-inline:-4px;margin-block-start:-4px}.editor-footer{align-items:center;display:flex;flex-shrink:0;flex-wrap:wrap;justify-content:flex-end}.editor-footer>*{margin-inline-start:16px}\n"], dependencies: [{ kind: "ngmodule", type: SiTranslateModule }, { kind: "pipe", type: i1.SiTranslatePipe, name: "translate" }] });
|
|
1346
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SiWidgetInstanceEditorDialogComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
1347
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: SiWidgetInstanceEditorDialogComponent, isStandalone: true, selector: "si-widget-instance-editor-dialog", inputs: { widgetConfig: { classPropertyName: "widgetConfig", publicName: "widgetConfig", isSignal: true, isRequired: true, transformFunction: null }, widget: { classPropertyName: "widget", publicName: "widget", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { widgetConfig: "widgetConfigChange", closed: "closed", editorSetupCompleted: "editorSetupCompleted" }, usesInheritance: true, ngImport: i0, template: "<div class=\"si-layout-fixed-height mb-8 mx-0 overflow-auto px-2 pt-2\">\n <ng-container #editorHost />\n</div>\n\n<div class=\"editor-footer px-2\">\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"onCancel()\">{{\n labelCancel | translate\n }}</button>\n\n @if (showPreviousButton()) {\n <button\n type=\"button\"\n class=\"btn btn-secondary\"\n [disabled]=\"disablePreviousButton()\"\n (click)=\"onPrevious()\"\n >{{ labelPrevious | translate }}</button\n >\n }\n @if (showNextButton()) {\n <button\n type=\"button\"\n class=\"btn btn-secondary\"\n [disabled]=\"disableNextButton()\"\n (click)=\"onNext()\"\n >{{ labelNext | translate }}</button\n >\n }\n\n <button type=\"button\" class=\"btn btn-primary\" [disabled]=\"invalidConfig()\" (click)=\"onSave()\">{{\n labelSave | translate\n }}</button>\n</div>\n", styles: [":host{display:flex;flex:1 1 0;flex-direction:column;margin-inline:-4px;margin-block-start:-4px}.editor-footer{align-items:center;display:flex;flex-shrink:0;flex-wrap:wrap;justify-content:flex-end}.editor-footer>*{margin-inline-start:16px}\n"], dependencies: [{ kind: "pipe", type: SiTranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.Eager });
|
|
1136
1348
|
}
|
|
1137
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1349
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SiWidgetInstanceEditorDialogComponent, decorators: [{
|
|
1138
1350
|
type: Component,
|
|
1139
|
-
args: [{ selector: 'si-widget-instance-editor-dialog', imports: [
|
|
1140
|
-
}] });
|
|
1351
|
+
args: [{ selector: 'si-widget-instance-editor-dialog', imports: [SiTranslatePipe], changeDetection: ChangeDetectionStrategy.Eager, template: "<div class=\"si-layout-fixed-height mb-8 mx-0 overflow-auto px-2 pt-2\">\n <ng-container #editorHost />\n</div>\n\n<div class=\"editor-footer px-2\">\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"onCancel()\">{{\n labelCancel | translate\n }}</button>\n\n @if (showPreviousButton()) {\n <button\n type=\"button\"\n class=\"btn btn-secondary\"\n [disabled]=\"disablePreviousButton()\"\n (click)=\"onPrevious()\"\n >{{ labelPrevious | translate }}</button\n >\n }\n @if (showNextButton()) {\n <button\n type=\"button\"\n class=\"btn btn-secondary\"\n [disabled]=\"disableNextButton()\"\n (click)=\"onNext()\"\n >{{ labelNext | translate }}</button\n >\n }\n\n <button type=\"button\" class=\"btn btn-primary\" [disabled]=\"invalidConfig()\" (click)=\"onSave()\">{{\n labelSave | translate\n }}</button>\n</div>\n", styles: [":host{display:flex;flex:1 1 0;flex-direction:column;margin-inline:-4px;margin-block-start:-4px}.editor-footer{align-items:center;display:flex;flex-shrink:0;flex-wrap:wrap;justify-content:flex-end}.editor-footer>*{margin-inline-start:16px}\n"] }]
|
|
1352
|
+
}], propDecorators: { widgetConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "widgetConfig", required: true }] }, { type: i0.Output, args: ["widgetConfigChange"] }], widget: [{ type: i0.Input, args: [{ isSignal: true, alias: "widget", required: true }] }], closed: [{ type: i0.Output, args: ["closed"] }], editorSetupCompleted: [{ type: i0.Output, args: ["editorSetupCompleted"] }] } });
|
|
1141
1353
|
|
|
1142
1354
|
/**
|
|
1143
|
-
* Copyright (c) Siemens 2016 -
|
|
1355
|
+
* Copyright (c) Siemens 2016 - 2026
|
|
1144
1356
|
* SPDX-License-Identifier: MIT
|
|
1145
1357
|
*/
|
|
1146
|
-
const NEW_WIDGET_PREFIX = 'new-widget-';
|
|
1147
|
-
let idCounter = 1;
|
|
1148
1358
|
/**
|
|
1149
1359
|
* The grid component is the actual component on which the widget instances are placed and visualized. You can think of
|
|
1150
1360
|
* a headless dashboard, without a title, toolbar or edit buttons.
|
|
1151
1361
|
*/
|
|
1152
1362
|
class SiGridComponent {
|
|
1153
1363
|
storeSubscription;
|
|
1154
|
-
gridService = inject(SiGridService);
|
|
1155
1364
|
modalService = inject(SiModalService);
|
|
1156
1365
|
widgetStorage = inject(SI_WIDGET_STORE);
|
|
1366
|
+
widgetIdProvider = inject(SI_WIDGET_ID_PROVIDER);
|
|
1157
1367
|
/**
|
|
1158
1368
|
* Configuration options for a grid instance. Default is the optional
|
|
1159
1369
|
* value from the {@link SI_DASHBOARD_CONFIGURATION}.
|
|
1160
1370
|
*
|
|
1161
1371
|
* @defaultValue inject(SI_DASHBOARD_CONFIGURATION)?.grid
|
|
1162
1372
|
*/
|
|
1163
|
-
gridConfig = input(inject(SI_DASHBOARD_CONFIGURATION)?.grid
|
|
1373
|
+
gridConfig = input(inject(SI_DASHBOARD_CONFIGURATION)?.grid, /* @ts-ignore */
|
|
1374
|
+
...(ngDevMode ? [{ debugName: "gridConfig" }] : /* istanbul ignore next */ []));
|
|
1164
1375
|
/**
|
|
1165
1376
|
* Sets the grid into editable mode, in which the widget instances can be moved,
|
|
1166
1377
|
* resized, removed or new ones added.
|
|
1167
1378
|
*
|
|
1168
1379
|
* @defaultValue false
|
|
1169
1380
|
*/
|
|
1170
|
-
editable = model(false
|
|
1171
|
-
|
|
1172
|
-
* This is the internal owner of the current editable state and is used to track if
|
|
1173
|
-
* editable or not. Not editable can be changed by either calling the `edit()` api
|
|
1174
|
-
* method or by setting the `editable` input. When setting the input, the `ngOnChanges(...)`
|
|
1175
|
-
* hook is used to call the `edit()` method. Similar, to get from editable to not editable,
|
|
1176
|
-
* `cancel()` or `save()` is used and can be triggered from `ngOnChanges(...)`.
|
|
1177
|
-
*/
|
|
1178
|
-
editableInternal = false;
|
|
1381
|
+
editable = model(false, /* @ts-ignore */
|
|
1382
|
+
...(ngDevMode ? [{ debugName: "editable" }] : /* istanbul ignore next */ []));
|
|
1179
1383
|
/**
|
|
1180
1384
|
* An optional, but recommended dashboard id that is used in persistence and passed
|
|
1181
1385
|
* to the widget store for saving and loading data.
|
|
1182
1386
|
*/
|
|
1183
|
-
dashboardId = input(
|
|
1387
|
+
dashboardId = input(/* @ts-ignore */
|
|
1388
|
+
...(ngDevMode ? [undefined, { debugName: "dashboardId" }] : /* istanbul ignore next */ []));
|
|
1184
1389
|
/**
|
|
1185
1390
|
* Provides the available widgets to the grid. When loading the widget configurations from
|
|
1186
1391
|
* the storage, we need to have the widget definition available to be able to create the widget
|
|
1187
1392
|
* instances on the grid.
|
|
1188
1393
|
*
|
|
1189
1394
|
* @defaultValue [] */
|
|
1190
|
-
widgetCatalog = input([]
|
|
1395
|
+
widgetCatalog = input([], /* @ts-ignore */
|
|
1396
|
+
...(ngDevMode ? [{ debugName: "widgetCatalog" }] : /* istanbul ignore next */ []));
|
|
1397
|
+
widgetCatalogMap = computed(() => new Map(this.widgetCatalog().map(widget => [widget.id, widget])), /* @ts-ignore */
|
|
1398
|
+
...(ngDevMode ? [{ debugName: "widgetCatalogMap" }] : /* istanbul ignore next */ []));
|
|
1191
1399
|
/**
|
|
1192
1400
|
* When the user clicks edit on a widget instance, an editor need to appear and the
|
|
1193
1401
|
* widget editor component need to be loaded. When the grid is used standalone, it
|
|
@@ -1198,18 +1406,21 @@ class SiGridComponent {
|
|
|
1198
1406
|
* widget `edit` action.
|
|
1199
1407
|
*
|
|
1200
1408
|
* @defaultValue false */
|
|
1201
|
-
emitWidgetInstanceEditEvents = input(false
|
|
1409
|
+
emitWidgetInstanceEditEvents = input(false, /* @ts-ignore */
|
|
1410
|
+
...(ngDevMode ? [{ debugName: "emitWidgetInstanceEditEvents" }] : /* istanbul ignore next */ []));
|
|
1202
1411
|
/**
|
|
1203
1412
|
* Option to turn off the loading spinner on save and load operations.
|
|
1204
1413
|
*
|
|
1205
1414
|
* @defaultValue false
|
|
1206
1415
|
*/
|
|
1207
|
-
hideProgressIndicator = input(false
|
|
1416
|
+
hideProgressIndicator = input(false, /* @ts-ignore */
|
|
1417
|
+
...(ngDevMode ? [{ debugName: "hideProgressIndicator" }] : /* istanbul ignore next */ []));
|
|
1208
1418
|
/**
|
|
1209
1419
|
* Option to configure a custom widget instance editor dialog component. The component provides the
|
|
1210
1420
|
* editor hosting and the buttons to save and cancel.
|
|
1211
1421
|
*/
|
|
1212
|
-
widgetInstanceEditorDialogComponent = input(
|
|
1422
|
+
widgetInstanceEditorDialogComponent = input(/* @ts-ignore */
|
|
1423
|
+
...(ngDevMode ? [undefined, { debugName: "widgetInstanceEditorDialogComponent" }] : /* istanbul ignore next */ []));
|
|
1213
1424
|
/**
|
|
1214
1425
|
* Emits the modification state of the grid. It is `unmodified` when the visible state
|
|
1215
1426
|
* is equal to the loaded state from the widget storage. When the user modifies the dashboard by
|
|
@@ -1260,13 +1471,6 @@ class SiGridComponent {
|
|
|
1260
1471
|
markedForRemoval = [];
|
|
1261
1472
|
/** @defaultValue viewChild.required(SiGridstackWrapperComponent) */
|
|
1262
1473
|
gridStackWrapper = viewChild.required(SiGridstackWrapperComponent);
|
|
1263
|
-
/**
|
|
1264
|
-
* Service used to indicate load and save indication.
|
|
1265
|
-
* @deprecated Use `isLoading` instead.
|
|
1266
|
-
*
|
|
1267
|
-
* @defaultValue inject(SiLoadingService)
|
|
1268
|
-
*/
|
|
1269
|
-
loadingService = inject(SiLoadingService);
|
|
1270
1474
|
/**
|
|
1271
1475
|
* Indication for load and save operations.
|
|
1272
1476
|
*/
|
|
@@ -1276,43 +1480,34 @@ class SiGridComponent {
|
|
|
1276
1480
|
return !this.initialLoad && this.visibleWidgetInstances$.value.length === 0;
|
|
1277
1481
|
}
|
|
1278
1482
|
ngOnChanges(changes) {
|
|
1279
|
-
// Reload widgets if the dashboardId changes. Do not load on
|
|
1483
|
+
// Reload widgets if the dashboardId changes. Do not load on initial
|
|
1280
1484
|
// dashboardId property binding as first load will be done in ngOnInit()
|
|
1281
1485
|
if (changes.dashboardId && !changes.dashboardId.firstChange) {
|
|
1282
1486
|
queueMicrotask(() => this.loadAndSubscribeWidgets());
|
|
1283
1487
|
}
|
|
1284
1488
|
if (changes.editable) {
|
|
1285
1489
|
if (changes.editable.currentValue) {
|
|
1286
|
-
this.
|
|
1490
|
+
this.resetEditState();
|
|
1287
1491
|
}
|
|
1288
1492
|
else {
|
|
1289
|
-
this.
|
|
1493
|
+
this.restoreSavedState();
|
|
1290
1494
|
}
|
|
1291
1495
|
}
|
|
1292
|
-
if (changes.widgetCatalog) {
|
|
1293
|
-
this.gridService.widgetCatalog = this.widgetCatalog();
|
|
1294
|
-
}
|
|
1295
1496
|
}
|
|
1296
1497
|
ngOnInit() {
|
|
1297
1498
|
queueMicrotask(() => this.loadAndSubscribeWidgets());
|
|
1298
|
-
this.gridService.widgetCatalog = this.widgetCatalog();
|
|
1299
1499
|
}
|
|
1300
1500
|
ngOnDestroy() {
|
|
1301
1501
|
this.storeSubscription?.unsubscribe();
|
|
1302
1502
|
this.isLoading.complete();
|
|
1303
|
-
this.loadingService.counter.complete();
|
|
1304
1503
|
}
|
|
1305
1504
|
/**
|
|
1306
1505
|
* Set dashboard grid in editable mode to modify widget instances.
|
|
1307
1506
|
*/
|
|
1308
1507
|
edit() {
|
|
1309
|
-
if (!this.
|
|
1310
|
-
this.transientWidgetInstances = [];
|
|
1311
|
-
this.markedForRemoval = [];
|
|
1312
|
-
this.setModified(false);
|
|
1313
|
-
this.editableInternal = true;
|
|
1508
|
+
if (!this.editable()) {
|
|
1314
1509
|
this.editable.set(true);
|
|
1315
|
-
this.
|
|
1510
|
+
this.resetEditState();
|
|
1316
1511
|
}
|
|
1317
1512
|
}
|
|
1318
1513
|
/**
|
|
@@ -1320,30 +1515,27 @@ class SiGridComponent {
|
|
|
1320
1515
|
* changes the editable and isModified modes.
|
|
1321
1516
|
*/
|
|
1322
1517
|
save() {
|
|
1323
|
-
if (!this.
|
|
1518
|
+
if (!this.editable()) {
|
|
1324
1519
|
return;
|
|
1325
1520
|
}
|
|
1326
1521
|
this.isLoading.next(true);
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
const
|
|
1330
|
-
const
|
|
1522
|
+
// Update position information
|
|
1523
|
+
const widgets = this.updateWidgetPositions(this.visibleWidgetInstances$.value);
|
|
1524
|
+
const transientWidgetIds = new Set(this.transientWidgetInstances.map(w => w.id));
|
|
1525
|
+
const modifiedWidgets = widgets.filter(widget => !transientWidgetIds.has(widget.id));
|
|
1526
|
+
const addedWidgets = widgets.filter(widget => transientWidgetIds.has(widget.id));
|
|
1331
1527
|
this.widgetStorage
|
|
1332
|
-
.save(
|
|
1528
|
+
.save(modifiedWidgets, addedWidgets, this.markedForRemoval, this.dashboardId())
|
|
1333
1529
|
.pipe(take(1))
|
|
1334
1530
|
.subscribe({
|
|
1335
1531
|
next: (value) => {
|
|
1336
1532
|
this.setModified(false);
|
|
1337
|
-
this.editableInternal = false;
|
|
1338
1533
|
this.editable.set(false);
|
|
1339
|
-
this.gridService.editable$.next(this.editableInternal);
|
|
1340
1534
|
this.isLoading.next(false);
|
|
1341
|
-
this.loadingService.counter.next(0);
|
|
1342
1535
|
},
|
|
1343
1536
|
error: (err) => {
|
|
1344
1537
|
console.error('Saving dashboard configuration failed.', err);
|
|
1345
1538
|
this.isLoading.next(false);
|
|
1346
|
-
this.loadingService.counter.next(0);
|
|
1347
1539
|
}
|
|
1348
1540
|
});
|
|
1349
1541
|
}
|
|
@@ -1351,18 +1543,10 @@ class SiGridComponent {
|
|
|
1351
1543
|
* Cancel current changes and restore last saved state.
|
|
1352
1544
|
*/
|
|
1353
1545
|
cancel() {
|
|
1354
|
-
if (!this.editableInternal) {
|
|
1355
|
-
return;
|
|
1356
|
-
}
|
|
1357
|
-
if (this.modified) {
|
|
1358
|
-
this.visibleWidgetInstances$.next([...this.persistedWidgetInstances]);
|
|
1359
|
-
this.setModified(false);
|
|
1360
|
-
}
|
|
1361
|
-
this.editableInternal = false;
|
|
1362
1546
|
if (this.editable()) {
|
|
1363
1547
|
this.editable.set(false);
|
|
1548
|
+
this.restoreSavedState();
|
|
1364
1549
|
}
|
|
1365
|
-
this.gridService.editable$.next(this.editableInternal);
|
|
1366
1550
|
}
|
|
1367
1551
|
/**
|
|
1368
1552
|
* Adds a new widget instance configuration to the dashboard grid. It is not
|
|
@@ -1371,7 +1555,7 @@ class SiGridComponent {
|
|
|
1371
1555
|
* @param widgetInstanceConfig - The new widget configuration.
|
|
1372
1556
|
*/
|
|
1373
1557
|
addWidgetInstance(widgetInstanceConfig) {
|
|
1374
|
-
const id =
|
|
1558
|
+
const id = this.widgetIdProvider.generateWidgetId(widgetInstanceConfig, this.dashboardId());
|
|
1375
1559
|
const newWidget = { ...widgetInstanceConfig, id };
|
|
1376
1560
|
const nextWidgets = this.updateWidgetPositions([
|
|
1377
1561
|
...this.visibleWidgetInstances$.value,
|
|
@@ -1389,7 +1573,9 @@ class SiGridComponent {
|
|
|
1389
1573
|
removeWidgetInstance(widgetInstanceId) {
|
|
1390
1574
|
const widgetToRemove = this.visibleWidgetInstances$.value.find(widget => widget.id === widgetInstanceId);
|
|
1391
1575
|
if (widgetToRemove) {
|
|
1392
|
-
this.
|
|
1576
|
+
if (!this.transientWidgetInstances.some(widget => widget.id === widgetToRemove.id)) {
|
|
1577
|
+
this.markedForRemoval.push(widgetToRemove);
|
|
1578
|
+
}
|
|
1393
1579
|
let nextWidgets = this.visibleWidgetInstances$.value.filter(widget => widget.id !== widgetInstanceId);
|
|
1394
1580
|
nextWidgets = this.updateWidgetPositions(nextWidgets);
|
|
1395
1581
|
this.visibleWidgetInstances$.next(nextWidgets);
|
|
@@ -1404,12 +1590,12 @@ class SiGridComponent {
|
|
|
1404
1590
|
*/
|
|
1405
1591
|
editWidgetInstance(widgetInstanceConfig) {
|
|
1406
1592
|
// Need to edit a clone to avoid runtime editing
|
|
1407
|
-
const widgetConfigClone =
|
|
1593
|
+
const widgetConfigClone = structuredClone(widgetInstanceConfig);
|
|
1408
1594
|
if (this.emitWidgetInstanceEditEvents()) {
|
|
1409
1595
|
this.widgetInstanceEdit.emit(widgetConfigClone);
|
|
1410
1596
|
}
|
|
1411
1597
|
else {
|
|
1412
|
-
const widget = this.
|
|
1598
|
+
const widget = this.widgetCatalogMap().get(widgetConfigClone.widgetId);
|
|
1413
1599
|
const config = {
|
|
1414
1600
|
animated: true,
|
|
1415
1601
|
keyboard: true,
|
|
@@ -1449,6 +1635,14 @@ class SiGridComponent {
|
|
|
1449
1635
|
}
|
|
1450
1636
|
handleGridEvent(event) {
|
|
1451
1637
|
const relevantEventTypes = ['added', 'removed', 'dragstop', 'resizestop'];
|
|
1638
|
+
// When widgets without explicit x/y are first added, GridStack auto-positions them.
|
|
1639
|
+
// Capture these resolved positions so cancel() can restore the exact layout
|
|
1640
|
+
// instead of re-triggering auto-positioning which may produce a different result.
|
|
1641
|
+
if (event.event.type === 'added' &&
|
|
1642
|
+
!this.editable() &&
|
|
1643
|
+
this.persistedWidgetInstances.some(w => w.x === undefined || w.y === undefined)) {
|
|
1644
|
+
this.persistedWidgetInstances = this.updateWidgetPositions(this.persistedWidgetInstances);
|
|
1645
|
+
}
|
|
1452
1646
|
if (this.editable() && relevantEventTypes.includes(event.event.type)) {
|
|
1453
1647
|
// Make sure the widget config always holds the latest position information
|
|
1454
1648
|
const widgets = this.updateWidgetPositions(this.visibleWidgetInstances$.value);
|
|
@@ -1468,7 +1662,6 @@ class SiGridComponent {
|
|
|
1468
1662
|
// subscription. To handle this, we use the boolean marker `initialLoad`.
|
|
1469
1663
|
this.initialLoad = true;
|
|
1470
1664
|
this.isLoading.next(true);
|
|
1471
|
-
this.loadingService.counter.next(1);
|
|
1472
1665
|
this.storeSubscription = this.widgetStorage.load(this.dashboardId()).subscribe({
|
|
1473
1666
|
next: widgets => {
|
|
1474
1667
|
this.visibleWidgetInstances$.next(widgets);
|
|
@@ -1476,21 +1669,23 @@ class SiGridComponent {
|
|
|
1476
1669
|
if (this.initialLoad) {
|
|
1477
1670
|
this.initialLoad = false;
|
|
1478
1671
|
this.isLoading.next(false);
|
|
1479
|
-
this.loadingService.counter.next(0);
|
|
1480
1672
|
}
|
|
1481
1673
|
},
|
|
1482
1674
|
error: err => {
|
|
1483
1675
|
console.error('Loading dashboard configuration failed', err);
|
|
1484
1676
|
this.isLoading.next(false);
|
|
1485
|
-
this.loadingService.counter.next(0);
|
|
1486
1677
|
}
|
|
1487
1678
|
});
|
|
1488
1679
|
}
|
|
1489
1680
|
updateWidgetPositions(widgetConfigs) {
|
|
1490
|
-
const
|
|
1681
|
+
const wrapper = this.gridStackWrapper();
|
|
1491
1682
|
const widgets = widgetConfigs.map(widget => {
|
|
1492
|
-
const position =
|
|
1493
|
-
if (position
|
|
1683
|
+
const position = wrapper.getWidgetLayout(widget.id);
|
|
1684
|
+
if (position &&
|
|
1685
|
+
(widget.x !== position.x ||
|
|
1686
|
+
widget.y !== position.y ||
|
|
1687
|
+
widget.width !== position.width ||
|
|
1688
|
+
widget.height !== position.height)) {
|
|
1494
1689
|
return {
|
|
1495
1690
|
...widget,
|
|
1496
1691
|
x: position.x,
|
|
@@ -1505,19 +1700,30 @@ class SiGridComponent {
|
|
|
1505
1700
|
});
|
|
1506
1701
|
return widgets || [];
|
|
1507
1702
|
}
|
|
1703
|
+
resetEditState() {
|
|
1704
|
+
this.transientWidgetInstances = [];
|
|
1705
|
+
this.markedForRemoval = [];
|
|
1706
|
+
this.setModified(false);
|
|
1707
|
+
}
|
|
1708
|
+
restoreSavedState() {
|
|
1709
|
+
if (this.modified) {
|
|
1710
|
+
this.visibleWidgetInstances$.next([...this.persistedWidgetInstances]);
|
|
1711
|
+
this.setModified(false);
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1508
1714
|
setModified(modified) {
|
|
1509
1715
|
if (this.modified !== modified) {
|
|
1510
1716
|
this.modified = modified;
|
|
1511
1717
|
this.isModified.emit(this.modified);
|
|
1512
1718
|
}
|
|
1513
1719
|
}
|
|
1514
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1515
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
1720
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SiGridComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1721
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: SiGridComponent, isStandalone: true, selector: "si-grid", inputs: { gridConfig: { classPropertyName: "gridConfig", publicName: "gridConfig", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, dashboardId: { classPropertyName: "dashboardId", publicName: "dashboardId", isSignal: true, isRequired: false, transformFunction: null }, widgetCatalog: { classPropertyName: "widgetCatalog", publicName: "widgetCatalog", isSignal: true, isRequired: false, transformFunction: null }, emitWidgetInstanceEditEvents: { classPropertyName: "emitWidgetInstanceEditEvents", publicName: "emitWidgetInstanceEditEvents", isSignal: true, isRequired: false, transformFunction: null }, hideProgressIndicator: { classPropertyName: "hideProgressIndicator", publicName: "hideProgressIndicator", isSignal: true, isRequired: false, transformFunction: null }, widgetInstanceEditorDialogComponent: { classPropertyName: "widgetInstanceEditorDialogComponent", publicName: "widgetInstanceEditorDialogComponent", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { editable: "editableChange", isModified: "isModified", widgetInstanceEdit: "widgetInstanceEdit" }, viewQueries: [{ propertyName: "gridStackWrapper", first: true, predicate: SiGridstackWrapperComponent, descendants: true, isSignal: true }], usesOnChanges: true, ngImport: i0, template: "@if (showEmptyState) {\n <ng-content select=\"[empty-state]\" />\n}\n<si-gridstack-wrapper\n [widgetConfigs]=\"(visibleWidgetInstances$ | async) ?? []\"\n [gridConfig]=\"gridConfig()\"\n [editable]=\"editable()\"\n [widgetCatalogMap]=\"widgetCatalogMap()\"\n [siLoading]=\"!hideProgressIndicator() && initialLoad && (isLoading | async)!\"\n [blocking]=\"false\"\n (gridEvent)=\"handleGridEvent($event)\"\n (widgetInstanceRemove)=\"removeWidgetInstance($event)\"\n (widgetInstanceEdit)=\"editWidgetInstance($event)\"\n/>\n", styles: [":host{display:flex;flex:1 1 0;flex-direction:column}::ng-deep .grid-stack-animate,::ng-deep .grid-stack-animate .grid-stack-item{transition-property:inset-inline-start,inset-block-start}\n"], dependencies: [{ kind: "component", type: SiGridstackWrapperComponent, selector: "si-gridstack-wrapper", inputs: ["widgetConfigs", "editable", "gridConfig", "widgetCatalogMap"], outputs: ["gridEvent", "widgetInstanceRemove", "widgetInstanceEdit"] }, { kind: "directive", type: SiLoadingSpinnerDirective, selector: "[siLoading]", inputs: ["siLoading", "blocking", "initialDelay"] }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.Eager });
|
|
1516
1722
|
}
|
|
1517
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1723
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SiGridComponent, decorators: [{
|
|
1518
1724
|
type: Component,
|
|
1519
|
-
args: [{ selector: 'si-grid', imports: [SiGridstackWrapperComponent, SiLoadingSpinnerDirective, AsyncPipe],
|
|
1520
|
-
}] });
|
|
1725
|
+
args: [{ selector: 'si-grid', imports: [SiGridstackWrapperComponent, SiLoadingSpinnerDirective, AsyncPipe], changeDetection: ChangeDetectionStrategy.Eager, template: "@if (showEmptyState) {\n <ng-content select=\"[empty-state]\" />\n}\n<si-gridstack-wrapper\n [widgetConfigs]=\"(visibleWidgetInstances$ | async) ?? []\"\n [gridConfig]=\"gridConfig()\"\n [editable]=\"editable()\"\n [widgetCatalogMap]=\"widgetCatalogMap()\"\n [siLoading]=\"!hideProgressIndicator() && initialLoad && (isLoading | async)!\"\n [blocking]=\"false\"\n (gridEvent)=\"handleGridEvent($event)\"\n (widgetInstanceRemove)=\"removeWidgetInstance($event)\"\n (widgetInstanceEdit)=\"editWidgetInstance($event)\"\n/>\n", styles: [":host{display:flex;flex:1 1 0;flex-direction:column}::ng-deep .grid-stack-animate,::ng-deep .grid-stack-animate .grid-stack-item{transition-property:inset-inline-start,inset-block-start}\n"] }]
|
|
1726
|
+
}], propDecorators: { gridConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "gridConfig", required: false }] }], editable: [{ type: i0.Input, args: [{ isSignal: true, alias: "editable", required: false }] }, { type: i0.Output, args: ["editableChange"] }], dashboardId: [{ type: i0.Input, args: [{ isSignal: true, alias: "dashboardId", required: false }] }], widgetCatalog: [{ type: i0.Input, args: [{ isSignal: true, alias: "widgetCatalog", required: false }] }], emitWidgetInstanceEditEvents: [{ type: i0.Input, args: [{ isSignal: true, alias: "emitWidgetInstanceEditEvents", required: false }] }], hideProgressIndicator: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideProgressIndicator", required: false }] }], widgetInstanceEditorDialogComponent: [{ type: i0.Input, args: [{ isSignal: true, alias: "widgetInstanceEditorDialogComponent", required: false }] }], isModified: [{ type: i0.Output, args: ["isModified"] }], widgetInstanceEdit: [{ type: i0.Output, args: ["widgetInstanceEdit"] }], gridStackWrapper: [{ type: i0.ViewChild, args: [i0.forwardRef(() => SiGridstackWrapperComponent), { isSignal: true }] }] } });
|
|
1521
1727
|
|
|
1522
1728
|
/**
|
|
1523
1729
|
* Function creates a new {@link WidgetConfig} without id from a {@link Widget} and
|
|
@@ -1538,7 +1744,7 @@ const createWidgetConfig = (widget) => {
|
|
|
1538
1744
|
};
|
|
1539
1745
|
|
|
1540
1746
|
/**
|
|
1541
|
-
* Copyright (c) Siemens 2016 -
|
|
1747
|
+
* Copyright (c) Siemens 2016 - 2026
|
|
1542
1748
|
* SPDX-License-Identifier: MIT
|
|
1543
1749
|
*/
|
|
1544
1750
|
/**
|
|
@@ -1547,7 +1753,7 @@ const createWidgetConfig = (widget) => {
|
|
|
1547
1753
|
* selection, a host in which the widget specific editor is loaded. Applications can either
|
|
1548
1754
|
* stay with the default catalog or implement their own by extending this class.
|
|
1549
1755
|
*/
|
|
1550
|
-
class SiWidgetCatalogComponent {
|
|
1756
|
+
class SiWidgetCatalogComponent extends SiWidgetEditorBase {
|
|
1551
1757
|
/**
|
|
1552
1758
|
* Placeholder text for the search input field in the widget catalog.
|
|
1553
1759
|
*
|
|
@@ -1556,7 +1762,8 @@ class SiWidgetCatalogComponent {
|
|
|
1556
1762
|
* t(() => $localize`:@@DASHBOARD.WIDGET_LIBRARY.SEARCH_PLACEHOLDER:Search widget`)
|
|
1557
1763
|
* ```
|
|
1558
1764
|
*/
|
|
1559
|
-
searchPlaceholder = input(t(() => $localize `:@@DASHBOARD.WIDGET_LIBRARY.SEARCH_PLACEHOLDER:Search widget`)
|
|
1765
|
+
searchPlaceholder = input(t(() => $localize `:@@DASHBOARD.WIDGET_LIBRARY.SEARCH_PLACEHOLDER:Search widget`), /* @ts-ignore */
|
|
1766
|
+
...(ngDevMode ? [{ debugName: "searchPlaceholder" }] : /* istanbul ignore next */ []));
|
|
1560
1767
|
/**
|
|
1561
1768
|
* Emits when the catalog is `closed`, either by canceling or by adding or saving
|
|
1562
1769
|
* a widget configuration. On cancel `undefined` is emitted, otherwise the related
|
|
@@ -1569,8 +1776,8 @@ class SiWidgetCatalogComponent {
|
|
|
1569
1776
|
* @internal
|
|
1570
1777
|
* @defaultValue 'list'
|
|
1571
1778
|
*/
|
|
1572
|
-
view = signal('list'
|
|
1573
|
-
|
|
1779
|
+
view = signal('list', /* @ts-ignore */
|
|
1780
|
+
...(ngDevMode ? [{ debugName: "view" }] : /* istanbul ignore next */ []));
|
|
1574
1781
|
/**
|
|
1575
1782
|
* Property to provide the available widgets to the catalog. The flexible
|
|
1576
1783
|
* dashboard creates the catalog by Angular's `createComponent()` method
|
|
@@ -1587,9 +1794,12 @@ class SiWidgetCatalogComponent {
|
|
|
1587
1794
|
* Array used to hold the search result on the widget catalog.
|
|
1588
1795
|
* @defaultValue [] */
|
|
1589
1796
|
filteredWidgetCatalog = [];
|
|
1590
|
-
selected = signal(undefined
|
|
1797
|
+
selected = signal(undefined, /* @ts-ignore */
|
|
1798
|
+
...(ngDevMode ? [{ debugName: "selected" }] : /* istanbul ignore next */ []));
|
|
1591
1799
|
widgetConfig;
|
|
1592
|
-
hasEditor =
|
|
1800
|
+
hasEditor = computed(() => !!this.selected()?.componentFactory.editorComponentName, /* @ts-ignore */
|
|
1801
|
+
...(ngDevMode ? [{ debugName: "hasEditor" }] : /* istanbul ignore next */ []));
|
|
1802
|
+
translateService = injectSiTranslateService();
|
|
1593
1803
|
labelCancel = t(() => $localize `:@@DASHBOARD.WIDGET_LIBRARY.CANCEL:Cancel`);
|
|
1594
1804
|
labelPrevious = t(() => $localize `:@@DASHBOARD.WIDGET_LIBRARY.PREVIOUS:Previous`);
|
|
1595
1805
|
labelNext = t(() => $localize `:@@DASHBOARD.WIDGET_LIBRARY.NEXT:Next`);
|
|
@@ -1599,10 +1809,15 @@ class SiWidgetCatalogComponent {
|
|
|
1599
1809
|
labelDialogCancel = t(() => $localize `:@@DASHBOARD.WIDGET_LIBRARY.DISCARD_CONFIG_CHANGE_DIALOG.CANCEL:Cancel`);
|
|
1600
1810
|
labelDialogMessage = t(() => $localize `:@@DASHBOARD.WIDGET_LIBRARY.DISCARD_CONFIG_CHANGE_DIALOG.MESSAGE:The widget configuration changed. Do you want to discard the changes?`);
|
|
1601
1811
|
labelDialogDiscard = t(() => $localize `:@@DASHBOARD.WIDGET_LIBRARY.DISCARD_CONFIG_CHANGE_DIALOG.DISCARD:Discard`);
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1812
|
+
labelWidgetCatalogList = t(() => $localize `:@@DASHBOARD.WIDGET_LIBRARY.WIDGET_CATALOG_LIST:Widget catalog list`);
|
|
1813
|
+
showAddButton = computed(() => this.view() === 'list' ? !this.hasEditor() : true, /* @ts-ignore */
|
|
1814
|
+
...(ngDevMode ? [{ debugName: "showAddButton" }] : /* istanbul ignore next */ []));
|
|
1815
|
+
showNextButton = computed(() => this.view() === 'list' ? this.hasEditor() : this.editorWizardState() !== undefined, /* @ts-ignore */
|
|
1816
|
+
...(ngDevMode ? [{ debugName: "showNextButton" }] : /* istanbul ignore next */ []));
|
|
1817
|
+
showPreviousButton = computed(() => this.view() === 'editor', /* @ts-ignore */
|
|
1818
|
+
...(ngDevMode ? [{ debugName: "showPreviousButton" }] : /* istanbul ignore next */ []));
|
|
1819
|
+
disableAddButton = computed(() => !this.selected() || this.invalidConfig(), /* @ts-ignore */
|
|
1820
|
+
...(ngDevMode ? [{ debugName: "disableAddButton" }] : /* istanbul ignore next */ []));
|
|
1606
1821
|
disableNextButton = computed(() => {
|
|
1607
1822
|
const wizardState = this.editorWizardState();
|
|
1608
1823
|
if (this.view() === 'list') {
|
|
@@ -1620,31 +1835,17 @@ class SiWidgetCatalogComponent {
|
|
|
1620
1835
|
else {
|
|
1621
1836
|
return false;
|
|
1622
1837
|
}
|
|
1623
|
-
}
|
|
1624
|
-
|
|
1625
|
-
invalidConfig = signal(false);
|
|
1626
|
-
/**
|
|
1627
|
-
* Marks the widget configuration as modified. Is set when widget editor instance
|
|
1628
|
-
* emits configChange events. Triggers edit discard confirmation dialog when widget config
|
|
1629
|
-
* is modified but not added to the dashboard.
|
|
1630
|
-
* */
|
|
1631
|
-
widgetConfigModified = false;
|
|
1632
|
-
widgetInstanceEditor;
|
|
1633
|
-
editorWizardState = signal(undefined);
|
|
1634
|
-
widgetInstanceEditorRef;
|
|
1635
|
-
subscriptions = [];
|
|
1838
|
+
}, /* @ts-ignore */
|
|
1839
|
+
...(ngDevMode ? [{ debugName: "disableNextButton" }] : /* istanbul ignore next */ []));
|
|
1636
1840
|
dialogService = inject(SiActionDialogService);
|
|
1637
|
-
|
|
1638
|
-
|
|
1841
|
+
widgetCdkListbox = viewChild((CdkListbox), /* @ts-ignore */
|
|
1842
|
+
...(ngDevMode ? [{ debugName: "widgetCdkListbox" }] : /* istanbul ignore next */ []));
|
|
1639
1843
|
ngOnInit() {
|
|
1640
1844
|
this.filteredWidgetCatalog = this.widgetCatalog;
|
|
1641
1845
|
if (this.widgetCatalog.length > 0) {
|
|
1642
1846
|
this.selectWidget(this.widgetCatalog[0]);
|
|
1643
1847
|
}
|
|
1644
1848
|
}
|
|
1645
|
-
ngOnDestroy() {
|
|
1646
|
-
this.tearDownEditor();
|
|
1647
|
-
}
|
|
1648
1849
|
onSearch(searchTerm) {
|
|
1649
1850
|
if (!searchTerm || searchTerm.trim().length === 0) {
|
|
1650
1851
|
this.searchTerm = '';
|
|
@@ -1652,7 +1853,11 @@ class SiWidgetCatalogComponent {
|
|
|
1652
1853
|
}
|
|
1653
1854
|
else {
|
|
1654
1855
|
this.searchTerm = searchTerm;
|
|
1655
|
-
|
|
1856
|
+
const term = searchTerm.trim().toLowerCase();
|
|
1857
|
+
this.filteredWidgetCatalog = this.widgetCatalog.filter(wd => {
|
|
1858
|
+
const name = this.translateService.translateSync(wd.name);
|
|
1859
|
+
return name.toLowerCase().includes(term);
|
|
1860
|
+
});
|
|
1656
1861
|
}
|
|
1657
1862
|
if (this.filteredWidgetCatalog.length > 0) {
|
|
1658
1863
|
this.selectWidget(this.filteredWidgetCatalog[0]);
|
|
@@ -1662,7 +1867,7 @@ class SiWidgetCatalogComponent {
|
|
|
1662
1867
|
}
|
|
1663
1868
|
}
|
|
1664
1869
|
onCancel() {
|
|
1665
|
-
if (!this.widgetConfigModified) {
|
|
1870
|
+
if (!this.widgetConfigModified()) {
|
|
1666
1871
|
this.closed.emit(undefined);
|
|
1667
1872
|
}
|
|
1668
1873
|
else {
|
|
@@ -1687,18 +1892,18 @@ class SiWidgetCatalogComponent {
|
|
|
1687
1892
|
this.setupWidgetInstanceEditor();
|
|
1688
1893
|
}
|
|
1689
1894
|
else {
|
|
1690
|
-
if (this.
|
|
1895
|
+
if (this.isEditorWizard(this.widgetInstanceEditor)) {
|
|
1691
1896
|
this.widgetInstanceEditor.next();
|
|
1692
1897
|
this.editorWizardState.set(this.widgetInstanceEditor.state);
|
|
1693
1898
|
}
|
|
1694
1899
|
}
|
|
1695
1900
|
}
|
|
1696
1901
|
onPrevious() {
|
|
1697
|
-
if (this.
|
|
1902
|
+
if (this.isEditorWizard(this.widgetInstanceEditor) && this.editorWizardState()?.hasPrevious) {
|
|
1698
1903
|
this.widgetInstanceEditor.previous();
|
|
1699
1904
|
this.editorWizardState.set(this.widgetInstanceEditor.state);
|
|
1700
1905
|
}
|
|
1701
|
-
else if (!this.widgetConfigModified) {
|
|
1906
|
+
else if (!this.widgetConfigModified()) {
|
|
1702
1907
|
this.setupCatalog();
|
|
1703
1908
|
}
|
|
1704
1909
|
else {
|
|
@@ -1726,61 +1931,17 @@ class SiWidgetCatalogComponent {
|
|
|
1726
1931
|
this.tearDownEditor();
|
|
1727
1932
|
this.view.set('editor');
|
|
1728
1933
|
this.widgetConfig = createWidgetConfig(selected);
|
|
1729
|
-
|
|
1934
|
+
this.loadWidgetEditor(selected.componentFactory, this.editorHost()).subscribe({
|
|
1730
1935
|
next: componentRef => {
|
|
1731
|
-
this.
|
|
1732
|
-
this.widgetInstanceEditor = componentRef.instance;
|
|
1733
|
-
if (isSignal(this.widgetInstanceEditor.config)) {
|
|
1734
|
-
this.widgetInstanceEditorRef.setInput('config', this.widgetConfig);
|
|
1735
|
-
}
|
|
1736
|
-
else {
|
|
1737
|
-
this.widgetInstanceEditor.config = this.widgetConfig;
|
|
1738
|
-
}
|
|
1739
|
-
// To be used by webcomponent wrapper
|
|
1740
|
-
if ('statusChangesHandler' in this.widgetInstanceEditor) {
|
|
1741
|
-
this.widgetInstanceEditor.statusChangesHandler = this.handleStatusChanges.bind(this);
|
|
1742
|
-
}
|
|
1743
|
-
let hasStatusChangesEmitter = false;
|
|
1744
|
-
if (this.widgetInstanceEditor.statusChanges) {
|
|
1745
|
-
hasStatusChangesEmitter = true;
|
|
1746
|
-
this.subscriptions.push(this.widgetInstanceEditor.statusChanges.subscribe(statusChanges => {
|
|
1747
|
-
this.handleStatusChanges(statusChanges);
|
|
1748
|
-
}));
|
|
1749
|
-
}
|
|
1750
|
-
if (this.widgetInstanceEditor.configChange) {
|
|
1751
|
-
this.subscriptions.push(this.widgetInstanceEditor.configChange.subscribe(config => {
|
|
1752
|
-
if (!hasStatusChangesEmitter) {
|
|
1753
|
-
this.invalidConfig.set(!!config.invalid);
|
|
1754
|
-
this.widgetConfigModified = true;
|
|
1755
|
-
}
|
|
1756
|
-
}));
|
|
1757
|
-
}
|
|
1758
|
-
if (this.isEditorWizdard(this.widgetInstanceEditor)) {
|
|
1759
|
-
this.editorWizardState.set(this.widgetInstanceEditor.state);
|
|
1760
|
-
if (this.widgetInstanceEditor.stateChange) {
|
|
1761
|
-
this.subscriptions.push(this.widgetInstanceEditor.stateChange.subscribe(state => {
|
|
1762
|
-
this.editorWizardState.set(state);
|
|
1763
|
-
}));
|
|
1764
|
-
}
|
|
1765
|
-
}
|
|
1936
|
+
this.initializeEditor(componentRef, this.widgetConfig);
|
|
1766
1937
|
},
|
|
1767
1938
|
error: error => {
|
|
1768
1939
|
console.error(error);
|
|
1769
1940
|
}
|
|
1770
1941
|
});
|
|
1771
1942
|
}
|
|
1772
|
-
tearDownEditor() {
|
|
1773
|
-
this.invalidConfig.set(false);
|
|
1774
|
-
this.widgetConfigModified = false;
|
|
1775
|
-
this.editorWizardState.set(undefined);
|
|
1776
|
-
this.widgetInstanceEditor = undefined;
|
|
1777
|
-
this.subscriptions.forEach(s => s.unsubscribe());
|
|
1778
|
-
this.subscriptions = [];
|
|
1779
|
-
}
|
|
1780
1943
|
setupCatalog() {
|
|
1781
|
-
|
|
1782
|
-
this.editorHost().remove(0);
|
|
1783
|
-
}
|
|
1944
|
+
this.editorHost().clear();
|
|
1784
1945
|
this.tearDownEditor();
|
|
1785
1946
|
this.widgetConfig = undefined;
|
|
1786
1947
|
this.view.set('list');
|
|
@@ -1806,34 +1967,30 @@ class SiWidgetCatalogComponent {
|
|
|
1806
1967
|
}
|
|
1807
1968
|
selectWidget(widget) {
|
|
1808
1969
|
this.selected.set(widget);
|
|
1809
|
-
if (widget
|
|
1810
|
-
this
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
}
|
|
1815
|
-
}
|
|
1816
|
-
isEditorWizdard(editor) {
|
|
1817
|
-
return !!editor && 'state' in editor;
|
|
1818
|
-
}
|
|
1819
|
-
handleStatusChanges(statusChanges) {
|
|
1820
|
-
if (statusChanges.invalid !== undefined) {
|
|
1821
|
-
this.invalidConfig.set(statusChanges.invalid);
|
|
1822
|
-
}
|
|
1823
|
-
if (statusChanges.modified !== undefined) {
|
|
1824
|
-
this.widgetConfigModified = statusChanges.modified;
|
|
1970
|
+
if (widget) {
|
|
1971
|
+
// need to keep this in setTimeout to avoid ExpressionChangedAfterItHasBeenCheckedError
|
|
1972
|
+
setTimeout(() => {
|
|
1973
|
+
this.widgetCdkListbox()?.selectValue(widget);
|
|
1974
|
+
});
|
|
1825
1975
|
}
|
|
1826
1976
|
}
|
|
1827
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1828
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
1977
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SiWidgetCatalogComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
1978
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: SiWidgetCatalogComponent, isStandalone: true, selector: "si-widget-catalog", inputs: { searchPlaceholder: { classPropertyName: "searchPlaceholder", publicName: "searchPlaceholder", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed" }, viewQueries: [{ propertyName: "widgetCdkListbox", first: true, predicate: (CdkListbox), descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"si-layout-fixed-height mb-8 mx-0 overflow-auto px-2 pt-2\">\n @if (view() === 'list') {\n <div class=\"si-layout-fixed-height\">\n <div class=\"mx-0\">\n <si-search-bar\n class=\"w-100\"\n colorVariant=\"base-1\"\n showIcon=\"true\"\n [placeholder]=\"searchPlaceholder() | translate\"\n [value]=\"searchTerm\"\n (searchChange)=\"onSearch($event)\"\n />\n </div>\n <div class=\"si-layout-fixed-height text-pre-wrap text-break mt-6 bg-base-1 rounded-2\">\n @if (filteredWidgetCatalog.length > 0) {\n <span id=\"widget-catalog-list-description\" class=\"visually-hidden\">\n {{ labelWidgetCatalogList | translate }}\n </span>\n <ul\n class=\"si-layout-fixed-height list-group overflow-auto mb-0\"\n aria-labelledby=\"widget-catalog-list-description\"\n cdkListbox\n [cdkListboxValue]=\"[]\"\n (cdkListboxValueChange)=\"selectWidget($event.value[0])\"\n >\n @for (widget of filteredWidgetCatalog; track $index) {\n <li\n class=\"list-group-item list-group-item-action d-flex\"\n [cdkOption]=\"widget\"\n [class.active]=\"selected() === widget\"\n >\n <si-circle-status class=\"my-n4 me-5\" [icon]=\"widget.iconClass\" />\n <div class=\"d-flex flex-column align-items-start align-self-center\">\n <span class=\"si-h5\">{{ widget.name | translate }}</span>\n <span class=\"si-body\">{{ widget.description?.trim() | translate }}</span>\n </div>\n </li>\n }\n </ul>\n } @else {\n <div>\n <si-empty-state icon=\"element-report\" [heading]=\"labelEmpty\" />\n </div>\n }\n </div>\n </div>\n }\n <ng-container #editorHost />\n</div>\n\n<div class=\"catalog-footer pb-4 px-2\">\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"onCancel()\">{{\n labelCancel | translate\n }}</button>\n @if (showPreviousButton()) {\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"onPrevious()\">{{\n labelPrevious | translate\n }}</button>\n }\n @if (showNextButton()) {\n <button\n type=\"button\"\n class=\"btn\"\n [class.btn-primary]=\"view() === 'list'\"\n [class.btn-secondary]=\"view() !== 'list'\"\n [disabled]=\"disableNextButton()\"\n (click)=\"onNext()\"\n >{{ labelNext | translate }}</button\n >\n }\n @if (showAddButton()) {\n <button\n type=\"button\"\n class=\"btn btn-primary\"\n [disabled]=\"disableAddButton()\"\n (click)=\"onAddWidget()\"\n >{{ labelAdd | translate }}</button\n >\n }\n</div>\n", styles: [":host{display:flex;flex:1 1 0;flex-direction:column;margin-inline:-4px;margin-block-start:-4px}.list-group-item{cursor:pointer}.catalog-footer{align-items:center;display:flex;flex-shrink:0;flex-wrap:wrap;justify-content:flex-end}.catalog-footer>*{margin-inline-start:16px}\n"], dependencies: [{ kind: "component", type: SiSearchBarComponent, selector: "si-search-bar", inputs: ["debounceTime", "prohibitedCharacters", "placeholder", "showIcon", "tabbable", "value", "readonly", "colorVariant", "disabled", "maxlength", "clearButtonAriaLabel"], outputs: ["searchChange"] }, { kind: "component", type: SiCircleStatusComponent, selector: "si-circle-status", inputs: ["status", "color", "icon", "size", "eventOut", "eventIcon", "blink", "blinkPulse", "ariaLabel"] }, { kind: "component", type: SiEmptyStateComponent, selector: "si-empty-state", inputs: ["icon", "heading", "content"] }, { kind: "directive", type: CdkListbox, selector: "[cdkListbox]", inputs: ["id", "tabindex", "cdkListboxValue", "cdkListboxMultiple", "cdkListboxDisabled", "cdkListboxUseActiveDescendant", "cdkListboxOrientation", "cdkListboxCompareWith", "cdkListboxNavigationWrapDisabled", "cdkListboxNavigatesDisabledOptions"], outputs: ["cdkListboxValueChange"], exportAs: ["cdkListbox"] }, { kind: "directive", type: CdkOption, selector: "[cdkOption]", inputs: ["id", "cdkOption", "cdkOptionTypeaheadLabel", "cdkOptionDisabled", "tabindex"], exportAs: ["cdkOption"] }, { kind: "pipe", type: SiTranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.Eager });
|
|
1829
1979
|
}
|
|
1830
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1980
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SiWidgetCatalogComponent, decorators: [{
|
|
1831
1981
|
type: Component,
|
|
1832
|
-
args: [{ selector: 'si-widget-catalog', imports: [
|
|
1833
|
-
|
|
1982
|
+
args: [{ selector: 'si-widget-catalog', imports: [
|
|
1983
|
+
SiSearchBarComponent,
|
|
1984
|
+
SiCircleStatusComponent,
|
|
1985
|
+
SiEmptyStateComponent,
|
|
1986
|
+
SiTranslatePipe,
|
|
1987
|
+
CdkListbox,
|
|
1988
|
+
CdkOption
|
|
1989
|
+
], changeDetection: ChangeDetectionStrategy.Eager, template: "<div class=\"si-layout-fixed-height mb-8 mx-0 overflow-auto px-2 pt-2\">\n @if (view() === 'list') {\n <div class=\"si-layout-fixed-height\">\n <div class=\"mx-0\">\n <si-search-bar\n class=\"w-100\"\n colorVariant=\"base-1\"\n showIcon=\"true\"\n [placeholder]=\"searchPlaceholder() | translate\"\n [value]=\"searchTerm\"\n (searchChange)=\"onSearch($event)\"\n />\n </div>\n <div class=\"si-layout-fixed-height text-pre-wrap text-break mt-6 bg-base-1 rounded-2\">\n @if (filteredWidgetCatalog.length > 0) {\n <span id=\"widget-catalog-list-description\" class=\"visually-hidden\">\n {{ labelWidgetCatalogList | translate }}\n </span>\n <ul\n class=\"si-layout-fixed-height list-group overflow-auto mb-0\"\n aria-labelledby=\"widget-catalog-list-description\"\n cdkListbox\n [cdkListboxValue]=\"[]\"\n (cdkListboxValueChange)=\"selectWidget($event.value[0])\"\n >\n @for (widget of filteredWidgetCatalog; track $index) {\n <li\n class=\"list-group-item list-group-item-action d-flex\"\n [cdkOption]=\"widget\"\n [class.active]=\"selected() === widget\"\n >\n <si-circle-status class=\"my-n4 me-5\" [icon]=\"widget.iconClass\" />\n <div class=\"d-flex flex-column align-items-start align-self-center\">\n <span class=\"si-h5\">{{ widget.name | translate }}</span>\n <span class=\"si-body\">{{ widget.description?.trim() | translate }}</span>\n </div>\n </li>\n }\n </ul>\n } @else {\n <div>\n <si-empty-state icon=\"element-report\" [heading]=\"labelEmpty\" />\n </div>\n }\n </div>\n </div>\n }\n <ng-container #editorHost />\n</div>\n\n<div class=\"catalog-footer pb-4 px-2\">\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"onCancel()\">{{\n labelCancel | translate\n }}</button>\n @if (showPreviousButton()) {\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"onPrevious()\">{{\n labelPrevious | translate\n }}</button>\n }\n @if (showNextButton()) {\n <button\n type=\"button\"\n class=\"btn\"\n [class.btn-primary]=\"view() === 'list'\"\n [class.btn-secondary]=\"view() !== 'list'\"\n [disabled]=\"disableNextButton()\"\n (click)=\"onNext()\"\n >{{ labelNext | translate }}</button\n >\n }\n @if (showAddButton()) {\n <button\n type=\"button\"\n class=\"btn btn-primary\"\n [disabled]=\"disableAddButton()\"\n (click)=\"onAddWidget()\"\n >{{ labelAdd | translate }}</button\n >\n }\n</div>\n", styles: [":host{display:flex;flex:1 1 0;flex-direction:column;margin-inline:-4px;margin-block-start:-4px}.list-group-item{cursor:pointer}.catalog-footer{align-items:center;display:flex;flex-shrink:0;flex-wrap:wrap;justify-content:flex-end}.catalog-footer>*{margin-inline-start:16px}\n"] }]
|
|
1990
|
+
}], propDecorators: { searchPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchPlaceholder", required: false }] }], closed: [{ type: i0.Output, args: ["closed"] }], widgetCdkListbox: [{ type: i0.ViewChild, args: [i0.forwardRef(() => CdkListbox), { isSignal: true }] }] } });
|
|
1834
1991
|
|
|
1835
1992
|
/**
|
|
1836
|
-
* Copyright (c) Siemens 2016 -
|
|
1993
|
+
* Copyright (c) Siemens 2016 - 2026
|
|
1837
1994
|
* SPDX-License-Identifier: MIT
|
|
1838
1995
|
*/
|
|
1839
1996
|
/**
|
|
@@ -1846,62 +2003,73 @@ class SiFlexibleDashboardComponent {
|
|
|
1846
2003
|
*
|
|
1847
2004
|
* @defaultValue false
|
|
1848
2005
|
*/
|
|
1849
|
-
editable = model(false
|
|
2006
|
+
editable = model(false, /* @ts-ignore */
|
|
2007
|
+
...(ngDevMode ? [{ debugName: "editable" }] : /* istanbul ignore next */ []));
|
|
1850
2008
|
/**
|
|
1851
2009
|
* Heading for the dashboard.
|
|
1852
2010
|
*/
|
|
1853
|
-
heading = input(
|
|
2011
|
+
heading = input(/* @ts-ignore */
|
|
2012
|
+
...(ngDevMode ? [undefined, { debugName: "heading" }] : /* istanbul ignore next */ []));
|
|
1854
2013
|
/**
|
|
1855
2014
|
* Optionally, provide a custom subclass of the SiWidgetCatalogComponent
|
|
1856
2015
|
* to use your own catalog component.
|
|
1857
2016
|
*/
|
|
1858
|
-
widgetCatalogComponent = input(
|
|
2017
|
+
widgetCatalogComponent = input(/* @ts-ignore */
|
|
2018
|
+
...(ngDevMode ? [undefined, { debugName: "widgetCatalogComponent" }] : /* istanbul ignore next */ []));
|
|
1859
2019
|
/**
|
|
1860
2020
|
* Optionally, provide a custom subclass of the {@link SiWidgetInstanceEditorDialogComponent}
|
|
1861
2021
|
* to use your own implementation.
|
|
1862
2022
|
*/
|
|
1863
|
-
widgetInstanceEditorDialogComponent = input(
|
|
2023
|
+
widgetInstanceEditorDialogComponent = input(/* @ts-ignore */
|
|
2024
|
+
...(ngDevMode ? [undefined, { debugName: "widgetInstanceEditorDialogComponent" }] : /* istanbul ignore next */ []));
|
|
1864
2025
|
/**
|
|
1865
2026
|
* Optionally, but it is recommended to include an id for a dashboard.
|
|
1866
2027
|
* The id is utilized in the persistence calls on the {@link SiWidgetStorage}.
|
|
1867
2028
|
*/
|
|
1868
|
-
dashboardId = input(
|
|
2029
|
+
dashboardId = input(/* @ts-ignore */
|
|
2030
|
+
...(ngDevMode ? [undefined, { debugName: "dashboardId" }] : /* istanbul ignore next */ []));
|
|
1869
2031
|
/**
|
|
1870
2032
|
* Sets the available widgets for the widget catalog to the dashboard.
|
|
1871
2033
|
*
|
|
1872
2034
|
* @defaultValue [] */
|
|
1873
|
-
widgetCatalog = input([]
|
|
2035
|
+
widgetCatalog = input([], /* @ts-ignore */
|
|
2036
|
+
...(ngDevMode ? [{ debugName: "widgetCatalog" }] : /* istanbul ignore next */ []));
|
|
1874
2037
|
/**
|
|
1875
2038
|
* Option to remove the add widget instance button from the primary toolbar.
|
|
1876
2039
|
*
|
|
1877
2040
|
* @defaultValue false
|
|
1878
2041
|
*/
|
|
1879
|
-
hideAddWidgetInstanceButton = input(false
|
|
2042
|
+
hideAddWidgetInstanceButton = input(false, /* @ts-ignore */
|
|
2043
|
+
...(ngDevMode ? [{ debugName: "hideAddWidgetInstanceButton" }] : /* istanbul ignore next */ []));
|
|
1880
2044
|
/**
|
|
1881
2045
|
* Option to hide the dashboard edit button.
|
|
1882
2046
|
*
|
|
1883
2047
|
* @defaultValue false
|
|
1884
2048
|
*/
|
|
1885
|
-
hideEditButton = input(false
|
|
2049
|
+
hideEditButton = input(false, /* @ts-ignore */
|
|
2050
|
+
...(ngDevMode ? [{ debugName: "hideEditButton" }] : /* istanbul ignore next */ []));
|
|
1886
2051
|
/**
|
|
1887
2052
|
* Option to display the edit button as a text button instead, only if the window is larger than xs {@link SiResponsiveContainerDirective}.
|
|
1888
2053
|
*
|
|
1889
2054
|
* @defaultValue false
|
|
1890
2055
|
*/
|
|
1891
|
-
showEditButtonLabel = input(false
|
|
2056
|
+
showEditButtonLabel = input(false, /* @ts-ignore */
|
|
2057
|
+
...(ngDevMode ? [{ debugName: "showEditButtonLabel" }] : /* istanbul ignore next */ []));
|
|
1892
2058
|
/**
|
|
1893
2059
|
* Option to turn off the loading spinner on save and load operations.
|
|
1894
2060
|
*
|
|
1895
2061
|
* @defaultValue false
|
|
1896
2062
|
*/
|
|
1897
|
-
hideProgressIndicator = input(false
|
|
2063
|
+
hideProgressIndicator = input(false, /* @ts-ignore */
|
|
2064
|
+
...(ngDevMode ? [{ debugName: "hideProgressIndicator" }] : /* istanbul ignore next */ []));
|
|
1898
2065
|
/**
|
|
1899
2066
|
* Option to configure a dashboard instance. Default is the optional value from
|
|
1900
2067
|
* the {@link SI_DASHBOARD_CONFIGURATION}.
|
|
1901
2068
|
*
|
|
1902
2069
|
* @defaultValue inject(SI_DASHBOARD_CONFIGURATION)
|
|
1903
2070
|
*/
|
|
1904
|
-
config = input(inject(SI_DASHBOARD_CONFIGURATION)
|
|
2071
|
+
config = input(inject(SI_DASHBOARD_CONFIGURATION), /* @ts-ignore */
|
|
2072
|
+
...(ngDevMode ? [{ debugName: "config" }] : /* istanbul ignore next */ []));
|
|
1905
2073
|
/**
|
|
1906
2074
|
* Placeholder text for the search input field in the widget catalog.
|
|
1907
2075
|
*
|
|
@@ -1910,7 +2078,20 @@ class SiFlexibleDashboardComponent {
|
|
|
1910
2078
|
* t(() => $localize`:@@DASHBOARD.WIDGET_LIBRARY.SEARCH_PLACEHOLDER:Search widget`)
|
|
1911
2079
|
* ```
|
|
1912
2080
|
*/
|
|
1913
|
-
searchPlaceholder = input(t(() => $localize `:@@DASHBOARD.WIDGET_LIBRARY.SEARCH_PLACEHOLDER:Search widget`)
|
|
2081
|
+
searchPlaceholder = input(t(() => $localize `:@@DASHBOARD.WIDGET_LIBRARY.SEARCH_PLACEHOLDER:Search widget`), /* @ts-ignore */
|
|
2082
|
+
...(ngDevMode ? [{ debugName: "searchPlaceholder" }] : /* istanbul ignore next */ []));
|
|
2083
|
+
/**
|
|
2084
|
+
* Primary action menu items shown in the edit mode of the dashboard.
|
|
2085
|
+
* @defaultValue []
|
|
2086
|
+
*/
|
|
2087
|
+
primaryEditActions = input([], /* @ts-ignore */
|
|
2088
|
+
...(ngDevMode ? [{ debugName: "primaryEditActions" }] : /* istanbul ignore next */ []));
|
|
2089
|
+
/**
|
|
2090
|
+
* Secondary action menu items shown in the edit mode of the dashboard.
|
|
2091
|
+
* @defaultValue []
|
|
2092
|
+
*/
|
|
2093
|
+
secondaryEditActions = input([], /* @ts-ignore */
|
|
2094
|
+
...(ngDevMode ? [{ debugName: "secondaryEditActions" }] : /* istanbul ignore next */ []));
|
|
1914
2095
|
/**
|
|
1915
2096
|
* The grid component is the actual container for the widgets.
|
|
1916
2097
|
*/
|
|
@@ -1945,13 +2126,14 @@ class SiFlexibleDashboardComponent {
|
|
|
1945
2126
|
]);
|
|
1946
2127
|
/**
|
|
1947
2128
|
* The secondary action menu items shown in the edit mode of the dashboard. When all menu items are more than
|
|
1948
|
-
* three, they will be
|
|
2129
|
+
* three, they will be placed in the secondary menu of the content action bar.
|
|
1949
2130
|
*/
|
|
1950
2131
|
secondaryEditActions$ = new BehaviorSubject([]);
|
|
1951
2132
|
/**
|
|
1952
2133
|
* @returns True, if the dashboard shows its widgets and not a catalog or and editor.
|
|
1953
2134
|
*/
|
|
1954
|
-
isDashboardVisible = computed(() => this.viewState() === 'dashboard'
|
|
2135
|
+
isDashboardVisible = computed(() => this.viewState() === 'dashboard', /* @ts-ignore */
|
|
2136
|
+
...(ngDevMode ? [{ debugName: "isDashboardVisible" }] : /* istanbul ignore next */ []));
|
|
1955
2137
|
/**
|
|
1956
2138
|
* The page title of the dashboard, which is either {@link SiFlexibleDashboardComponent.heading} for the
|
|
1957
2139
|
* default widget view or `DASHBOARD.WIDGET_EDITOR_DIALOG.TITLE` or 'DASHBOARD.WIDGET_LIBRARY.TITLE' when
|
|
@@ -1967,14 +2149,18 @@ class SiFlexibleDashboardComponent {
|
|
|
1967
2149
|
default:
|
|
1968
2150
|
return this.heading();
|
|
1969
2151
|
}
|
|
1970
|
-
}
|
|
1971
|
-
|
|
1972
|
-
|
|
2152
|
+
}, /* @ts-ignore */
|
|
2153
|
+
...(ngDevMode ? [{ debugName: "pageTitle" }] : /* istanbul ignore next */ []));
|
|
2154
|
+
viewState = signal('dashboard', /* @ts-ignore */
|
|
2155
|
+
...(ngDevMode ? [{ debugName: "viewState" }] : /* istanbul ignore next */ []));
|
|
1973
2156
|
widgetStorage = inject(SI_WIDGET_STORE);
|
|
1974
2157
|
hideAddWidgetInstanceButton$ = new BehaviorSubject(this.hideAddWidgetInstanceButton());
|
|
1975
2158
|
dashboardId$ = new Subject();
|
|
1976
|
-
outputRefSubscription = [];
|
|
1977
2159
|
toolbar = viewChild.required('toolbar');
|
|
2160
|
+
globalEditActions = inject(SI_DASHBOARD_TOOLBAR_ITEMS, { optional: true }) ?? {
|
|
2161
|
+
primary: [],
|
|
2162
|
+
secondary: []
|
|
2163
|
+
};
|
|
1978
2164
|
ngOnChanges(changes) {
|
|
1979
2165
|
if (changes.dashboardId) {
|
|
1980
2166
|
const dashboard = this.dashboard();
|
|
@@ -1982,39 +2168,22 @@ class SiFlexibleDashboardComponent {
|
|
|
1982
2168
|
dashboard.restore();
|
|
1983
2169
|
}
|
|
1984
2170
|
if (this.editable()) {
|
|
1985
|
-
this.
|
|
2171
|
+
this.editable.set(false);
|
|
1986
2172
|
this.viewState.set('dashboard');
|
|
1987
2173
|
this.catalogHost().clear();
|
|
1988
2174
|
}
|
|
1989
2175
|
this.dashboardId$.next(changes.dashboardId.currentValue);
|
|
1990
2176
|
this.setupMenuItems();
|
|
1991
2177
|
}
|
|
1992
|
-
if (changes.editable) {
|
|
1993
|
-
if (changes.editable.currentValue) {
|
|
1994
|
-
this.grid().edit();
|
|
1995
|
-
}
|
|
1996
|
-
else {
|
|
1997
|
-
this.grid().cancel();
|
|
1998
|
-
}
|
|
1999
|
-
}
|
|
2000
2178
|
if (changes.hideAddWidgetInstanceButton) {
|
|
2001
2179
|
this.hideAddWidgetInstanceButton$.next(changes.hideAddWidgetInstanceButton.currentValue);
|
|
2002
2180
|
}
|
|
2003
2181
|
}
|
|
2004
2182
|
ngOnInit() {
|
|
2005
2183
|
this.setupMenuItems();
|
|
2006
|
-
const grid = this.grid();
|
|
2007
|
-
this.outputRefSubscription.push(grid.widgetInstanceEdit.subscribe(widgetConfig => {
|
|
2008
|
-
this.editWidgetInstance(widgetConfig);
|
|
2009
|
-
}));
|
|
2010
|
-
this.outputRefSubscription.push(grid.editable.subscribe(editable => {
|
|
2011
|
-
this.editable.set(editable);
|
|
2012
|
-
}));
|
|
2013
2184
|
}
|
|
2014
2185
|
ngOnDestroy() {
|
|
2015
2186
|
this.dashboardId$.next(undefined);
|
|
2016
|
-
this.subscriptions?.forEach(sub => sub.unsubscribe());
|
|
2017
|
-
this.outputRefSubscription?.forEach(sub => sub.unsubscribe());
|
|
2018
2187
|
}
|
|
2019
2188
|
/**
|
|
2020
2189
|
* Shows the widget catalog of the dashboard. If a widget instance is expanded
|
|
@@ -2026,39 +2195,50 @@ class SiFlexibleDashboardComponent {
|
|
|
2026
2195
|
dashboard.restore();
|
|
2027
2196
|
}
|
|
2028
2197
|
if (!this.editable()) {
|
|
2029
|
-
this.
|
|
2198
|
+
this.editable.set(true);
|
|
2030
2199
|
}
|
|
2031
2200
|
this.viewState.set('catalog');
|
|
2032
2201
|
const componentType = this.widgetCatalogComponent() ?? SiWidgetCatalogComponent;
|
|
2033
|
-
const catalogRef = this.catalogHost().createComponent(componentType
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2202
|
+
const catalogRef = this.catalogHost().createComponent(componentType, {
|
|
2203
|
+
bindings: [
|
|
2204
|
+
inputBinding('searchPlaceholder', this.searchPlaceholder),
|
|
2205
|
+
outputBinding('closed', widgetConfig => {
|
|
2206
|
+
this.viewState.set('dashboard');
|
|
2207
|
+
this.catalogHost().clear();
|
|
2208
|
+
if (widgetConfig) {
|
|
2209
|
+
this.grid().addWidgetInstance(widgetConfig);
|
|
2210
|
+
}
|
|
2211
|
+
})
|
|
2212
|
+
]
|
|
2043
2213
|
});
|
|
2214
|
+
catalogRef.instance.widgetCatalog = this.widgetCatalog();
|
|
2044
2215
|
}
|
|
2045
2216
|
onModified(event) {
|
|
2046
2217
|
this.isModified.emit(event);
|
|
2047
2218
|
this.toolbar().disableSaveButton.set(!event);
|
|
2048
2219
|
}
|
|
2049
2220
|
setupMenuItems() {
|
|
2050
|
-
const
|
|
2051
|
-
|
|
2052
|
-
|
|
2221
|
+
const primaryEditActions = [...this.primaryEditActions(), ...this.globalEditActions.primary];
|
|
2222
|
+
const secondaryEditActions = [
|
|
2223
|
+
...this.secondaryEditActions(),
|
|
2224
|
+
...this.globalEditActions.secondary
|
|
2225
|
+
];
|
|
2226
|
+
const primaryMenuItems = primaryEditActions.length
|
|
2227
|
+
? of(primaryEditActions)
|
|
2228
|
+
: this.widgetStorage.getToolbarMenuItems
|
|
2229
|
+
? this.widgetStorage.getToolbarMenuItems(this.dashboardId()).primary
|
|
2230
|
+
: of([]);
|
|
2053
2231
|
combineLatest([primaryMenuItems, this.hideAddWidgetInstanceButton$])
|
|
2054
2232
|
.pipe(takeUntil(this.dashboardId$))
|
|
2055
2233
|
.subscribe(([items, hideAddButton]) => this.setupPrimaryMenuItems(items, hideAddButton));
|
|
2056
|
-
const secondaryMenuItems =
|
|
2057
|
-
?
|
|
2058
|
-
:
|
|
2234
|
+
const secondaryMenuItems = secondaryEditActions.length
|
|
2235
|
+
? of(secondaryEditActions)
|
|
2236
|
+
: this.widgetStorage.getToolbarMenuItems
|
|
2237
|
+
? this.widgetStorage.getToolbarMenuItems(this.dashboardId()).secondary
|
|
2238
|
+
: undefined;
|
|
2059
2239
|
if (secondaryMenuItems) {
|
|
2060
2240
|
secondaryMenuItems
|
|
2061
|
-
.pipe(takeUntil(this.dashboardId$)
|
|
2241
|
+
.pipe(takeUntil(this.dashboardId$))
|
|
2062
2242
|
.subscribe(items => this.secondaryEditActions$.next(items));
|
|
2063
2243
|
}
|
|
2064
2244
|
}
|
|
@@ -2066,7 +2246,7 @@ class SiFlexibleDashboardComponent {
|
|
|
2066
2246
|
const next = hideAddWidgetInstanceButton
|
|
2067
2247
|
? []
|
|
2068
2248
|
: [this.addWidgetInstanceAction];
|
|
2069
|
-
next.push(...items
|
|
2249
|
+
next.push(...items);
|
|
2070
2250
|
this.primaryEditActions$.next(next);
|
|
2071
2251
|
}
|
|
2072
2252
|
editWidgetInstance(widgetConfig) {
|
|
@@ -2078,82 +2258,121 @@ class SiFlexibleDashboardComponent {
|
|
|
2078
2258
|
const widgetInstanceEditorDialogComponent = this.widgetInstanceEditorDialogComponent();
|
|
2079
2259
|
const componentType = widgetInstanceEditorDialogComponent ?? SiWidgetInstanceEditorDialogComponent;
|
|
2080
2260
|
this.viewState.set('editor');
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2261
|
+
this.catalogHost().createComponent(componentType, {
|
|
2262
|
+
bindings: [
|
|
2263
|
+
inputBinding('widgetConfig', () => widgetConfig),
|
|
2264
|
+
inputBinding('widget', () => widget),
|
|
2265
|
+
outputBinding('closed', editedWidgetConfig => {
|
|
2266
|
+
this.viewState.set('dashboard');
|
|
2267
|
+
this.catalogHost().clear();
|
|
2268
|
+
if (editedWidgetConfig) {
|
|
2269
|
+
this.grid().updateWidgetInstance(editedWidgetConfig);
|
|
2270
|
+
}
|
|
2271
|
+
})
|
|
2272
|
+
]
|
|
2091
2273
|
});
|
|
2092
2274
|
}
|
|
2093
|
-
proxyMenuItemAction(item) {
|
|
2094
|
-
if ('action' in item) {
|
|
2095
|
-
if (!item.action || item.action instanceof String) {
|
|
2096
|
-
return item;
|
|
2097
|
-
}
|
|
2098
|
-
else {
|
|
2099
|
-
const realAction = item.action;
|
|
2100
|
-
item.action = () => {
|
|
2101
|
-
realAction(this.grid());
|
|
2102
|
-
};
|
|
2103
|
-
}
|
|
2104
|
-
}
|
|
2105
|
-
return item;
|
|
2106
|
-
}
|
|
2107
2275
|
getWidget(id) {
|
|
2108
2276
|
return this.widgetCatalog().find(widget => widget.id === id);
|
|
2109
2277
|
}
|
|
2110
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
2111
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "
|
|
2278
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SiFlexibleDashboardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2279
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.0.5", type: SiFlexibleDashboardComponent, isStandalone: true, selector: "si-flexible-dashboard", inputs: { editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, heading: { classPropertyName: "heading", publicName: "heading", isSignal: true, isRequired: false, transformFunction: null }, widgetCatalogComponent: { classPropertyName: "widgetCatalogComponent", publicName: "widgetCatalogComponent", isSignal: true, isRequired: false, transformFunction: null }, widgetInstanceEditorDialogComponent: { classPropertyName: "widgetInstanceEditorDialogComponent", publicName: "widgetInstanceEditorDialogComponent", isSignal: true, isRequired: false, transformFunction: null }, dashboardId: { classPropertyName: "dashboardId", publicName: "dashboardId", isSignal: true, isRequired: false, transformFunction: null }, widgetCatalog: { classPropertyName: "widgetCatalog", publicName: "widgetCatalog", isSignal: true, isRequired: false, transformFunction: null }, hideAddWidgetInstanceButton: { classPropertyName: "hideAddWidgetInstanceButton", publicName: "hideAddWidgetInstanceButton", isSignal: true, isRequired: false, transformFunction: null }, hideEditButton: { classPropertyName: "hideEditButton", publicName: "hideEditButton", isSignal: true, isRequired: false, transformFunction: null }, showEditButtonLabel: { classPropertyName: "showEditButtonLabel", publicName: "showEditButtonLabel", isSignal: true, isRequired: false, transformFunction: null }, hideProgressIndicator: { classPropertyName: "hideProgressIndicator", publicName: "hideProgressIndicator", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, searchPlaceholder: { classPropertyName: "searchPlaceholder", publicName: "searchPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, primaryEditActions: { classPropertyName: "primaryEditActions", publicName: "primaryEditActions", isSignal: true, isRequired: false, transformFunction: null }, secondaryEditActions: { classPropertyName: "secondaryEditActions", publicName: "secondaryEditActions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { editable: "editableChange", isModified: "isModified" }, viewQueries: [{ propertyName: "grid", first: true, predicate: ["grid"], descendants: true, isSignal: true }, { propertyName: "dashboard", first: true, predicate: ["dashboard"], descendants: true, isSignal: true }, { propertyName: "catalogHost", first: true, predicate: ["catalogHost"], descendants: true, read: ViewContainerRef, isSignal: true }, { propertyName: "toolbar", first: true, predicate: ["toolbar"], descendants: true, isSignal: true }], usesOnChanges: true, ngImport: i0, template: "<si-dashboard #dashboard [heading]=\"pageTitle()\" [sticky]=\"true\">\n <si-dashboard-toolbar\n #toolbar\n menubar\n [class.d-none]=\"!isDashboardVisible()\"\n [primaryEditActions]=\"(primaryEditActions$ | async) ?? []\"\n [secondaryEditActions]=\"(secondaryEditActions$ | async) ?? []\"\n [disabled]=\"(grid.isLoading | async) ?? false\"\n [hideEditButton]=\"hideEditButton()\"\n [showEditButtonLabel]=\"showEditButtonLabel()\"\n [grid]=\"grid\"\n [(editable)]=\"editable\"\n (save)=\"grid.save()\"\n >\n <ng-content select=\"[filters-slot]\" filters-slot />\n </si-dashboard-toolbar>\n\n <div dashboard class=\"d-contents\">\n <si-grid\n #grid\n [class.d-none]=\"!isDashboardVisible()\"\n [gridConfig]=\"config()?.grid\"\n [dashboardId]=\"dashboardId()\"\n [widgetCatalog]=\"widgetCatalog()\"\n [hideProgressIndicator]=\"hideProgressIndicator()\"\n [widgetInstanceEditorDialogComponent]=\"widgetInstanceEditorDialogComponent()\"\n [emitWidgetInstanceEditEvents]=\"true\"\n [(editable)]=\"editable\"\n (isModified)=\"onModified($event)\"\n (widgetInstanceEdit)=\"editWidgetInstance($event)\"\n >\n <ng-content select=\"[empty-state]\" empty-state />\n </si-grid>\n <ng-container #catalogHost />\n </div>\n</si-dashboard>\n", styles: [":host{display:flex;flex:1 1 0;flex-direction:column}\n"], dependencies: [{ kind: "component", type: SiDashboardComponent, selector: "si-dashboard", inputs: ["heading", "enableExpandInteractions", "sticky", "hideMenubar"] }, { kind: "component", type: SiDashboardToolbarComponent, selector: "si-dashboard-toolbar", inputs: ["primaryEditActions", "secondaryEditActions", "disableSaveButton", "disabled", "editable", "hideEditButton", "showEditButtonLabel", "grid"], outputs: ["disableSaveButtonChange", "editableChange", "save"] }, { kind: "component", type: SiGridComponent, selector: "si-grid", inputs: ["gridConfig", "editable", "dashboardId", "widgetCatalog", "emitWidgetInstanceEditEvents", "hideProgressIndicator", "widgetInstanceEditorDialogComponent"], outputs: ["editableChange", "isModified", "widgetInstanceEdit"] }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.Eager });
|
|
2112
2280
|
}
|
|
2113
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
2281
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SiFlexibleDashboardComponent, decorators: [{
|
|
2114
2282
|
type: Component,
|
|
2115
|
-
args: [{ selector: 'si-flexible-dashboard', imports: [SiDashboardComponent, SiDashboardToolbarComponent, SiGridComponent, AsyncPipe], template: "<si-dashboard #dashboard [heading]=\"pageTitle()\" [sticky]=\"true\">\n <si-dashboard-toolbar\n #toolbar\n menubar\n [class.d-none]=\"!isDashboardVisible()\"\n [primaryEditActions]=\"(primaryEditActions$ | async) ?? []\"\n [secondaryEditActions]=\"(secondaryEditActions$ | async) ?? []\"\n [
|
|
2116
|
-
}] });
|
|
2283
|
+
args: [{ selector: 'si-flexible-dashboard', imports: [SiDashboardComponent, SiDashboardToolbarComponent, SiGridComponent, AsyncPipe], changeDetection: ChangeDetectionStrategy.Eager, template: "<si-dashboard #dashboard [heading]=\"pageTitle()\" [sticky]=\"true\">\n <si-dashboard-toolbar\n #toolbar\n menubar\n [class.d-none]=\"!isDashboardVisible()\"\n [primaryEditActions]=\"(primaryEditActions$ | async) ?? []\"\n [secondaryEditActions]=\"(secondaryEditActions$ | async) ?? []\"\n [disabled]=\"(grid.isLoading | async) ?? false\"\n [hideEditButton]=\"hideEditButton()\"\n [showEditButtonLabel]=\"showEditButtonLabel()\"\n [grid]=\"grid\"\n [(editable)]=\"editable\"\n (save)=\"grid.save()\"\n >\n <ng-content select=\"[filters-slot]\" filters-slot />\n </si-dashboard-toolbar>\n\n <div dashboard class=\"d-contents\">\n <si-grid\n #grid\n [class.d-none]=\"!isDashboardVisible()\"\n [gridConfig]=\"config()?.grid\"\n [dashboardId]=\"dashboardId()\"\n [widgetCatalog]=\"widgetCatalog()\"\n [hideProgressIndicator]=\"hideProgressIndicator()\"\n [widgetInstanceEditorDialogComponent]=\"widgetInstanceEditorDialogComponent()\"\n [emitWidgetInstanceEditEvents]=\"true\"\n [(editable)]=\"editable\"\n (isModified)=\"onModified($event)\"\n (widgetInstanceEdit)=\"editWidgetInstance($event)\"\n >\n <ng-content select=\"[empty-state]\" empty-state />\n </si-grid>\n <ng-container #catalogHost />\n </div>\n</si-dashboard>\n", styles: [":host{display:flex;flex:1 1 0;flex-direction:column}\n"] }]
|
|
2284
|
+
}], propDecorators: { editable: [{ type: i0.Input, args: [{ isSignal: true, alias: "editable", required: false }] }, { type: i0.Output, args: ["editableChange"] }], heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: false }] }], widgetCatalogComponent: [{ type: i0.Input, args: [{ isSignal: true, alias: "widgetCatalogComponent", required: false }] }], widgetInstanceEditorDialogComponent: [{ type: i0.Input, args: [{ isSignal: true, alias: "widgetInstanceEditorDialogComponent", required: false }] }], dashboardId: [{ type: i0.Input, args: [{ isSignal: true, alias: "dashboardId", required: false }] }], widgetCatalog: [{ type: i0.Input, args: [{ isSignal: true, alias: "widgetCatalog", required: false }] }], hideAddWidgetInstanceButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideAddWidgetInstanceButton", required: false }] }], hideEditButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideEditButton", required: false }] }], showEditButtonLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "showEditButtonLabel", required: false }] }], hideProgressIndicator: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideProgressIndicator", required: false }] }], config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], searchPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchPlaceholder", required: false }] }], primaryEditActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "primaryEditActions", required: false }] }], secondaryEditActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "secondaryEditActions", required: false }] }], grid: [{ type: i0.ViewChild, args: ['grid', { isSignal: true }] }], dashboard: [{ type: i0.ViewChild, args: ['dashboard', { isSignal: true }] }], catalogHost: [{ type: i0.ViewChild, args: ['catalogHost', { ...{ read: ViewContainerRef }, isSignal: true }] }], isModified: [{ type: i0.Output, args: ["isModified"] }], toolbar: [{ type: i0.ViewChild, args: ['toolbar', { isSignal: true }] }] } });
|
|
2117
2285
|
|
|
2118
2286
|
/**
|
|
2119
|
-
* Copyright (c) Siemens 2016 -
|
|
2287
|
+
* Copyright (c) Siemens 2016 - 2026
|
|
2288
|
+
* SPDX-License-Identifier: MIT
|
|
2289
|
+
*/
|
|
2290
|
+
/**
|
|
2291
|
+
* A presentational (dumb) component that renders a single widget instance from a
|
|
2292
|
+
* {@link WidgetConfig} using the provided {@link Widget} definition. It delegates
|
|
2293
|
+
* the actual rendering to the {@link SiWidgetHostComponent}.
|
|
2294
|
+
*
|
|
2295
|
+
* **Intended Usage**
|
|
2296
|
+
*
|
|
2297
|
+
* This component renders an individual widget outside of a dashboard or grid,
|
|
2298
|
+
* without requiring manual host wiring. It is a self-contained widget cell that
|
|
2299
|
+
* can be placed anywhere a single widget needs to be shown, for example in kiosk
|
|
2300
|
+
* mode or within a custom layout such as a carousel.
|
|
2301
|
+
*
|
|
2302
|
+
* **Example**
|
|
2303
|
+
*
|
|
2304
|
+
* ```html
|
|
2305
|
+
* <!-- Render a single widget instance -->
|
|
2306
|
+
* <si-widget-renderer [widgetConfig]="widgetConfig" [widget]="widget" />
|
|
2307
|
+
* ```
|
|
2308
|
+
*/
|
|
2309
|
+
class SiWidgetRendererComponent {
|
|
2310
|
+
/** The configuration of the widget instance to render. */
|
|
2311
|
+
widgetConfig = input.required(/* @ts-ignore */
|
|
2312
|
+
...(ngDevMode ? [{ debugName: "widgetConfig" }] : /* istanbul ignore next */ []));
|
|
2313
|
+
/**
|
|
2314
|
+
* The {@link Widget} definition used to render the given {@link widgetConfig}.
|
|
2315
|
+
* It must match the config's `widgetId`.
|
|
2316
|
+
*/
|
|
2317
|
+
widget = input.required(/* @ts-ignore */
|
|
2318
|
+
...(ngDevMode ? [{ debugName: "widget" }] : /* istanbul ignore next */ []));
|
|
2319
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SiWidgetRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2320
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.5", type: SiWidgetRendererComponent, isStandalone: true, selector: "si-widget-renderer", inputs: { widgetConfig: { classPropertyName: "widgetConfig", publicName: "widgetConfig", isSignal: true, isRequired: true, transformFunction: null }, widget: { classPropertyName: "widget", publicName: "widget", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<si-widget-host\n class=\"h-100\"\n [widgetConfig]=\"widgetConfig()\"\n [componentFactory]=\"widget().componentFactory\"\n/>\n", styles: ["si-widget-host ::ng-deep .grid-stack-item-content{flex:1 1 auto;min-inline-size:0;min-block-size:0;padding:0!important}\n"], dependencies: [{ kind: "component", type: SiWidgetHostComponent, selector: "si-widget-host", inputs: ["widgetConfig", "grid", "componentFactory", "editable"], outputs: ["remove", "edit", "gridEvent"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2321
|
+
}
|
|
2322
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SiWidgetRendererComponent, decorators: [{
|
|
2323
|
+
type: Component,
|
|
2324
|
+
args: [{ selector: 'si-widget-renderer', imports: [SiWidgetHostComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<si-widget-host\n class=\"h-100\"\n [widgetConfig]=\"widgetConfig()\"\n [componentFactory]=\"widget().componentFactory\"\n/>\n", styles: ["si-widget-host ::ng-deep .grid-stack-item-content{flex:1 1 auto;min-inline-size:0;min-block-size:0;padding:0!important}\n"] }]
|
|
2325
|
+
}], propDecorators: { widgetConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "widgetConfig", required: true }] }], widget: [{ type: i0.Input, args: [{ isSignal: true, alias: "widget", required: true }] }] } });
|
|
2326
|
+
|
|
2327
|
+
/**
|
|
2328
|
+
* Formats an error message for failed module loading.
|
|
2329
|
+
*/
|
|
2330
|
+
const formatLoadingError = (factory, rejection) => rejection
|
|
2331
|
+
? `Loading widget module ${factory.exposedModule} failed with ${String(rejection)}`
|
|
2332
|
+
: `Loading widget module ${factory.exposedModule} failed`;
|
|
2333
|
+
/**
|
|
2334
|
+
* Handles the result of a federated module loading promise.
|
|
2335
|
+
* This is the main utility function that encapsulates the common loading pattern
|
|
2336
|
+
* used across module-federation, native-federation, and mf-bridge loaders.
|
|
2337
|
+
*
|
|
2338
|
+
* @param options - Configuration options for loading and creating the component
|
|
2339
|
+
* @returns Observable that emits the ComponentRef once the component is created
|
|
2340
|
+
* @internal
|
|
2341
|
+
*/
|
|
2342
|
+
const handleFederatedModuleLoad = ({ loadPromise, factory, componentName, host, injector, environmentInjector }) => {
|
|
2343
|
+
const result = new Subject();
|
|
2344
|
+
loadPromise.then(module => {
|
|
2345
|
+
if (module) {
|
|
2346
|
+
const componentType = module[factory[componentName]];
|
|
2347
|
+
const widgetInstanceRef = host.createComponent(componentType, {
|
|
2348
|
+
injector,
|
|
2349
|
+
environmentInjector
|
|
2350
|
+
});
|
|
2351
|
+
result.next(widgetInstanceRef);
|
|
2352
|
+
}
|
|
2353
|
+
result.complete();
|
|
2354
|
+
}, rejection => result.error(formatLoadingError(factory, rejection)));
|
|
2355
|
+
return result;
|
|
2356
|
+
};
|
|
2357
|
+
|
|
2358
|
+
/**
|
|
2359
|
+
* Copyright (c) Siemens 2016 - 2026
|
|
2120
2360
|
* SPDX-License-Identifier: MIT
|
|
2121
2361
|
*/
|
|
2122
2362
|
class SiDashboardsNgModule {
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
* `SI_DASHBOARD_CONFIGURATION` and `SI_WIDGET_STORE` direct in your app configuration. We migrated to standalone
|
|
2126
|
-
* components already and will delete this module definition later.
|
|
2127
|
-
*/
|
|
2128
|
-
static forRoot({ config, dashboardApi }) {
|
|
2129
|
-
return {
|
|
2130
|
-
ngModule: SiDashboardsNgModule,
|
|
2131
|
-
providers: [
|
|
2132
|
-
{
|
|
2133
|
-
provide: SI_DASHBOARD_CONFIGURATION,
|
|
2134
|
-
useValue: config
|
|
2135
|
-
},
|
|
2136
|
-
dashboardApi ?? {
|
|
2137
|
-
provide: SiWidgetStorage,
|
|
2138
|
-
useClass: SiDefaultWidgetStorage
|
|
2139
|
-
},
|
|
2140
|
-
dashboardApi ? { provide: SI_WIDGET_STORE, useExisting: SiWidgetStorage } : []
|
|
2141
|
-
]
|
|
2142
|
-
};
|
|
2143
|
-
}
|
|
2144
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: SiDashboardsNgModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
2145
|
-
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.0.6", ngImport: i0, type: SiDashboardsNgModule, imports: [SiFlexibleDashboardComponent,
|
|
2363
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SiDashboardsNgModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
2364
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.0.5", ngImport: i0, type: SiDashboardsNgModule, imports: [SiFlexibleDashboardComponent,
|
|
2146
2365
|
SiGridComponent,
|
|
2147
2366
|
SiWidgetCatalogComponent,
|
|
2148
2367
|
SiWidgetInstanceEditorDialogComponent], exports: [SiFlexibleDashboardComponent,
|
|
2149
2368
|
SiGridComponent,
|
|
2150
2369
|
SiWidgetCatalogComponent,
|
|
2151
2370
|
SiWidgetInstanceEditorDialogComponent] });
|
|
2152
|
-
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "
|
|
2153
|
-
|
|
2154
|
-
|
|
2371
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SiDashboardsNgModule, imports: [SiFlexibleDashboardComponent,
|
|
2372
|
+
SiGridComponent,
|
|
2373
|
+
SiWidgetCatalogComponent] });
|
|
2155
2374
|
}
|
|
2156
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
2375
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SiDashboardsNgModule, decorators: [{
|
|
2157
2376
|
type: NgModule,
|
|
2158
2377
|
args: [{
|
|
2159
2378
|
imports: [
|
|
@@ -2170,9 +2389,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.6", ngImpor
|
|
|
2170
2389
|
]
|
|
2171
2390
|
}]
|
|
2172
2391
|
}] });
|
|
2392
|
+
/**
|
|
2393
|
+
* @deprecated Use {@link SiDashboardsNgModule} instead. The `Simpl` prefix is deprecated and will be removed in v51.
|
|
2394
|
+
*/
|
|
2395
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
2396
|
+
const SimplDashboardsNgModule = SiDashboardsNgModule;
|
|
2173
2397
|
|
|
2174
2398
|
/**
|
|
2175
|
-
* Copyright (c) Siemens 2016 -
|
|
2399
|
+
* Copyright (c) Siemens 2016 - 2026
|
|
2176
2400
|
* SPDX-License-Identifier: MIT
|
|
2177
2401
|
*/
|
|
2178
2402
|
|
|
@@ -2180,5 +2404,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.6", ngImpor
|
|
|
2180
2404
|
* Generated bundle index. Do not edit.
|
|
2181
2405
|
*/
|
|
2182
2406
|
|
|
2183
|
-
export {
|
|
2407
|
+
export { DEFAULT_GRIDSTACK_OPTIONS, DEFAULT_WIDGET_STORAGE_TOKEN, SI_DASHBOARD_CONFIGURATION, SI_DASHBOARD_TOOLBAR_ITEMS, SI_WIDGET_ID_PROVIDER, SI_WIDGET_STORE, STORAGE_KEY, SiDashboardsNgModule, SiDefaultWidgetStorage, SiFlexibleDashboardComponent, SiGridComponent, SiWidgetCatalogComponent, SiWidgetDefaultIdProvider, SiWidgetIdProvider, SiWidgetInstanceEditorDialogComponent, SiWidgetRendererComponent, SiWidgetStorage, SimplDashboardsNgModule, createWidgetConfig, handleFederatedModuleLoad, provideDashboardToolbarItems, setupWidgetEditor, setupWidgetInstance, widgetFactoryRegistry };
|
|
2184
2408
|
//# sourceMappingURL=spike-rabbit-dashboards-ng.mjs.map
|