@openmfp/ngx 0.6.1
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/fesm2022/openmfp-ngx.mjs +1188 -0
- package/fesm2022/openmfp-ngx.mjs.map +1 -0
- package/package.json +34 -0
- package/types/openmfp-ngx.d.ts +437 -0
|
@@ -0,0 +1,1188 @@
|
|
|
1
|
+
import jsonpath from 'jsonpath';
|
|
2
|
+
import * as i0 from '@angular/core';
|
|
3
|
+
import { input, computed, CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, Component, output, signal, ViewEncapsulation, inject, effect, viewChild, Injector, afterNextRender, reflectComponentType, isStandalone, isDevMode, ViewContainerRef, Renderer2, model, ElementRef, linkedSignal } from '@angular/core';
|
|
4
|
+
import { Icon } from '@fundamental-ngx/ui5-webcomponents/icon';
|
|
5
|
+
import { Link } from '@fundamental-ngx/ui5-webcomponents/link';
|
|
6
|
+
import { Button } from '@fundamental-ngx/ui5-webcomponents/button';
|
|
7
|
+
import '@ui5/webcomponents-icons/dist/AllIcons.js';
|
|
8
|
+
import { IllustratedMessage } from '@fundamental-ngx/ui5-webcomponents-fiori';
|
|
9
|
+
import { Option } from '@fundamental-ngx/ui5-webcomponents/option';
|
|
10
|
+
import { Select } from '@fundamental-ngx/ui5-webcomponents/select';
|
|
11
|
+
import { Table } from '@fundamental-ngx/ui5-webcomponents/table';
|
|
12
|
+
import { TableCell } from '@fundamental-ngx/ui5-webcomponents/table-cell';
|
|
13
|
+
import { TableGrowing } from '@fundamental-ngx/ui5-webcomponents/table-growing';
|
|
14
|
+
import { TableHeaderCell } from '@fundamental-ngx/ui5-webcomponents/table-header-cell';
|
|
15
|
+
import { TableHeaderRow } from '@fundamental-ngx/ui5-webcomponents/table-header-row';
|
|
16
|
+
import { TableRow } from '@fundamental-ngx/ui5-webcomponents/table-row';
|
|
17
|
+
import '@ui5/webcomponents-fiori/dist/illustrations/NoData.js';
|
|
18
|
+
import * as i1 from '@angular/forms';
|
|
19
|
+
import { FormBuilder, FormControl, ReactiveFormsModule } from '@angular/forms';
|
|
20
|
+
import { Input } from '@fundamental-ngx/ui5-webcomponents/input';
|
|
21
|
+
import { Label } from '@fundamental-ngx/ui5-webcomponents/label';
|
|
22
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
23
|
+
import { Dialog } from '@fundamental-ngx/ui5-webcomponents/dialog';
|
|
24
|
+
import { Title } from '@fundamental-ngx/ui5-webcomponents/title';
|
|
25
|
+
import '@ui5/webcomponents-icons/dist/add.js';
|
|
26
|
+
import '@ui5/webcomponents-icons/dist/search.js';
|
|
27
|
+
import { debounceTime } from 'rxjs';
|
|
28
|
+
import { CheckBox } from '@fundamental-ngx/ui5-webcomponents/check-box';
|
|
29
|
+
import { Menu } from '@fundamental-ngx/ui5-webcomponents/menu';
|
|
30
|
+
import { MenuItem } from '@fundamental-ngx/ui5-webcomponents/menu-item';
|
|
31
|
+
import { MenuSeparator } from '@fundamental-ngx/ui5-webcomponents/menu-separator';
|
|
32
|
+
import { Text } from '@fundamental-ngx/ui5-webcomponents/text';
|
|
33
|
+
import '@ui5/webcomponents-icons/dist/action-settings.js';
|
|
34
|
+
import '@ui5/webcomponents-icons/dist/menu2.js';
|
|
35
|
+
import { GridstackComponent, GridstackItemComponent } from 'gridstack/dist/angular';
|
|
36
|
+
import { Card } from '@fundamental-ngx/ui5-webcomponents/card';
|
|
37
|
+
import { CardHeader } from '@fundamental-ngx/ui5-webcomponents/card-header';
|
|
38
|
+
import { List } from '@fundamental-ngx/ui5-webcomponents/list';
|
|
39
|
+
import { ListItemStandard } from '@fundamental-ngx/ui5-webcomponents/list-item-standard';
|
|
40
|
+
|
|
41
|
+
const processGroupFields = (fields) => {
|
|
42
|
+
return combineGroupFields(fields);
|
|
43
|
+
};
|
|
44
|
+
const collectGroupFields = (fields) => {
|
|
45
|
+
return fields.reduce((acc, f) => {
|
|
46
|
+
if (!f.group?.name) {
|
|
47
|
+
return acc;
|
|
48
|
+
}
|
|
49
|
+
const key = f.group.name;
|
|
50
|
+
if (!acc[key]) {
|
|
51
|
+
acc[key] = [];
|
|
52
|
+
}
|
|
53
|
+
// Strip group information from the field when adding to the fields array
|
|
54
|
+
const { group, ...fieldWithoutGroup } = f;
|
|
55
|
+
acc[key].push(fieldWithoutGroup);
|
|
56
|
+
return acc;
|
|
57
|
+
}, {});
|
|
58
|
+
};
|
|
59
|
+
const combineGroupFields = (fields) => {
|
|
60
|
+
const seenGroup = new Set();
|
|
61
|
+
const groupFields = collectGroupFields(fields);
|
|
62
|
+
const result = [];
|
|
63
|
+
fields.forEach((f) => {
|
|
64
|
+
if (!f.group?.name) {
|
|
65
|
+
result.push(f);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const key = f.group.name;
|
|
69
|
+
if (seenGroup.has(key)) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
seenGroup.add(key);
|
|
73
|
+
const grouped = {
|
|
74
|
+
...f,
|
|
75
|
+
group: {
|
|
76
|
+
...f.group,
|
|
77
|
+
fields: groupFields[key],
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
result.push(grouped);
|
|
81
|
+
});
|
|
82
|
+
return result;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const getResourceValueByJsonPath = (resource, field) => {
|
|
86
|
+
const property = field.jsonPathExpression || field.property;
|
|
87
|
+
if (!property) {
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
if (property instanceof Array) {
|
|
91
|
+
console.error(`Property defined as an array: ${JSON.stringify(property)}, provide "jsonPathExpression" field to properly ready resource value`);
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
const prefix = property.startsWith('$.') ? '' : '$.';
|
|
95
|
+
const queryResult = jsonpath.query(resource, `${prefix}${property}`);
|
|
96
|
+
const value = queryResult.length ? queryResult[0] : undefined;
|
|
97
|
+
if (value && field.propertyField) {
|
|
98
|
+
return executeTransform(value[field.propertyField.key], field.propertyField.transform);
|
|
99
|
+
}
|
|
100
|
+
return value;
|
|
101
|
+
};
|
|
102
|
+
const executeTransform = (value, transform) => {
|
|
103
|
+
if (value == null || transform == null || !transform.length)
|
|
104
|
+
return value;
|
|
105
|
+
return transform.reduce((acc, t) => {
|
|
106
|
+
if (acc == null)
|
|
107
|
+
return acc;
|
|
108
|
+
switch (t) {
|
|
109
|
+
case 'uppercase':
|
|
110
|
+
return acc.toUpperCase();
|
|
111
|
+
case 'lowercase':
|
|
112
|
+
return acc.toLowerCase();
|
|
113
|
+
case 'capitalize': {
|
|
114
|
+
const str = String(acc);
|
|
115
|
+
return str.length ? str.charAt(0).toUpperCase() + str.slice(1) : str;
|
|
116
|
+
}
|
|
117
|
+
case 'decode': {
|
|
118
|
+
try {
|
|
119
|
+
return decodeBase64(acc);
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
return acc;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
case 'encode': {
|
|
126
|
+
try {
|
|
127
|
+
return encodeBase64(acc);
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
return acc;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
default:
|
|
134
|
+
return acc;
|
|
135
|
+
}
|
|
136
|
+
}, value);
|
|
137
|
+
};
|
|
138
|
+
const encodeBase64 = (str) => {
|
|
139
|
+
try {
|
|
140
|
+
const utf8Bytes = new TextEncoder().encode(str);
|
|
141
|
+
const binaryString = Array.from(utf8Bytes, (byte) => String.fromCharCode(byte)).join('');
|
|
142
|
+
return btoa(binaryString);
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
console.error('Base64 encoding failed:', error);
|
|
146
|
+
throw new Error('Failed to encode string to Base64');
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
const decodeBase64 = (base64) => {
|
|
150
|
+
try {
|
|
151
|
+
const binaryString = atob(base64);
|
|
152
|
+
const bytes = Uint8Array.from(binaryString, (char) => char.charCodeAt(0));
|
|
153
|
+
return new TextDecoder().decode(bytes);
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
console.error('Base64 decoding failed:', error);
|
|
157
|
+
throw new Error('Failed to decode Base64 string');
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const parseStringValue = (value) => {
|
|
162
|
+
if (value === 'true') {
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
if (value === 'false') {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
if (!isNaN(Number(value))) {
|
|
169
|
+
return Number(value);
|
|
170
|
+
}
|
|
171
|
+
return value;
|
|
172
|
+
};
|
|
173
|
+
const cssRuleResolver = (rule, resourceValue) => {
|
|
174
|
+
const parsedResouceValue = parseStringValue(resourceValue);
|
|
175
|
+
const parsedConditionValue = parseStringValue(rule.if.value);
|
|
176
|
+
switch (rule.if.condition) {
|
|
177
|
+
case 'equals':
|
|
178
|
+
return parsedResouceValue === parsedConditionValue;
|
|
179
|
+
case 'notEquals':
|
|
180
|
+
return parsedResouceValue !== parsedConditionValue;
|
|
181
|
+
case 'greaterThan':
|
|
182
|
+
return parsedResouceValue > parsedConditionValue;
|
|
183
|
+
case 'greaterThanOrEqual':
|
|
184
|
+
return parsedResouceValue >= parsedConditionValue;
|
|
185
|
+
case 'lessThan':
|
|
186
|
+
return parsedResouceValue < parsedConditionValue;
|
|
187
|
+
case 'lessThanOrEqual':
|
|
188
|
+
return parsedResouceValue <= parsedConditionValue;
|
|
189
|
+
case 'contains':
|
|
190
|
+
if (Array.isArray(parsedResouceValue)) {
|
|
191
|
+
return parsedResouceValue.includes(parsedConditionValue);
|
|
192
|
+
}
|
|
193
|
+
else if (typeof parsedResouceValue === 'string' &&
|
|
194
|
+
typeof parsedConditionValue === 'string') {
|
|
195
|
+
return parsedResouceValue.includes(parsedConditionValue);
|
|
196
|
+
}
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
return false;
|
|
200
|
+
};
|
|
201
|
+
const evaluateCssRules = (value, rules) => {
|
|
202
|
+
if (!rules) {
|
|
203
|
+
return {};
|
|
204
|
+
}
|
|
205
|
+
return rules.reduce((acc, rule) => {
|
|
206
|
+
const result = cssRuleResolver(rule, value);
|
|
207
|
+
if (result) {
|
|
208
|
+
return { ...acc, ...rule.styles };
|
|
209
|
+
}
|
|
210
|
+
return acc;
|
|
211
|
+
}, {});
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
function getFieldValue(field, resource) {
|
|
215
|
+
if (resource) {
|
|
216
|
+
return getResourceValueByJsonPath(resource, field) ?? field.value;
|
|
217
|
+
}
|
|
218
|
+
return field.value;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const ICON_DESIGN_POSITIVE = 'Positive';
|
|
222
|
+
const ICON_DESIGN_NEGATIVE = 'Negative';
|
|
223
|
+
const ICON_NAME_POSITIVE = 'accept';
|
|
224
|
+
const ICON_NAME_NEGATIVE = 'decline';
|
|
225
|
+
|
|
226
|
+
class BooleanValue {
|
|
227
|
+
boolValue = input.required(...(ngDevMode ? [{ debugName: "boolValue" }] : /* istanbul ignore next */ []));
|
|
228
|
+
testId = input('boolean-value-icon', ...(ngDevMode ? [{ debugName: "testId" }] : /* istanbul ignore next */ []));
|
|
229
|
+
iconDesign = computed(() => {
|
|
230
|
+
return this.boolValue() ? ICON_DESIGN_POSITIVE : ICON_DESIGN_NEGATIVE;
|
|
231
|
+
}, ...(ngDevMode ? [{ debugName: "iconDesign" }] : /* istanbul ignore next */ []));
|
|
232
|
+
iconName = computed(() => {
|
|
233
|
+
return this.boolValue() ? ICON_NAME_POSITIVE : ICON_NAME_NEGATIVE;
|
|
234
|
+
}, ...(ngDevMode ? [{ debugName: "iconName" }] : /* istanbul ignore next */ []));
|
|
235
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: BooleanValue, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
236
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.12", type: BooleanValue, isStandalone: true, selector: "mfp-boolean-value", inputs: { boolValue: { classPropertyName: "boolValue", publicName: "boolValue", isSignal: true, isRequired: true, transformFunction: null }, testId: { classPropertyName: "testId", publicName: "testId", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<ui5-icon [attr.test-id]=\"testId()\" [design]=\"iconDesign()\" [name]=\"iconName()\" />\n", styles: [""], dependencies: [{ kind: "component", type: Icon, selector: "ui5-icon, [ui5-icon]", inputs: ["design", "name", "accessibleName", "showTooltip", "mode"], outputs: ["ui5Click"], exportAs: ["ui5Icon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
237
|
+
}
|
|
238
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: BooleanValue, decorators: [{
|
|
239
|
+
type: Component,
|
|
240
|
+
args: [{ selector: 'mfp-boolean-value', imports: [Icon], changeDetection: ChangeDetectionStrategy.OnPush, schemas: [CUSTOM_ELEMENTS_SCHEMA], template: "<ui5-icon [attr.test-id]=\"testId()\" [design]=\"iconDesign()\" [name]=\"iconName()\" />\n" }]
|
|
241
|
+
}], propDecorators: { boolValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "boolValue", required: true }] }], testId: [{ type: i0.Input, args: [{ isSignal: true, alias: "testId", required: false }] }] } });
|
|
242
|
+
|
|
243
|
+
class LinkValue {
|
|
244
|
+
urlValue = input.required(...(ngDevMode ? [{ debugName: "urlValue" }] : /* istanbul ignore next */ []));
|
|
245
|
+
testId = input('link-value-link', ...(ngDevMode ? [{ debugName: "testId" }] : /* istanbul ignore next */ []));
|
|
246
|
+
stopPropagation(event) {
|
|
247
|
+
event.stopPropagation();
|
|
248
|
+
}
|
|
249
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: LinkValue, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
250
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.12", type: LinkValue, isStandalone: true, selector: "mfp-link-value", inputs: { urlValue: { classPropertyName: "urlValue", publicName: "urlValue", isSignal: true, isRequired: true, transformFunction: null }, testId: { classPropertyName: "testId", publicName: "testId", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<ui5-link\n target=\"_blank\"\n [attr.test-id]=\"testId()\"\n [href]=\"urlValue()\"\n [tooltip]=\"urlValue()\"\n (click)=\"stopPropagation($event)\"\n>Link</ui5-link>\n", styles: [""], dependencies: [{ kind: "component", type: Link, selector: "ui5-link, [ui5-link]", inputs: ["disabled", "tooltip", "href", "target", "design", "interactiveAreaSize", "wrappingType", "accessibleName", "accessibleNameRef", "accessibleRole", "accessibilityAttributes", "accessibleDescription", "icon", "endIcon"], outputs: ["ui5Click"], exportAs: ["ui5Link"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
251
|
+
}
|
|
252
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: LinkValue, decorators: [{
|
|
253
|
+
type: Component,
|
|
254
|
+
args: [{ selector: 'mfp-link-value', imports: [Link], changeDetection: ChangeDetectionStrategy.OnPush, schemas: [CUSTOM_ELEMENTS_SCHEMA], template: "<ui5-link\n target=\"_blank\"\n [attr.test-id]=\"testId()\"\n [href]=\"urlValue()\"\n [tooltip]=\"urlValue()\"\n (click)=\"stopPropagation($event)\"\n>Link</ui5-link>\n" }]
|
|
255
|
+
}], propDecorators: { urlValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "urlValue", required: true }] }], testId: [{ type: i0.Input, args: [{ isSignal: true, alias: "testId", required: false }] }] } });
|
|
256
|
+
|
|
257
|
+
class SecretValue {
|
|
258
|
+
value = input.required(...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
259
|
+
isVisible = input(false, ...(ngDevMode ? [{ debugName: "isVisible" }] : /* istanbul ignore next */ []));
|
|
260
|
+
testId = input('secret-value', ...(ngDevMode ? [{ debugName: "testId" }] : /* istanbul ignore next */ []));
|
|
261
|
+
maskedValue = computed(() => '*'.repeat(this.value().length || 8), ...(ngDevMode ? [{ debugName: "maskedValue" }] : /* istanbul ignore next */ []));
|
|
262
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: SecretValue, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
263
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: SecretValue, isStandalone: true, selector: "mfp-secret-value", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, isVisible: { classPropertyName: "isVisible", publicName: "isVisible", isSignal: true, isRequired: false, transformFunction: null }, testId: { classPropertyName: "testId", publicName: "testId", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<span class=\"secret-value\" [attr.test-id]=\"testId()\" [class.masked]=\"!isVisible()\">\n @if (isVisible()) {\n {{ value() }}\n } @else {\n {{ maskedValue() }}\n }\n</span>\n", styles: [".secret-value{display:inline-flex;align-items:center;gap:.25rem}.masked{transform:translateY(.2em);display:flex}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
264
|
+
}
|
|
265
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: SecretValue, decorators: [{
|
|
266
|
+
type: Component,
|
|
267
|
+
args: [{ selector: 'mfp-secret-value', imports: [], changeDetection: ChangeDetectionStrategy.OnPush, schemas: [], template: "<span class=\"secret-value\" [attr.test-id]=\"testId()\" [class.masked]=\"!isVisible()\">\n @if (isVisible()) {\n {{ value() }}\n } @else {\n {{ maskedValue() }}\n }\n</span>\n", styles: [".secret-value{display:inline-flex;align-items:center;gap:.25rem}.masked{transform:translateY(.2em);display:flex}\n"] }]
|
|
268
|
+
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], isVisible: [{ type: i0.Input, args: [{ isSignal: true, alias: "isVisible", required: false }] }], testId: [{ type: i0.Input, args: [{ isSignal: true, alias: "testId", required: false }] }] } });
|
|
269
|
+
|
|
270
|
+
class ValueCell {
|
|
271
|
+
fieldDefinition = input.required(...(ngDevMode ? [{ debugName: "fieldDefinition" }] : /* istanbul ignore next */ []));
|
|
272
|
+
resource = input(...(ngDevMode ? [undefined, { debugName: "resource" }] : /* istanbul ignore next */ []));
|
|
273
|
+
buttonClick = output();
|
|
274
|
+
value = computed(() => getFieldValue(this.fieldDefinition(), this.resource()), ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
275
|
+
uiSettings = computed(() => this.fieldDefinition().uiSettings, ...(ngDevMode ? [{ debugName: "uiSettings" }] : /* istanbul ignore next */ []));
|
|
276
|
+
displayAs = computed(() => this.uiSettings()?.displayAs, ...(ngDevMode ? [{ debugName: "displayAs" }] : /* istanbul ignore next */ []));
|
|
277
|
+
withCopyButton = computed(() => this.uiSettings()?.withCopyButton, ...(ngDevMode ? [{ debugName: "withCopyButton" }] : /* istanbul ignore next */ []));
|
|
278
|
+
labelDisplay = computed(() => this.uiSettings()?.labelDisplay, ...(ngDevMode ? [{ debugName: "labelDisplay" }] : /* istanbul ignore next */ []));
|
|
279
|
+
cssCustomization = computed(() => this.uiSettings()?.cssCustomization, ...(ngDevMode ? [{ debugName: "cssCustomization" }] : /* istanbul ignore next */ []));
|
|
280
|
+
tooltipIcon = computed(() => this.uiSettings()?.tooltipIcon, ...(ngDevMode ? [{ debugName: "tooltipIcon" }] : /* istanbul ignore next */ []));
|
|
281
|
+
cssRules = computed(() => evaluateCssRules(this.value(), this.uiSettings()?.cssRules), ...(ngDevMode ? [{ debugName: "cssRules" }] : /* istanbul ignore next */ []));
|
|
282
|
+
cssStyles = computed(() => ({
|
|
283
|
+
...this.cssCustomization(),
|
|
284
|
+
...this.cssRules(),
|
|
285
|
+
}), ...(ngDevMode ? [{ debugName: "cssStyles" }] : /* istanbul ignore next */ []));
|
|
286
|
+
isBoolLike = computed(() => this.boolValue() !== undefined, ...(ngDevMode ? [{ debugName: "isBoolLike" }] : /* istanbul ignore next */ []));
|
|
287
|
+
isUrlValue = computed(() => this.checkValidUrl(this.stringValue()), ...(ngDevMode ? [{ debugName: "isUrlValue" }] : /* istanbul ignore next */ []));
|
|
288
|
+
testId = computed(() => `value-cell-${this.fieldDefinition().property}`, ...(ngDevMode ? [{ debugName: "testId" }] : /* istanbul ignore next */ []));
|
|
289
|
+
boolValue = computed(() => this.normalizeBoolean(this.value()), ...(ngDevMode ? [{ debugName: "boolValue" }] : /* istanbul ignore next */ []));
|
|
290
|
+
stringValue = computed(() => this.normalizeString(this.value()), ...(ngDevMode ? [{ debugName: "stringValue" }] : /* istanbul ignore next */ []));
|
|
291
|
+
isVisible = signal(false, ...(ngDevMode ? [{ debugName: "isVisible" }] : /* istanbul ignore next */ []));
|
|
292
|
+
toggleVisibility(e) {
|
|
293
|
+
e.stopPropagation();
|
|
294
|
+
this.isVisible.set(!this.isVisible());
|
|
295
|
+
}
|
|
296
|
+
normalizeBoolean(value) {
|
|
297
|
+
const normalizedValue = value?.toString()?.toLowerCase();
|
|
298
|
+
if (normalizedValue === 'true') {
|
|
299
|
+
return true;
|
|
300
|
+
}
|
|
301
|
+
if (normalizedValue === 'false') {
|
|
302
|
+
return false;
|
|
303
|
+
}
|
|
304
|
+
return undefined;
|
|
305
|
+
}
|
|
306
|
+
normalizeString(value) {
|
|
307
|
+
if (typeof value !== 'string' || !value.trim()) {
|
|
308
|
+
return undefined;
|
|
309
|
+
}
|
|
310
|
+
return value;
|
|
311
|
+
}
|
|
312
|
+
checkValidUrl(value) {
|
|
313
|
+
if (!value) {
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
try {
|
|
317
|
+
new URL(value);
|
|
318
|
+
return true;
|
|
319
|
+
}
|
|
320
|
+
catch {
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
copyValue(event) {
|
|
325
|
+
event.stopPropagation();
|
|
326
|
+
navigator.clipboard.writeText(this.value() || '');
|
|
327
|
+
}
|
|
328
|
+
buttonClicked(event) {
|
|
329
|
+
event.stopPropagation();
|
|
330
|
+
this.buttonClick.emit({
|
|
331
|
+
event,
|
|
332
|
+
field: this.fieldDefinition(),
|
|
333
|
+
resource: this.resource(),
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: ValueCell, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
337
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: ValueCell, isStandalone: true, selector: "mfp-value-cell", inputs: { fieldDefinition: { classPropertyName: "fieldDefinition", publicName: "fieldDefinition", isSignal: true, isRequired: true, transformFunction: null }, resource: { classPropertyName: "resource", publicName: "resource", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { buttonClick: "buttonClick" }, ngImport: i0, template: "@let displayType = displayAs();\n<span [attr.test-id]=\"testId()\" [class.label-value]=\"labelDisplay()\" [style]=\"cssStyles()\">\n @if (displayType === 'secret') {\n <span class=\"secret-value-container\">\n <mfp-secret-value [isVisible]=\"isVisible()\" [testId]=\"testId() + '-secret'\" [value]=\"value()\" />\n <ui5-icon\n class=\"toggle-icon\"\n [attr.test-id]=\"testId() + '-secret-toggle'\"\n [name]=\"isVisible() ? 'hide' : 'show'\"\n (click)=\"toggleVisibility($event)\"\n />\n </span>\n } @else if (displayType === 'boolIcon' && isBoolLike()) {\n <mfp-boolean-value [boolValue]=\"boolValue()!\" [testId]=\"testId() + '-boolean'\" />\n } @else if (displayType === 'link' && isUrlValue()) {\n <mfp-link-value [testId]=\"testId() + '-link'\" [urlValue]=\"stringValue()!\" />\n } @else if (displayType === 'tooltip') {\n <ui5-icon\n [accessibleName]=\"stringValue()!\"\n [attr.test-id]=\"testId() + '-tooltip'\"\n [name]=\"tooltipIcon() ?? 'hint'\"\n [showTooltip]=\"true\"\n />\n } @else if (displayType === 'alert') {\n @if (!value()) {\n <ui5-icon\n design=\"Critical\"\n name=\"alert\"\n [accessibleName]=\"resource()?.accessibleName ?? ''\"\n [attr.test-id]=\"testId() + '-icon'\"\n [showTooltip]=\"true\"\n (click)=\"$event.stopPropagation()\"\n />\n }\n } @else if (displayType === 'img' && value()) {\n <img alt=\"Resource image\" class=\"image-cell\" [src]=\"value()\" />\n } @else if (displayType === 'button') {\n @let buttonSettings = uiSettings()?.buttonSettings;\n <ui5-button\n [design]=\"buttonSettings?.design\"\n [endIcon]=\"buttonSettings?.endIcon\"\n [icon]=\"buttonSettings?.icon\"\n [tooltip]=\"buttonSettings?.tooltip\"\n (click)=\"buttonClicked($event)\"\n >{{ buttonSettings?.text }}</ui5-button\n > \n } @else {\n {{ value() }}\n }\n</span>\n\n@if (withCopyButton()) {\n <ui5-icon\n class=\"toggle-icon\"\n name=\"copy\"\n [attr.test-id]=\"testId() + '-copy'\"\n (click)=\"copyValue($event)\"\n />\n}\n", styles: [":host{display:inline-flex;align-items:center}.label-value{background-color:#01b4ff;color:#fff;border-radius:1.4rem;padding-inline:.4375rem;letter-spacing:-.00875rem;display:inline-block;border:0;font-size:.875rem;line-height:1.4rem;margin:3px}.secret-value-container{display:flex;align-items:center;gap:.25rem}.toggle-icon{cursor:pointer;transition:color .2s ease;padding:.25rem}.toggle-icon:hover{color:var(--sapButton_Hover_TextColor, #0854a0)}\n"], dependencies: [{ kind: "component", type: Icon, selector: "ui5-icon, [ui5-icon]", inputs: ["design", "name", "accessibleName", "showTooltip", "mode"], outputs: ["ui5Click"], exportAs: ["ui5Icon"] }, { kind: "component", type: BooleanValue, selector: "mfp-boolean-value", inputs: ["boolValue", "testId"] }, { kind: "component", type: LinkValue, selector: "mfp-link-value", inputs: ["urlValue", "testId"] }, { kind: "component", type: SecretValue, selector: "mfp-secret-value", inputs: ["value", "isVisible", "testId"] }, { kind: "component", type: Button, selector: "ui5-button, [ui5-button]", inputs: ["design", "disabled", "icon", "endIcon", "submits", "tooltip", "accessibleName", "accessibleNameRef", "accessibilityAttributes", "accessibleDescription", "type", "accessibleRole", "loading", "loadingDelay"], outputs: ["ui5Click"], exportAs: ["ui5Button"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.ShadowDom });
|
|
338
|
+
}
|
|
339
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: ValueCell, decorators: [{
|
|
340
|
+
type: Component,
|
|
341
|
+
args: [{ selector: 'mfp-value-cell', imports: [Icon, BooleanValue, LinkValue, SecretValue, Button], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.ShadowDom, schemas: [CUSTOM_ELEMENTS_SCHEMA], template: "@let displayType = displayAs();\n<span [attr.test-id]=\"testId()\" [class.label-value]=\"labelDisplay()\" [style]=\"cssStyles()\">\n @if (displayType === 'secret') {\n <span class=\"secret-value-container\">\n <mfp-secret-value [isVisible]=\"isVisible()\" [testId]=\"testId() + '-secret'\" [value]=\"value()\" />\n <ui5-icon\n class=\"toggle-icon\"\n [attr.test-id]=\"testId() + '-secret-toggle'\"\n [name]=\"isVisible() ? 'hide' : 'show'\"\n (click)=\"toggleVisibility($event)\"\n />\n </span>\n } @else if (displayType === 'boolIcon' && isBoolLike()) {\n <mfp-boolean-value [boolValue]=\"boolValue()!\" [testId]=\"testId() + '-boolean'\" />\n } @else if (displayType === 'link' && isUrlValue()) {\n <mfp-link-value [testId]=\"testId() + '-link'\" [urlValue]=\"stringValue()!\" />\n } @else if (displayType === 'tooltip') {\n <ui5-icon\n [accessibleName]=\"stringValue()!\"\n [attr.test-id]=\"testId() + '-tooltip'\"\n [name]=\"tooltipIcon() ?? 'hint'\"\n [showTooltip]=\"true\"\n />\n } @else if (displayType === 'alert') {\n @if (!value()) {\n <ui5-icon\n design=\"Critical\"\n name=\"alert\"\n [accessibleName]=\"resource()?.accessibleName ?? ''\"\n [attr.test-id]=\"testId() + '-icon'\"\n [showTooltip]=\"true\"\n (click)=\"$event.stopPropagation()\"\n />\n }\n } @else if (displayType === 'img' && value()) {\n <img alt=\"Resource image\" class=\"image-cell\" [src]=\"value()\" />\n } @else if (displayType === 'button') {\n @let buttonSettings = uiSettings()?.buttonSettings;\n <ui5-button\n [design]=\"buttonSettings?.design\"\n [endIcon]=\"buttonSettings?.endIcon\"\n [icon]=\"buttonSettings?.icon\"\n [tooltip]=\"buttonSettings?.tooltip\"\n (click)=\"buttonClicked($event)\"\n >{{ buttonSettings?.text }}</ui5-button\n > \n } @else {\n {{ value() }}\n }\n</span>\n\n@if (withCopyButton()) {\n <ui5-icon\n class=\"toggle-icon\"\n name=\"copy\"\n [attr.test-id]=\"testId() + '-copy'\"\n (click)=\"copyValue($event)\"\n />\n}\n", styles: [":host{display:inline-flex;align-items:center}.label-value{background-color:#01b4ff;color:#fff;border-radius:1.4rem;padding-inline:.4375rem;letter-spacing:-.00875rem;display:inline-block;border:0;font-size:.875rem;line-height:1.4rem;margin:3px}.secret-value-container{display:flex;align-items:center;gap:.25rem}.toggle-icon{cursor:pointer;transition:color .2s ease;padding:.25rem}.toggle-icon:hover{color:var(--sapButton_Hover_TextColor, #0854a0)}\n"] }]
|
|
342
|
+
}], propDecorators: { fieldDefinition: [{ type: i0.Input, args: [{ isSignal: true, alias: "fieldDefinition", required: true }] }], resource: [{ type: i0.Input, args: [{ isSignal: true, alias: "resource", required: false }] }], buttonClick: [{ type: i0.Output, args: ["buttonClick"] }] } });
|
|
343
|
+
|
|
344
|
+
class DeclarativeTable {
|
|
345
|
+
columns = input.required(...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
|
|
346
|
+
resources = input.required(...(ngDevMode ? [{ debugName: "resources" }] : /* istanbul ignore next */ []));
|
|
347
|
+
trackByPath = input('id', ...(ngDevMode ? [{ debugName: "trackByPath" }] : /* istanbul ignore next */ []));
|
|
348
|
+
totalItemsCount = input(...(ngDevMode ? [undefined, { debugName: "totalItemsCount" }] : /* istanbul ignore next */ []));
|
|
349
|
+
paginationLimit = input(5, ...(ngDevMode ? [{ debugName: "paginationLimit" }] : /* istanbul ignore next */ []));
|
|
350
|
+
hasMore = input(false, ...(ngDevMode ? [{ debugName: "hasMore" }] : /* istanbul ignore next */ []));
|
|
351
|
+
buttonClick = output();
|
|
352
|
+
tableRowClicked = output();
|
|
353
|
+
loadMoreResources = output();
|
|
354
|
+
paginationLimitChanged = output();
|
|
355
|
+
columnTrackBy = (column, index) => column.property ?? column.value ?? index;
|
|
356
|
+
rowTrackBy = (_index, item) => getResourceValueByJsonPath(item, { property: this.trackByPath() }) ?? _index;
|
|
357
|
+
viewColumns = computed(() => processGroupFields(this.columns()), ...(ngDevMode ? [{ debugName: "viewColumns" }] : /* istanbul ignore next */ []));
|
|
358
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: DeclarativeTable, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
359
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: DeclarativeTable, isStandalone: true, selector: "mfp-declarative-table", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, resources: { classPropertyName: "resources", publicName: "resources", isSignal: true, isRequired: true, transformFunction: null }, trackByPath: { classPropertyName: "trackByPath", publicName: "trackByPath", isSignal: true, isRequired: false, transformFunction: null }, totalItemsCount: { classPropertyName: "totalItemsCount", publicName: "totalItemsCount", isSignal: true, isRequired: false, transformFunction: null }, paginationLimit: { classPropertyName: "paginationLimit", publicName: "paginationLimit", isSignal: true, isRequired: false, transformFunction: null }, hasMore: { classPropertyName: "hasMore", publicName: "hasMore", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { buttonClick: "buttonClick", tableRowClicked: "tableRowClicked", loadMoreResources: "loadMoreResources", paginationLimitChanged: "paginationLimitChanged" }, ngImport: i0, template: "<ui5-table test-id=\"generic-table\">\n <ui5-table-header-row slot=\"headerRow\">\n @for (column of viewColumns(); track columnTrackBy(column, $index)) {\n @if (column.group) {\n <ui5-table-header-cell\n [attr.test-id]=\"'generic-table-header-' + column.property\"\n >{{ column.group.label ?? column.group.name }}</ui5-table-header-cell\n >\n } @else {\n <ui5-table-header-cell\n [attr.test-id]=\"'generic-table-header-' + column.property\"\n [width]=\"column.uiSettings?.columnWidth ?? 'auto'\"\n >{{ column.label }}</ui5-table-header-cell\n >\n }\n }\n </ui5-table-header-row>\n\n @for (item of resources(); let i = $index; track rowTrackBy($index, item)) {\n <ui5-table-row\n [attr.test-id]=\"'generic-table-row-' + i\"\n [class.disabled]=\"!(item.isAvailable ?? true)\"\n [interactive]=\"item.isAvailable ?? true\"\n (click)=\"tableRowClicked.emit(item)\"\n >\n @for (column of viewColumns(); track columnTrackBy(column, $index)) {\n @if (column.group) {\n <ui5-table-cell\n [attr.test-id]=\"'generic-table-cell-' + i + '-' + column.property\"\n [class.multiline]=\"column.group.multiline ?? true\"\n >\n @for (\n field of column.group.fields;\n let last = $last;\n track columnTrackBy(field, $index)\n ) {\n <span\n class=\"group-value\"\n [attr.test-id]=\"\n 'generic-table-cell-' +\n i +\n '-' +\n column.property +\n '-' +\n field.property\n \"\n >\n @if (field.label) {\n <span>{{ field.label }}: </span>\n }\n <mfp-value-cell\n [fieldDefinition]=\"field\"\n [resource]=\"item\"\n (buttonClick)=\"buttonClick.emit($event)\"\n />\n </span>\n @if (!last && column.group.delimiter) {\n <span>{{ column.group.delimiter }}</span>\n }\n }\n </ui5-table-cell>\n } @else {\n <ui5-table-cell\n [attr.test-id]=\"'generic-table-cell-' + i + '-' + column.property\"\n >\n <mfp-value-cell\n [fieldDefinition]=\"column\"\n [resource]=\"item\"\n (buttonClick)=\"buttonClick.emit($event)\"\n />\n </ui5-table-cell>\n }\n }\n </ui5-table-row>\n } @empty {\n <ui5-illustrated-message\n name=\"NoData\"\n slot=\"noData\"\n test-id=\"generic-table-view-nodata\"\n >\n <span slot=\"title\">No Resources</span>\n <span slot=\"subtitle\">There are currently no items to show.</span>\n </ui5-illustrated-message>\n }\n\n @if (hasMore()) {\n <ui5-table-growing\n id=\"growing\"\n mode=\"Button\"\n slot=\"features\"\n text=\"Load more\"\n (ui5LoadMore)=\"loadMoreResources.emit()\"\n />\n }\n</ui5-table>\n\n<!-- Pagination Control -->\n<div\n class=\"pagination-footer\"\n style=\"\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0rem 1rem;\n min-height: 3rem;\n border-top: 1px solid var(--sapList_BorderColor);\n \"\n>\n <div style=\"display: flex; align-items: center; gap: 1.5rem\">\n <div style=\"display: flex; align-items: center; gap: 0.5rem\">\n <span\n style=\"color: var(--sapTextColor); font-size: var(--sapFontSmallSize)\"\n >Items per load:</span\n >\n <ui5-select\n style=\"width: 5rem\"\n (change)=\"paginationLimitChanged.emit(+$any($event)?.target?.value)\"\n [value]=\"paginationLimit().toString()\"\n >\n <ui5-option value=\"5\">5</ui5-option>\n <ui5-option value=\"10\">10</ui5-option>\n <ui5-option value=\"50\">50</ui5-option>\n <ui5-option value=\"100\">100</ui5-option>\n </ui5-select>\n </div>\n\n <span\n style=\"color: var(--sapTextColor); font-size: var(--sapFontSmallSize)\"\n >\n Items loaded:\n <b>{{\n totalItemsCount() !== undefined\n ? resources().length + ' / ' + totalItemsCount()\n : resources().length\n }}</b>\n </span>\n </div>\n</div>\n", styles: [".multiline{display:block}.disabled{filter:brightness(.92);pointer-events:none}.disabled ui5-table-cell:not(:first-child){color:var(--sapContent_DisabledTextColor, #6a6d70)}.disabled ui5-table-cell:not(:first-child) ui5-icon{color:var(--sapContent_DisabledTextColor, #6a6d70)}.group-value{display:flex;align-items:center}\n"], dependencies: [{ kind: "component", type: IllustratedMessage, selector: "ui5-illustrated-message, [ui5-illustrated-message]", inputs: ["name", "design", "subtitleText", "titleText", "accessibleNameRef", "decorative"], exportAs: ["ui5IllustratedMessage"] }, { kind: "component", type: Table, selector: "ui5-table, [ui5-table]", inputs: ["accessibleName", "accessibleNameRef", "noDataText", "overflowMode", "loading", "loadingDelay", "rowActionCount", "alternateRowColors"], outputs: ["ui5RowClick", "ui5MoveOver", "ui5Move", "ui5RowActionClick"], exportAs: ["ui5Table"] }, { kind: "component", type: TableCell, selector: "ui5-table-cell, [ui5-table-cell]", inputs: ["horizontalAlign"], exportAs: ["ui5TableCell"] }, { kind: "component", type: TableHeaderCell, selector: "ui5-table-header-cell, [ui5-table-header-cell]", inputs: ["width", "minWidth", "importance", "popinText", "sortIndicator", "popinHidden", "horizontalAlign"], exportAs: ["ui5TableHeaderCell"] }, { kind: "component", type: TableHeaderRow, selector: "ui5-table-header-row, [ui5-table-header-row]", inputs: ["sticky"], exportAs: ["ui5TableHeaderRow"] }, { kind: "component", type: TableRow, selector: "ui5-table-row, [ui5-table-row]", inputs: ["rowKey", "position", "interactive", "navigated", "movable"], exportAs: ["ui5TableRow"] }, { kind: "component", type: ValueCell, selector: "mfp-value-cell", inputs: ["fieldDefinition", "resource"], outputs: ["buttonClick"] }, { kind: "component", type: Select, selector: "ui5-select, [ui5-select]", inputs: ["disabled", "name", "valueState", "required", "readonly", "accessibleName", "accessibleNameRef", "accessibleDescription", "accessibleDescriptionRef", "tooltip", "textSeparator", "value"], outputs: ["ui5Change", "ui5LiveChange", "ui5Open", "ui5Close"], exportAs: ["ui5Select"] }, { kind: "component", type: Option, selector: "ui5-option, [ui5-option]", inputs: ["value", "icon", "additionalText", "tooltip", "selected"], exportAs: ["ui5Option"] }, { kind: "component", type: TableGrowing, selector: "ui5-table-growing, [ui5-table-growing]", inputs: ["mode", "text", "subtext"], outputs: ["ui5LoadMore"], exportAs: ["ui5TableGrowing"] }], encapsulation: i0.ViewEncapsulation.ShadowDom });
|
|
360
|
+
}
|
|
361
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: DeclarativeTable, decorators: [{
|
|
362
|
+
type: Component,
|
|
363
|
+
args: [{ selector: 'mfp-declarative-table', imports: [
|
|
364
|
+
IllustratedMessage,
|
|
365
|
+
Table,
|
|
366
|
+
TableCell,
|
|
367
|
+
TableHeaderCell,
|
|
368
|
+
TableHeaderRow,
|
|
369
|
+
TableRow,
|
|
370
|
+
ValueCell,
|
|
371
|
+
Select,
|
|
372
|
+
Option,
|
|
373
|
+
TableGrowing,
|
|
374
|
+
], encapsulation: ViewEncapsulation.ShadowDom, template: "<ui5-table test-id=\"generic-table\">\n <ui5-table-header-row slot=\"headerRow\">\n @for (column of viewColumns(); track columnTrackBy(column, $index)) {\n @if (column.group) {\n <ui5-table-header-cell\n [attr.test-id]=\"'generic-table-header-' + column.property\"\n >{{ column.group.label ?? column.group.name }}</ui5-table-header-cell\n >\n } @else {\n <ui5-table-header-cell\n [attr.test-id]=\"'generic-table-header-' + column.property\"\n [width]=\"column.uiSettings?.columnWidth ?? 'auto'\"\n >{{ column.label }}</ui5-table-header-cell\n >\n }\n }\n </ui5-table-header-row>\n\n @for (item of resources(); let i = $index; track rowTrackBy($index, item)) {\n <ui5-table-row\n [attr.test-id]=\"'generic-table-row-' + i\"\n [class.disabled]=\"!(item.isAvailable ?? true)\"\n [interactive]=\"item.isAvailable ?? true\"\n (click)=\"tableRowClicked.emit(item)\"\n >\n @for (column of viewColumns(); track columnTrackBy(column, $index)) {\n @if (column.group) {\n <ui5-table-cell\n [attr.test-id]=\"'generic-table-cell-' + i + '-' + column.property\"\n [class.multiline]=\"column.group.multiline ?? true\"\n >\n @for (\n field of column.group.fields;\n let last = $last;\n track columnTrackBy(field, $index)\n ) {\n <span\n class=\"group-value\"\n [attr.test-id]=\"\n 'generic-table-cell-' +\n i +\n '-' +\n column.property +\n '-' +\n field.property\n \"\n >\n @if (field.label) {\n <span>{{ field.label }}: </span>\n }\n <mfp-value-cell\n [fieldDefinition]=\"field\"\n [resource]=\"item\"\n (buttonClick)=\"buttonClick.emit($event)\"\n />\n </span>\n @if (!last && column.group.delimiter) {\n <span>{{ column.group.delimiter }}</span>\n }\n }\n </ui5-table-cell>\n } @else {\n <ui5-table-cell\n [attr.test-id]=\"'generic-table-cell-' + i + '-' + column.property\"\n >\n <mfp-value-cell\n [fieldDefinition]=\"column\"\n [resource]=\"item\"\n (buttonClick)=\"buttonClick.emit($event)\"\n />\n </ui5-table-cell>\n }\n }\n </ui5-table-row>\n } @empty {\n <ui5-illustrated-message\n name=\"NoData\"\n slot=\"noData\"\n test-id=\"generic-table-view-nodata\"\n >\n <span slot=\"title\">No Resources</span>\n <span slot=\"subtitle\">There are currently no items to show.</span>\n </ui5-illustrated-message>\n }\n\n @if (hasMore()) {\n <ui5-table-growing\n id=\"growing\"\n mode=\"Button\"\n slot=\"features\"\n text=\"Load more\"\n (ui5LoadMore)=\"loadMoreResources.emit()\"\n />\n }\n</ui5-table>\n\n<!-- Pagination Control -->\n<div\n class=\"pagination-footer\"\n style=\"\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0rem 1rem;\n min-height: 3rem;\n border-top: 1px solid var(--sapList_BorderColor);\n \"\n>\n <div style=\"display: flex; align-items: center; gap: 1.5rem\">\n <div style=\"display: flex; align-items: center; gap: 0.5rem\">\n <span\n style=\"color: var(--sapTextColor); font-size: var(--sapFontSmallSize)\"\n >Items per load:</span\n >\n <ui5-select\n style=\"width: 5rem\"\n (change)=\"paginationLimitChanged.emit(+$any($event)?.target?.value)\"\n [value]=\"paginationLimit().toString()\"\n >\n <ui5-option value=\"5\">5</ui5-option>\n <ui5-option value=\"10\">10</ui5-option>\n <ui5-option value=\"50\">50</ui5-option>\n <ui5-option value=\"100\">100</ui5-option>\n </ui5-select>\n </div>\n\n <span\n style=\"color: var(--sapTextColor); font-size: var(--sapFontSmallSize)\"\n >\n Items loaded:\n <b>{{\n totalItemsCount() !== undefined\n ? resources().length + ' / ' + totalItemsCount()\n : resources().length\n }}</b>\n </span>\n </div>\n</div>\n", styles: [".multiline{display:block}.disabled{filter:brightness(.92);pointer-events:none}.disabled ui5-table-cell:not(:first-child){color:var(--sapContent_DisabledTextColor, #6a6d70)}.disabled ui5-table-cell:not(:first-child) ui5-icon{color:var(--sapContent_DisabledTextColor, #6a6d70)}.group-value{display:flex;align-items:center}\n"] }]
|
|
375
|
+
}], propDecorators: { columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], resources: [{ type: i0.Input, args: [{ isSignal: true, alias: "resources", required: true }] }], trackByPath: [{ type: i0.Input, args: [{ isSignal: true, alias: "trackByPath", required: false }] }], totalItemsCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalItemsCount", required: false }] }], paginationLimit: [{ type: i0.Input, args: [{ isSignal: true, alias: "paginationLimit", required: false }] }], hasMore: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasMore", required: false }] }], buttonClick: [{ type: i0.Output, args: ["buttonClick"] }], tableRowClicked: [{ type: i0.Output, args: ["tableRowClicked"] }], loadMoreResources: [{ type: i0.Output, args: ["loadMoreResources"] }], paginationLimitChanged: [{ type: i0.Output, args: ["paginationLimitChanged"] }] } });
|
|
376
|
+
|
|
377
|
+
function setPropertyByPath(object, path, value) {
|
|
378
|
+
const segments = path.split('.').filter(Boolean);
|
|
379
|
+
if (segments.length === 0) {
|
|
380
|
+
return object;
|
|
381
|
+
}
|
|
382
|
+
let current = object;
|
|
383
|
+
for (let i = 0; i < segments.length; i += 1) {
|
|
384
|
+
const key = segments[i];
|
|
385
|
+
if (i === segments.length - 1) {
|
|
386
|
+
current[key] = value;
|
|
387
|
+
break;
|
|
388
|
+
}
|
|
389
|
+
const existing = current[key];
|
|
390
|
+
if (existing === undefined || existing === null || typeof existing !== 'object') {
|
|
391
|
+
current[key] = {};
|
|
392
|
+
}
|
|
393
|
+
current = current[key];
|
|
394
|
+
}
|
|
395
|
+
return object;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
class DeclarativeForm {
|
|
399
|
+
fields = input.required(...(ngDevMode ? [{ debugName: "fields" }] : /* istanbul ignore next */ []));
|
|
400
|
+
initialValues = input({}, ...(ngDevMode ? [{ debugName: "initialValues" }] : /* istanbul ignore next */ []));
|
|
401
|
+
fieldErrors = input({}, ...(ngDevMode ? [{ debugName: "fieldErrors" }] : /* istanbul ignore next */ []));
|
|
402
|
+
fieldChange = output();
|
|
403
|
+
formSubmit = output();
|
|
404
|
+
form;
|
|
405
|
+
fb = inject(FormBuilder);
|
|
406
|
+
constructor() {
|
|
407
|
+
this.form = this.fb.group({});
|
|
408
|
+
effect(() => {
|
|
409
|
+
this.rebuildControls(this.fields());
|
|
410
|
+
});
|
|
411
|
+
effect(() => {
|
|
412
|
+
this.setInitialValues(this.initialValues());
|
|
413
|
+
});
|
|
414
|
+
effect(() => {
|
|
415
|
+
const fields = this.fields();
|
|
416
|
+
this.initialValues();
|
|
417
|
+
for (const field of fields) {
|
|
418
|
+
if (field.validation) {
|
|
419
|
+
this.fieldChange.emit({
|
|
420
|
+
fieldProperty: field.name,
|
|
421
|
+
value: this.form.controls[field.name]?.value ?? '',
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
setFormControlValue($event, field) {
|
|
428
|
+
const target = $event.target;
|
|
429
|
+
const control = this.form.controls[field.name];
|
|
430
|
+
control.setValue(target.value);
|
|
431
|
+
control.markAsTouched();
|
|
432
|
+
control.markAsDirty();
|
|
433
|
+
if (field.validation === 'onChange') {
|
|
434
|
+
this.fieldChange.emit({
|
|
435
|
+
fieldProperty: field.name,
|
|
436
|
+
value: control.value,
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
getError(name) {
|
|
441
|
+
const control = this.form.controls[name];
|
|
442
|
+
const error = this.fieldErrors()[name];
|
|
443
|
+
return error && (control.dirty || control.touched) ? error : null;
|
|
444
|
+
}
|
|
445
|
+
getValueState(name) {
|
|
446
|
+
return this.getError(name) ? 'Negative' : 'None';
|
|
447
|
+
}
|
|
448
|
+
onFieldBlur(field) {
|
|
449
|
+
this.form.controls[field.name]?.markAsTouched();
|
|
450
|
+
if (field.validation === 'onBlur') {
|
|
451
|
+
this.fieldChange.emit({
|
|
452
|
+
fieldProperty: field.name,
|
|
453
|
+
value: this.form.controls[field.name]?.value,
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
submit() {
|
|
458
|
+
this.formSubmit.emit(this.buildOutputValue());
|
|
459
|
+
}
|
|
460
|
+
clear() {
|
|
461
|
+
this.form.reset();
|
|
462
|
+
}
|
|
463
|
+
rebuildControls(fields) {
|
|
464
|
+
const existingControls = this.form.controls;
|
|
465
|
+
const nextFieldNames = new Set(fields.map((field) => field.name));
|
|
466
|
+
for (const field of fields) {
|
|
467
|
+
const existingControl = existingControls[field.name];
|
|
468
|
+
if (existingControl) {
|
|
469
|
+
existingControl.clearValidators();
|
|
470
|
+
existingControl.updateValueAndValidity({ emitEvent: false });
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
473
|
+
this.form.addControl(field.name, new FormControl(''));
|
|
474
|
+
}
|
|
475
|
+
for (const controlName of Object.keys(existingControls)) {
|
|
476
|
+
if (!nextFieldNames.has(controlName)) {
|
|
477
|
+
this.form.removeControl(controlName);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
this.form.updateValueAndValidity({ emitEvent: false });
|
|
481
|
+
}
|
|
482
|
+
setInitialValues(initialValues) {
|
|
483
|
+
if (!initialValues) {
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
this.form.patchValue(initialValues, { emitEvent: false });
|
|
487
|
+
this.form.markAsPristine({ emitEvent: false });
|
|
488
|
+
this.form.updateValueAndValidity({ emitEvent: false });
|
|
489
|
+
}
|
|
490
|
+
buildOutputValue() {
|
|
491
|
+
const result = {};
|
|
492
|
+
for (const key of Object.keys(this.form.controls)) {
|
|
493
|
+
setPropertyByPath(result, key, this.form.controls[key].value);
|
|
494
|
+
}
|
|
495
|
+
return result;
|
|
496
|
+
}
|
|
497
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: DeclarativeForm, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
498
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: DeclarativeForm, isStandalone: true, selector: "mfp-declarative-form", inputs: { fields: { classPropertyName: "fields", publicName: "fields", isSignal: true, isRequired: true, transformFunction: null }, initialValues: { classPropertyName: "initialValues", publicName: "initialValues", isSignal: true, isRequired: false, transformFunction: null }, fieldErrors: { classPropertyName: "fieldErrors", publicName: "fieldErrors", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { fieldChange: "fieldChange", formSubmit: "formSubmit" }, ngImport: i0, template: "<section test-id=\"generic-form\" class=\"form\" [formGroup]=\"form\">\n @for (field of fields(); track field.name) {\n @let control = form.controls[field.name];\n <div\n class=\"inputs\"\n [attr.test-id]=\"'generic-form-field-container-' + field.name\"\n >\n <ui5-label\n [attr.test-id]=\"'generic-form-field-label-' + field.name\"\n show-colon\n [required]=\"field.required\"\n >{{ field.label }}</ui5-label\n >\n @if (field.values?.length) {\n <ui5-select\n class=\"input\"\n [attr.test-id]=\"'generic-form-field-' + field.name\"\n [value]=\"control.value\"\n (input)=\"setFormControlValue($event, field)\"\n (change)=\"setFormControlValue($event, field)\"\n (blur)=\"onFieldBlur(field)\"\n [required]=\"field.required\"\n [valueState]=\"getValueState(field.name)\"\n [disabled]=\"field.disabled\"\n >\n @if (getError(field.name); as error) {\n <div slot=\"valueStateMessage\">{{ error }}</div>\n }\n @for (value of [''].concat(field.values ?? []); track value) {\n <ui5-option\n [attr.test-id]=\"\n 'generic-form-field-' +\n field.name +\n '-option-' +\n (value || 'empty')\n \"\n [value]=\"value\"\n [selected]=\"value === control.value\"\n >{{ value }}</ui5-option\n >\n }\n </ui5-select>\n } @else {\n <ui5-input\n class=\"input\"\n [attr.test-id]=\"'generic-form-field-' + field.name\"\n [value]=\"control.value\"\n (blur)=\"onFieldBlur(field)\"\n (change)=\"setFormControlValue($event, field)\"\n (input)=\"setFormControlValue($event, field)\"\n [required]=\"field.required\"\n [valueState]=\"getValueState(field.name)\"\n [disabled]=\"field.disabled\"\n >\n @if (getError(field.name); as error) {\n <div slot=\"valueStateMessage\">{{ error }}</div>\n }\n </ui5-input>\n }\n </div>\n }\n</section>\n", styles: [".form>div{display:flex;flex-direction:column;justify-content:space-evenly;align-items:flex-start;margin-bottom:.5rem;width:100%}.input{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: Input, selector: "ui5-input, [ui5-input]", inputs: ["disabled", "placeholder", "readonly", "required", "noTypeahead", "type", "value", "valueState", "name", "showSuggestions", "maxlength", "accessibleName", "accessibleNameRef", "accessibleDescription", "accessibleDescriptionRef", "showClearIcon", "open", "filter"], outputs: ["ui5Change", "ui5Input", "ui5Select", "ui5SelectionChange", "ui5Open", "ui5Close"], exportAs: ["ui5Input"] }, { kind: "component", type: Label, selector: "ui5-label, [ui5-label]", inputs: ["for", "showColon", "required", "wrappingType"], exportAs: ["ui5Label"] }, { kind: "component", type: Select, selector: "ui5-select, [ui5-select]", inputs: ["disabled", "name", "valueState", "required", "readonly", "accessibleName", "accessibleNameRef", "accessibleDescription", "accessibleDescriptionRef", "tooltip", "textSeparator", "value"], outputs: ["ui5Change", "ui5LiveChange", "ui5Open", "ui5Close"], exportAs: ["ui5Select"] }, { kind: "component", type: Option, selector: "ui5-option, [ui5-option]", inputs: ["value", "icon", "additionalText", "tooltip", "selected"], exportAs: ["ui5Option"] }], encapsulation: i0.ViewEncapsulation.ShadowDom });
|
|
499
|
+
}
|
|
500
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: DeclarativeForm, decorators: [{
|
|
501
|
+
type: Component,
|
|
502
|
+
args: [{ selector: 'mfp-declarative-form', imports: [ReactiveFormsModule, Input, Label, Select, Option], encapsulation: ViewEncapsulation.ShadowDom, template: "<section test-id=\"generic-form\" class=\"form\" [formGroup]=\"form\">\n @for (field of fields(); track field.name) {\n @let control = form.controls[field.name];\n <div\n class=\"inputs\"\n [attr.test-id]=\"'generic-form-field-container-' + field.name\"\n >\n <ui5-label\n [attr.test-id]=\"'generic-form-field-label-' + field.name\"\n show-colon\n [required]=\"field.required\"\n >{{ field.label }}</ui5-label\n >\n @if (field.values?.length) {\n <ui5-select\n class=\"input\"\n [attr.test-id]=\"'generic-form-field-' + field.name\"\n [value]=\"control.value\"\n (input)=\"setFormControlValue($event, field)\"\n (change)=\"setFormControlValue($event, field)\"\n (blur)=\"onFieldBlur(field)\"\n [required]=\"field.required\"\n [valueState]=\"getValueState(field.name)\"\n [disabled]=\"field.disabled\"\n >\n @if (getError(field.name); as error) {\n <div slot=\"valueStateMessage\">{{ error }}</div>\n }\n @for (value of [''].concat(field.values ?? []); track value) {\n <ui5-option\n [attr.test-id]=\"\n 'generic-form-field-' +\n field.name +\n '-option-' +\n (value || 'empty')\n \"\n [value]=\"value\"\n [selected]=\"value === control.value\"\n >{{ value }}</ui5-option\n >\n }\n </ui5-select>\n } @else {\n <ui5-input\n class=\"input\"\n [attr.test-id]=\"'generic-form-field-' + field.name\"\n [value]=\"control.value\"\n (blur)=\"onFieldBlur(field)\"\n (change)=\"setFormControlValue($event, field)\"\n (input)=\"setFormControlValue($event, field)\"\n [required]=\"field.required\"\n [valueState]=\"getValueState(field.name)\"\n [disabled]=\"field.disabled\"\n >\n @if (getError(field.name); as error) {\n <div slot=\"valueStateMessage\">{{ error }}</div>\n }\n </ui5-input>\n }\n </div>\n }\n</section>\n", styles: [".form>div{display:flex;flex-direction:column;justify-content:space-evenly;align-items:flex-start;margin-bottom:.5rem;width:100%}.input{width:100%}\n"] }]
|
|
503
|
+
}], ctorParameters: () => [], propDecorators: { fields: [{ type: i0.Input, args: [{ isSignal: true, alias: "fields", required: true }] }], initialValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialValues", required: false }] }], fieldErrors: [{ type: i0.Input, args: [{ isSignal: true, alias: "fieldErrors", required: false }] }], fieldChange: [{ type: i0.Output, args: ["fieldChange"] }], formSubmit: [{ type: i0.Output, args: ["formSubmit"] }] } });
|
|
504
|
+
|
|
505
|
+
class DeclarativeTableCard {
|
|
506
|
+
resources = input.required(...(ngDevMode ? [{ debugName: "resources" }] : /* istanbul ignore next */ []));
|
|
507
|
+
config = input.required(...(ngDevMode ? [{ debugName: "config" }] : /* istanbul ignore next */ []));
|
|
508
|
+
createFormState = input({}, ...(ngDevMode ? [{ debugName: "createFormState" }] : /* istanbul ignore next */ []));
|
|
509
|
+
editFormState = input({}, ...(ngDevMode ? [{ debugName: "editFormState" }] : /* istanbul ignore next */ []));
|
|
510
|
+
actionButtonClick = output();
|
|
511
|
+
tableRowClicked = output();
|
|
512
|
+
loadMoreResources = output();
|
|
513
|
+
paginationLimitChanged = output();
|
|
514
|
+
searchChanged = output();
|
|
515
|
+
createFieldChange = output();
|
|
516
|
+
editFieldChange = output();
|
|
517
|
+
createSubmit = output();
|
|
518
|
+
editSubmit = output();
|
|
519
|
+
deleteSubmit = output();
|
|
520
|
+
searchState = signal('collapsed', ...(ngDevMode ? [{ debugName: "searchState" }] : /* istanbul ignore next */ []));
|
|
521
|
+
searchExpanded = computed(() => this.searchState() !== 'collapsed', ...(ngDevMode ? [{ debugName: "searchExpanded" }] : /* istanbul ignore next */ []));
|
|
522
|
+
searchCollapsing = computed(() => this.searchState() === 'collapsing', ...(ngDevMode ? [{ debugName: "searchCollapsing" }] : /* istanbul ignore next */ []));
|
|
523
|
+
searchControl = new FormControl('');
|
|
524
|
+
searchInputRef = viewChild('searchInput', ...(ngDevMode ? [{ debugName: "searchInputRef" }] : /* istanbul ignore next */ []));
|
|
525
|
+
createDialogOpen = signal(false, ...(ngDevMode ? [{ debugName: "createDialogOpen" }] : /* istanbul ignore next */ []));
|
|
526
|
+
editDialogOpen = signal(false, ...(ngDevMode ? [{ debugName: "editDialogOpen" }] : /* istanbul ignore next */ []));
|
|
527
|
+
deleteDialogOpen = signal(false, ...(ngDevMode ? [{ debugName: "deleteDialogOpen" }] : /* istanbul ignore next */ []));
|
|
528
|
+
pendingResource = signal(null, ...(ngDevMode ? [{ debugName: "pendingResource" }] : /* istanbul ignore next */ []));
|
|
529
|
+
tableConfig = computed(() => this.config().tableConfig, ...(ngDevMode ? [{ debugName: "tableConfig" }] : /* istanbul ignore next */ []));
|
|
530
|
+
header = computed(() => this.config().header, ...(ngDevMode ? [{ debugName: "header" }] : /* istanbul ignore next */ []));
|
|
531
|
+
headerTooltip = computed(() => this.config().headerTooltip, ...(ngDevMode ? [{ debugName: "headerTooltip" }] : /* istanbul ignore next */ []));
|
|
532
|
+
createFormConfig = computed(() => this.config().createResourceFormConfig, ...(ngDevMode ? [{ debugName: "createFormConfig" }] : /* istanbul ignore next */ []));
|
|
533
|
+
editFormConfig = computed(() => this.config().editResourceFormConfig, ...(ngDevMode ? [{ debugName: "editFormConfig" }] : /* istanbul ignore next */ []));
|
|
534
|
+
deleteConfirmationConfig = computed(() => this.config().deleteResourceConfirmationConfig, ...(ngDevMode ? [{ debugName: "deleteConfirmationConfig" }] : /* istanbul ignore next */ []));
|
|
535
|
+
createButtonConfig = computed(() => this.config().buttonSettings?.createButton, ...(ngDevMode ? [{ debugName: "createButtonConfig" }] : /* istanbul ignore next */ []));
|
|
536
|
+
searchButtonConfig = computed(() => this.config().buttonSettings?.searchButton, ...(ngDevMode ? [{ debugName: "searchButtonConfig" }] : /* istanbul ignore next */ []));
|
|
537
|
+
effectiveColumns = computed(() => this.addActionsColumn(), ...(ngDevMode ? [{ debugName: "effectiveColumns" }] : /* istanbul ignore next */ []));
|
|
538
|
+
editInitialValue = computed(() => {
|
|
539
|
+
const pendingResource = this.pendingResource();
|
|
540
|
+
const editConfig = this.editFormConfig();
|
|
541
|
+
if (!pendingResource || !editConfig) {
|
|
542
|
+
return {};
|
|
543
|
+
}
|
|
544
|
+
return this.buildInitialValues(editConfig.fields, pendingResource);
|
|
545
|
+
}, ...(ngDevMode ? [{ debugName: "editInitialValue" }] : /* istanbul ignore next */ []));
|
|
546
|
+
injector = inject(Injector);
|
|
547
|
+
constructor() {
|
|
548
|
+
this.searchControl.valueChanges
|
|
549
|
+
.pipe(debounceTime(300), takeUntilDestroyed())
|
|
550
|
+
.subscribe((value) => {
|
|
551
|
+
this.searchChanged.emit(value ?? '');
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
toggleSearch() {
|
|
555
|
+
if (this.searchState() === 'expanded') {
|
|
556
|
+
this.collapseSearch();
|
|
557
|
+
}
|
|
558
|
+
else if (this.searchState() === 'collapsed') {
|
|
559
|
+
this.searchState.set('expanded');
|
|
560
|
+
afterNextRender(() => {
|
|
561
|
+
this.searchInputRef()?.elementRef.nativeElement.focus();
|
|
562
|
+
}, { injector: this.injector });
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
onSearchBlur() {
|
|
566
|
+
if (!this.searchControl.value) {
|
|
567
|
+
this.collapseSearch();
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
onSearchAnimationEnd() {
|
|
571
|
+
if (this.searchCollapsing()) {
|
|
572
|
+
this.searchState.set('collapsed');
|
|
573
|
+
this.searchControl.setValue('', { emitEvent: false });
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
collapseSearch() {
|
|
577
|
+
this.searchState.set('collapsing');
|
|
578
|
+
}
|
|
579
|
+
onButtonClick(event) {
|
|
580
|
+
const action = event.field.uiSettings?.buttonSettings?.action;
|
|
581
|
+
if (action === 'edit' && event.resource) {
|
|
582
|
+
this.pendingResource.set(event.resource);
|
|
583
|
+
this.editDialogOpen.set(true);
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
if (action === 'delete' && event.resource) {
|
|
587
|
+
this.pendingResource.set(event.resource);
|
|
588
|
+
this.deleteDialogOpen.set(true);
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
this.actionButtonClick.emit(event);
|
|
592
|
+
}
|
|
593
|
+
closeCreateDialog() {
|
|
594
|
+
this.createDialogOpen.set(false);
|
|
595
|
+
}
|
|
596
|
+
closeEditDialog() {
|
|
597
|
+
this.editDialogOpen.set(false);
|
|
598
|
+
}
|
|
599
|
+
closeDeleteDialog() {
|
|
600
|
+
this.deleteDialogOpen.set(false);
|
|
601
|
+
}
|
|
602
|
+
onCreateFieldChange(event) {
|
|
603
|
+
this.createFieldChange.emit(event);
|
|
604
|
+
}
|
|
605
|
+
onEditFieldChange(event) {
|
|
606
|
+
const resource = this.pendingResource();
|
|
607
|
+
if (resource) {
|
|
608
|
+
this.editFieldChange.emit({ resource, formChangeEvent: event });
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
onCreateSubmit(value) {
|
|
612
|
+
this.createSubmit.emit(value);
|
|
613
|
+
}
|
|
614
|
+
onEditSubmit(value) {
|
|
615
|
+
const resource = this.pendingResource();
|
|
616
|
+
if (resource) {
|
|
617
|
+
this.editSubmit.emit({ resource, value });
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
onDeleteSubmit() {
|
|
621
|
+
const resource = this.pendingResource();
|
|
622
|
+
if (resource)
|
|
623
|
+
this.deleteSubmit.emit(resource);
|
|
624
|
+
}
|
|
625
|
+
hasErrors(state) {
|
|
626
|
+
const errors = state.fieldErrors;
|
|
627
|
+
return !!errors && Object.values(errors).some((val) => !!val);
|
|
628
|
+
}
|
|
629
|
+
addActionsColumn() {
|
|
630
|
+
const cols = this.tableConfig().fields;
|
|
631
|
+
const buttonSettings = this.config().buttonSettings;
|
|
632
|
+
const editButton = this.editFormConfig();
|
|
633
|
+
const deleteButton = this.deleteConfirmationConfig();
|
|
634
|
+
const actions = [];
|
|
635
|
+
if (editButton) {
|
|
636
|
+
actions.push({
|
|
637
|
+
uiSettings: {
|
|
638
|
+
displayAs: 'button',
|
|
639
|
+
buttonSettings: {
|
|
640
|
+
icon: 'edit',
|
|
641
|
+
design: 'Transparent',
|
|
642
|
+
action: 'edit',
|
|
643
|
+
...buttonSettings?.editButton,
|
|
644
|
+
},
|
|
645
|
+
},
|
|
646
|
+
group: { name: 'actions', label: '', multiline: false },
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
if (deleteButton) {
|
|
650
|
+
actions.push({
|
|
651
|
+
uiSettings: {
|
|
652
|
+
displayAs: 'button',
|
|
653
|
+
buttonSettings: {
|
|
654
|
+
icon: 'decline',
|
|
655
|
+
design: 'Transparent',
|
|
656
|
+
action: 'delete',
|
|
657
|
+
...buttonSettings?.deleteButton,
|
|
658
|
+
},
|
|
659
|
+
},
|
|
660
|
+
group: { name: 'actions', label: '', multiline: false },
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
return cols.concat(actions);
|
|
664
|
+
}
|
|
665
|
+
buildInitialValues(fields, resource) {
|
|
666
|
+
if (!resource)
|
|
667
|
+
return {};
|
|
668
|
+
return fields.reduce((acc, field) => {
|
|
669
|
+
if (typeof field.name === 'string') {
|
|
670
|
+
acc[field.name] =
|
|
671
|
+
getResourceValueByJsonPath(resource, { property: field.name }) ??
|
|
672
|
+
'';
|
|
673
|
+
}
|
|
674
|
+
return acc;
|
|
675
|
+
}, {});
|
|
676
|
+
}
|
|
677
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: DeclarativeTableCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
678
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: DeclarativeTableCard, isStandalone: true, selector: "mfp-declarative-table-card", inputs: { resources: { classPropertyName: "resources", publicName: "resources", isSignal: true, isRequired: true, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null }, createFormState: { classPropertyName: "createFormState", publicName: "createFormState", isSignal: true, isRequired: false, transformFunction: null }, editFormState: { classPropertyName: "editFormState", publicName: "editFormState", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { actionButtonClick: "actionButtonClick", tableRowClicked: "tableRowClicked", loadMoreResources: "loadMoreResources", paginationLimitChanged: "paginationLimitChanged", searchChanged: "searchChanged", createFieldChange: "createFieldChange", editFieldChange: "editFieldChange", createSubmit: "createSubmit", editSubmit: "editSubmit", deleteSubmit: "deleteSubmit" }, viewQueries: [{ propertyName: "searchInputRef", first: true, predicate: ["searchInput"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"card\">\n <div class=\"card__header\">\n <div class=\"card__title\">\n @if (header(); as header) {\n {{ header }}\n @if (headerTooltip(); as tooltip) {\n <ui5-icon\n class=\"card__info-icon\"\n name=\"hint\"\n [showTooltip]=\"true\"\n [accessibleName]=\"tooltip\"\n />\n }\n }\n </div>\n <div class=\"card__actions\">\n @if (config().resourcesSearchable) {\n @if (searchExpanded()) {\n <ui5-input\n #searchInput\n [class]=\"\n 'card__search-input card__search-input--' +\n (searchCollapsing() ? 'leave' : 'enter')\n \"\n [formControl]=\"searchControl\"\n (blur)=\"onSearchBlur()\"\n (animationend)=\"onSearchAnimationEnd()\"\n />\n }\n <ui5-button\n class=\"card__search-btn\"\n design=\"Transparent\"\n [icon]=\"searchButtonConfig()?.icon ?? 'search'\"\n [tooltip]=\"searchButtonConfig()?.tooltip ?? 'Search'\"\n (click)=\"toggleSearch()\"\n />\n }\n @if (createFormConfig()) {\n <ui5-button\n class=\"card__create-btn\"\n [icon]=\"createButtonConfig()?.icon ?? 'add'\"\n [design]=\"createButtonConfig()?.design ?? 'Transparent'\"\n [tooltip]=\"createButtonConfig()?.tooltip\"\n (click)=\"createDialogOpen.set(true)\"\n >\n {{ createButtonConfig()?.text ?? '' }}\n </ui5-button>\n }\n </div>\n </div>\n\n <div class=\"card__body\">\n @if (tableConfig(); as config) {\n <mfp-declarative-table\n [columns]=\"effectiveColumns()\"\n [resources]=\"resources()\"\n [totalItemsCount]=\"config.totalItemsCount\"\n [paginationLimit]=\"config.paginationLimit ?? 5\"\n [hasMore]=\"config.hasMore ?? false\"\n (buttonClick)=\"onButtonClick($event)\"\n (tableRowClicked)=\"tableRowClicked.emit($event)\"\n (loadMoreResources)=\"loadMoreResources.emit()\"\n (paginationLimitChanged)=\"paginationLimitChanged.emit($event)\"\n />\n }\n </div>\n</div>\n\n@if (createFormConfig(); as config) {\n <ui5-dialog\n [open]=\"createDialogOpen()\"\n (ui5BeforeClose)=\"createDialogOpen.set(false)\"\n >\n <div slot=\"header\" class=\"dialog__header\">\n <ui5-title level=\"H5\">{{ config.title ?? 'Create' }}</ui5-title>\n </div>\n <div class=\"dialog__body\">\n <mfp-declarative-form\n #createForm\n [fields]=\"config.fields\"\n [fieldErrors]=\"createFormState().fieldErrors ?? {}\"\n (fieldChange)=\"onCreateFieldChange($event)\"\n (formSubmit)=\"onCreateSubmit($event)\"\n />\n </div>\n <div slot=\"footer\" class=\"dialog__footer\">\n <ui5-button\n design=\"Emphasized\"\n [disabled]=\"hasErrors(createFormState())\"\n (click)=\"createForm.submit()\"\n >\n {{ config.confirmLabel ?? 'Save' }}\n </ui5-button>\n <ui5-button design=\"Transparent\" (click)=\"closeCreateDialog()\">\n {{ config.cancelLabel ?? 'Cancel' }}\n </ui5-button>\n </div>\n </ui5-dialog>\n}\n\n@if (editFormConfig(); as config) {\n <ui5-dialog\n [open]=\"editDialogOpen()\"\n (ui5BeforeClose)=\"editDialogOpen.set(false)\"\n >\n <div slot=\"header\" class=\"dialog__header\">\n <ui5-title level=\"H5\">{{ config.title ?? 'Edit' }}</ui5-title>\n </div>\n <div class=\"dialog__body\">\n <mfp-declarative-form\n #editForm\n [fields]=\"config.fields\"\n [initialValues]=\"editInitialValue()\"\n [fieldErrors]=\"editFormState().fieldErrors ?? {}\"\n (fieldChange)=\"onEditFieldChange($event)\"\n (formSubmit)=\"onEditSubmit($event)\"\n />\n </div>\n <div slot=\"footer\" class=\"dialog__footer\">\n <ui5-button\n design=\"Emphasized\"\n [disabled]=\"hasErrors(editFormState())\"\n (click)=\"editForm.submit()\"\n >\n {{ config.confirmLabel ?? 'Edit' }}\n </ui5-button>\n <ui5-button design=\"Transparent\" (click)=\"closeEditDialog()\">\n {{ config.cancelLabel ?? 'Cancel' }}\n </ui5-button>\n </div>\n </ui5-dialog>\n}\n\n@if (deleteConfirmationConfig(); as config) {\n <ui5-dialog\n [open]=\"deleteDialogOpen()\"\n (ui5BeforeClose)=\"deleteDialogOpen.set(false)\"\n >\n <div slot=\"header\" class=\"dialog__header\">\n <ui5-title level=\"H5\">{{ config.title ?? 'Confirm Delete' }}</ui5-title>\n </div>\n @if (config.message) {\n <div class=\"dialog__body\">\n <p class=\"dialog__message\">{{ config.message }}</p>\n </div>\n }\n <div slot=\"footer\" class=\"dialog__footer\">\n <ui5-button design=\"Negative\" (click)=\"onDeleteSubmit()\">\n {{ config.confirmLabel ?? 'Delete' }}\n </ui5-button>\n <ui5-button design=\"Transparent\" (click)=\"closeDeleteDialog()\">\n {{ config.cancelLabel ?? 'Cancel' }}\n </ui5-button>\n </div>\n </ui5-dialog>\n}\n", styles: [":host{display:block}@keyframes slide-in{0%{opacity:0;transform:scaleX(0)}to{opacity:1;transform:scaleX(1)}}@keyframes slide-out{0%{opacity:1;transform:scaleX(1)}to{opacity:0;transform:scaleX(0)}}.card{display:flex;flex-direction:column;border:1px solid var(--sapGroup_ContentBorderColor, #d9d9d9);border-radius:16px;background:var(--sapGroup_TitleBackground, #fff)}.card__header{display:flex;align-items:center;justify-content:space-between;min-height:3rem;padding:0 1rem;border-bottom:1px solid var(--sapGroup_ContentBorderColor, #d9d9d9)}.card__title{color:var(--sapTile_TitleTextColor);text-overflow:ellipsis;font-family:var(--Font-Family-sapFontFamily, 72);font-size:var(--Font-Size-sapFontHeader6Size, 16px);font-style:normal;font-weight:700;line-height:normal;display:flex;align-items:center}.card__actions{display:flex;align-items:center;gap:.5rem}.card__info-icon{color:var(--sapButton_IconColor, #0070f2);margin-left:.5rem}.card__search-input{transform-origin:right center}.card__search-input--enter{animation:slide-in .2s ease-out both}.card__search-input--leave{animation:slide-out .2s ease-in both}.card__create-btn,.card__search-btn{min-width:auto;color:var(--sapButton_IconColor, #0070f2)}.card__body{flex:1;overflow:auto}.dialog__header{display:flex;align-items:flex-start;padding:.75rem 1rem}.dialog__body{margin:-1rem;min-width:320px;padding:1rem}.dialog__message{padding:1rem;margin:0;font-size:.875rem;max-width:320px}.dialog__footer{display:flex;justify-content:space-between;align-items:center;gap:.5rem;padding:.5rem 1rem;margin:0 -1rem;width:100%}\n"], dependencies: [{ kind: "component", type: DeclarativeTable, selector: "mfp-declarative-table", inputs: ["columns", "resources", "trackByPath", "totalItemsCount", "paginationLimit", "hasMore"], outputs: ["buttonClick", "tableRowClicked", "loadMoreResources", "paginationLimitChanged"] }, { kind: "component", type: DeclarativeForm, selector: "mfp-declarative-form", inputs: ["fields", "initialValues", "fieldErrors"], outputs: ["fieldChange", "formSubmit"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: Dialog, selector: "ui5-dialog, [ui5-dialog]", inputs: ["headerText", "stretch", "draggable", "resizable", "state", "initialFocus", "preventFocusRestore", "accessibleName", "accessibleNameRef", "accessibleRole", "accessibleDescription", "accessibleDescriptionRef", "preventInitialFocus", "open"], outputs: ["ui5BeforeOpen", "ui5Open", "ui5BeforeClose", "ui5Close"], exportAs: ["ui5Dialog"] }, { kind: "component", type: Title, selector: "ui5-title, [ui5-title]", inputs: ["wrappingType", "level", "size"], exportAs: ["ui5Title"] }, { kind: "component", type: Button, selector: "ui5-button, [ui5-button]", inputs: ["design", "disabled", "icon", "endIcon", "submits", "tooltip", "accessibleName", "accessibleNameRef", "accessibilityAttributes", "accessibleDescription", "type", "accessibleRole", "loading", "loadingDelay"], outputs: ["ui5Click"], exportAs: ["ui5Button"] }, { kind: "component", type: Icon, selector: "ui5-icon, [ui5-icon]", inputs: ["design", "name", "accessibleName", "showTooltip", "mode"], outputs: ["ui5Click"], exportAs: ["ui5Icon"] }, { kind: "component", type: Input, selector: "ui5-input, [ui5-input]", inputs: ["disabled", "placeholder", "readonly", "required", "noTypeahead", "type", "value", "valueState", "name", "showSuggestions", "maxlength", "accessibleName", "accessibleNameRef", "accessibleDescription", "accessibleDescriptionRef", "showClearIcon", "open", "filter"], outputs: ["ui5Change", "ui5Input", "ui5Select", "ui5SelectionChange", "ui5Open", "ui5Close"], exportAs: ["ui5Input"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.ShadowDom });
|
|
679
|
+
}
|
|
680
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: DeclarativeTableCard, decorators: [{
|
|
681
|
+
type: Component,
|
|
682
|
+
args: [{ selector: 'mfp-declarative-table-card', imports: [
|
|
683
|
+
DeclarativeTable,
|
|
684
|
+
DeclarativeForm,
|
|
685
|
+
ReactiveFormsModule,
|
|
686
|
+
Dialog,
|
|
687
|
+
Title,
|
|
688
|
+
Button,
|
|
689
|
+
Icon,
|
|
690
|
+
Input,
|
|
691
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.ShadowDom, schemas: [CUSTOM_ELEMENTS_SCHEMA], template: "<div class=\"card\">\n <div class=\"card__header\">\n <div class=\"card__title\">\n @if (header(); as header) {\n {{ header }}\n @if (headerTooltip(); as tooltip) {\n <ui5-icon\n class=\"card__info-icon\"\n name=\"hint\"\n [showTooltip]=\"true\"\n [accessibleName]=\"tooltip\"\n />\n }\n }\n </div>\n <div class=\"card__actions\">\n @if (config().resourcesSearchable) {\n @if (searchExpanded()) {\n <ui5-input\n #searchInput\n [class]=\"\n 'card__search-input card__search-input--' +\n (searchCollapsing() ? 'leave' : 'enter')\n \"\n [formControl]=\"searchControl\"\n (blur)=\"onSearchBlur()\"\n (animationend)=\"onSearchAnimationEnd()\"\n />\n }\n <ui5-button\n class=\"card__search-btn\"\n design=\"Transparent\"\n [icon]=\"searchButtonConfig()?.icon ?? 'search'\"\n [tooltip]=\"searchButtonConfig()?.tooltip ?? 'Search'\"\n (click)=\"toggleSearch()\"\n />\n }\n @if (createFormConfig()) {\n <ui5-button\n class=\"card__create-btn\"\n [icon]=\"createButtonConfig()?.icon ?? 'add'\"\n [design]=\"createButtonConfig()?.design ?? 'Transparent'\"\n [tooltip]=\"createButtonConfig()?.tooltip\"\n (click)=\"createDialogOpen.set(true)\"\n >\n {{ createButtonConfig()?.text ?? '' }}\n </ui5-button>\n }\n </div>\n </div>\n\n <div class=\"card__body\">\n @if (tableConfig(); as config) {\n <mfp-declarative-table\n [columns]=\"effectiveColumns()\"\n [resources]=\"resources()\"\n [totalItemsCount]=\"config.totalItemsCount\"\n [paginationLimit]=\"config.paginationLimit ?? 5\"\n [hasMore]=\"config.hasMore ?? false\"\n (buttonClick)=\"onButtonClick($event)\"\n (tableRowClicked)=\"tableRowClicked.emit($event)\"\n (loadMoreResources)=\"loadMoreResources.emit()\"\n (paginationLimitChanged)=\"paginationLimitChanged.emit($event)\"\n />\n }\n </div>\n</div>\n\n@if (createFormConfig(); as config) {\n <ui5-dialog\n [open]=\"createDialogOpen()\"\n (ui5BeforeClose)=\"createDialogOpen.set(false)\"\n >\n <div slot=\"header\" class=\"dialog__header\">\n <ui5-title level=\"H5\">{{ config.title ?? 'Create' }}</ui5-title>\n </div>\n <div class=\"dialog__body\">\n <mfp-declarative-form\n #createForm\n [fields]=\"config.fields\"\n [fieldErrors]=\"createFormState().fieldErrors ?? {}\"\n (fieldChange)=\"onCreateFieldChange($event)\"\n (formSubmit)=\"onCreateSubmit($event)\"\n />\n </div>\n <div slot=\"footer\" class=\"dialog__footer\">\n <ui5-button\n design=\"Emphasized\"\n [disabled]=\"hasErrors(createFormState())\"\n (click)=\"createForm.submit()\"\n >\n {{ config.confirmLabel ?? 'Save' }}\n </ui5-button>\n <ui5-button design=\"Transparent\" (click)=\"closeCreateDialog()\">\n {{ config.cancelLabel ?? 'Cancel' }}\n </ui5-button>\n </div>\n </ui5-dialog>\n}\n\n@if (editFormConfig(); as config) {\n <ui5-dialog\n [open]=\"editDialogOpen()\"\n (ui5BeforeClose)=\"editDialogOpen.set(false)\"\n >\n <div slot=\"header\" class=\"dialog__header\">\n <ui5-title level=\"H5\">{{ config.title ?? 'Edit' }}</ui5-title>\n </div>\n <div class=\"dialog__body\">\n <mfp-declarative-form\n #editForm\n [fields]=\"config.fields\"\n [initialValues]=\"editInitialValue()\"\n [fieldErrors]=\"editFormState().fieldErrors ?? {}\"\n (fieldChange)=\"onEditFieldChange($event)\"\n (formSubmit)=\"onEditSubmit($event)\"\n />\n </div>\n <div slot=\"footer\" class=\"dialog__footer\">\n <ui5-button\n design=\"Emphasized\"\n [disabled]=\"hasErrors(editFormState())\"\n (click)=\"editForm.submit()\"\n >\n {{ config.confirmLabel ?? 'Edit' }}\n </ui5-button>\n <ui5-button design=\"Transparent\" (click)=\"closeEditDialog()\">\n {{ config.cancelLabel ?? 'Cancel' }}\n </ui5-button>\n </div>\n </ui5-dialog>\n}\n\n@if (deleteConfirmationConfig(); as config) {\n <ui5-dialog\n [open]=\"deleteDialogOpen()\"\n (ui5BeforeClose)=\"deleteDialogOpen.set(false)\"\n >\n <div slot=\"header\" class=\"dialog__header\">\n <ui5-title level=\"H5\">{{ config.title ?? 'Confirm Delete' }}</ui5-title>\n </div>\n @if (config.message) {\n <div class=\"dialog__body\">\n <p class=\"dialog__message\">{{ config.message }}</p>\n </div>\n }\n <div slot=\"footer\" class=\"dialog__footer\">\n <ui5-button design=\"Negative\" (click)=\"onDeleteSubmit()\">\n {{ config.confirmLabel ?? 'Delete' }}\n </ui5-button>\n <ui5-button design=\"Transparent\" (click)=\"closeDeleteDialog()\">\n {{ config.cancelLabel ?? 'Cancel' }}\n </ui5-button>\n </div>\n </ui5-dialog>\n}\n", styles: [":host{display:block}@keyframes slide-in{0%{opacity:0;transform:scaleX(0)}to{opacity:1;transform:scaleX(1)}}@keyframes slide-out{0%{opacity:1;transform:scaleX(1)}to{opacity:0;transform:scaleX(0)}}.card{display:flex;flex-direction:column;border:1px solid var(--sapGroup_ContentBorderColor, #d9d9d9);border-radius:16px;background:var(--sapGroup_TitleBackground, #fff)}.card__header{display:flex;align-items:center;justify-content:space-between;min-height:3rem;padding:0 1rem;border-bottom:1px solid var(--sapGroup_ContentBorderColor, #d9d9d9)}.card__title{color:var(--sapTile_TitleTextColor);text-overflow:ellipsis;font-family:var(--Font-Family-sapFontFamily, 72);font-size:var(--Font-Size-sapFontHeader6Size, 16px);font-style:normal;font-weight:700;line-height:normal;display:flex;align-items:center}.card__actions{display:flex;align-items:center;gap:.5rem}.card__info-icon{color:var(--sapButton_IconColor, #0070f2);margin-left:.5rem}.card__search-input{transform-origin:right center}.card__search-input--enter{animation:slide-in .2s ease-out both}.card__search-input--leave{animation:slide-out .2s ease-in both}.card__create-btn,.card__search-btn{min-width:auto;color:var(--sapButton_IconColor, #0070f2)}.card__body{flex:1;overflow:auto}.dialog__header{display:flex;align-items:flex-start;padding:.75rem 1rem}.dialog__body{margin:-1rem;min-width:320px;padding:1rem}.dialog__message{padding:1rem;margin:0;font-size:.875rem;max-width:320px}.dialog__footer{display:flex;justify-content:space-between;align-items:center;gap:.5rem;padding:.5rem 1rem;margin:0 -1rem;width:100%}\n"] }]
|
|
692
|
+
}], ctorParameters: () => [], propDecorators: { resources: [{ type: i0.Input, args: [{ isSignal: true, alias: "resources", required: true }] }], config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: true }] }], createFormState: [{ type: i0.Input, args: [{ isSignal: true, alias: "createFormState", required: false }] }], editFormState: [{ type: i0.Input, args: [{ isSignal: true, alias: "editFormState", required: false }] }], actionButtonClick: [{ type: i0.Output, args: ["actionButtonClick"] }], tableRowClicked: [{ type: i0.Output, args: ["tableRowClicked"] }], loadMoreResources: [{ type: i0.Output, args: ["loadMoreResources"] }], paginationLimitChanged: [{ type: i0.Output, args: ["paginationLimitChanged"] }], searchChanged: [{ type: i0.Output, args: ["searchChanged"] }], createFieldChange: [{ type: i0.Output, args: ["createFieldChange"] }], editFieldChange: [{ type: i0.Output, args: ["editFieldChange"] }], createSubmit: [{ type: i0.Output, args: ["createSubmit"] }], editSubmit: [{ type: i0.Output, args: ["editSubmit"] }], deleteSubmit: [{ type: i0.Output, args: ["deleteSubmit"] }], searchInputRef: [{ type: i0.ViewChild, args: ['searchInput', { isSignal: true }] }] } });
|
|
693
|
+
|
|
694
|
+
class AddCardDialog {
|
|
695
|
+
availableCards = input([], ...(ngDevMode ? [{ debugName: "availableCards" }] : /* istanbul ignore next */ []));
|
|
696
|
+
addedCardsIds = input(new Set(), ...(ngDevMode ? [{ debugName: "addedCardsIds" }] : /* istanbul ignore next */ []));
|
|
697
|
+
open = input(false, ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
|
|
698
|
+
confirm = output();
|
|
699
|
+
cancel = output();
|
|
700
|
+
selectedIds = signal(new Set(), ...(ngDevMode ? [{ debugName: "selectedIds" }] : /* istanbul ignore next */ []));
|
|
701
|
+
constructor() {
|
|
702
|
+
effect(() => {
|
|
703
|
+
if (this.open()) {
|
|
704
|
+
this.selectedIds.set(new Set());
|
|
705
|
+
}
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
toggle(id) {
|
|
709
|
+
this.selectedIds.update((set) => {
|
|
710
|
+
const next = new Set(set);
|
|
711
|
+
if (next.has(id)) {
|
|
712
|
+
next.delete(id);
|
|
713
|
+
}
|
|
714
|
+
else {
|
|
715
|
+
next.add(id);
|
|
716
|
+
}
|
|
717
|
+
return next;
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
confirmAdd() {
|
|
721
|
+
const toAdd = this.availableCards().filter((ac) => this.selectedIds().has(ac.id) && !this.addedCardsIds().has(ac.id));
|
|
722
|
+
this.confirm.emit(toAdd);
|
|
723
|
+
}
|
|
724
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: AddCardDialog, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
725
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: AddCardDialog, isStandalone: true, selector: "mfp-add-card-dialog", inputs: { availableCards: { classPropertyName: "availableCards", publicName: "availableCards", isSignal: true, isRequired: false, transformFunction: null }, addedCardsIds: { classPropertyName: "addedCardsIds", publicName: "addedCardsIds", isSignal: true, isRequired: false, transformFunction: null }, open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { confirm: "confirm", cancel: "cancel" }, ngImport: i0, template: "<ui5-dialog [open]=\"open()\" (ui5BeforeClose)=\"cancel.emit()\">\n <div slot=\"header\" class=\"add-card-dialog__header\">\n <ui5-title level=\"H5\">Add Card</ui5-title>\n </div>\n <div class=\"add-card-dialog\">\n @if (availableCards().length === 0) {\n <p class=\"add-card-dialog__empty\">No cards available.</p>\n }\n @for (ac of availableCards(); track ac.id) {\n @let alreadyAdded = addedCardsIds().has(ac.id);\n <div\n class=\"add-card-dialog__item\"\n [class.add-card-dialog__item--disabled]=\"alreadyAdded\"\n >\n <ui5-checkbox\n [checked]=\"alreadyAdded || selectedIds().has(ac.id)\"\n [disabled]=\"alreadyAdded\"\n (ui5Change)=\"toggle(ac.id)\"\n [text]=\"ac.label || ac.component\"\n />\n </div>\n }\n </div>\n <div slot=\"footer\" class=\"add-card-dialog__footer\">\n <ui5-button design=\"Emphasized\" (click)=\"confirmAdd()\">Add</ui5-button>\n <ui5-button design=\"Transparent\" (click)=\"cancel.emit()\">Cancel</ui5-button>\n </div>\n</ui5-dialog>\n", styles: [":host{display:contents}.add-card-dialog{margin:-1rem;min-width:300px}.add-card-dialog__header{display:flex;align-items:flex-start;padding:.75rem 1rem}.add-card-dialog__item{display:flex;align-items:center;padding:.5rem 1rem;border-bottom:1px solid var(--sapList_BorderColor, #e5e5e5)}.add-card-dialog__item:last-child{border-bottom:none}.add-card-dialog__item--disabled{opacity:.8}.add-card-dialog__empty{padding:1rem;margin:0;color:var(--sapContent_LabelColor, #6a6d70);font-size:.875rem}.add-card-dialog__footer{display:flex;justify-content:flex-end;gap:.5rem;padding:.5rem 1rem;margin:0 -1rem}\n"], dependencies: [{ kind: "component", type: Button, selector: "ui5-button, [ui5-button]", inputs: ["design", "disabled", "icon", "endIcon", "submits", "tooltip", "accessibleName", "accessibleNameRef", "accessibilityAttributes", "accessibleDescription", "type", "accessibleRole", "loading", "loadingDelay"], outputs: ["ui5Click"], exportAs: ["ui5Button"] }, { kind: "component", type: CheckBox, selector: "ui5-checkbox, [ui5-checkbox]", inputs: ["accessibleNameRef", "accessibleName", "disabled", "readonly", "displayOnly", "required", "indeterminate", "checked", "text", "valueState", "wrappingType", "name", "value"], outputs: ["ui5Change"], exportAs: ["ui5CheckBox"] }, { kind: "component", type: Dialog, selector: "ui5-dialog, [ui5-dialog]", inputs: ["headerText", "stretch", "draggable", "resizable", "state", "initialFocus", "preventFocusRestore", "accessibleName", "accessibleNameRef", "accessibleRole", "accessibleDescription", "accessibleDescriptionRef", "preventInitialFocus", "open"], outputs: ["ui5BeforeOpen", "ui5Open", "ui5BeforeClose", "ui5Close"], exportAs: ["ui5Dialog"] }, { kind: "component", type: Title, selector: "ui5-title, [ui5-title]", inputs: ["wrappingType", "level", "size"], exportAs: ["ui5Title"] }], encapsulation: i0.ViewEncapsulation.ShadowDom });
|
|
726
|
+
}
|
|
727
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: AddCardDialog, decorators: [{
|
|
728
|
+
type: Component,
|
|
729
|
+
args: [{ selector: 'mfp-add-card-dialog', encapsulation: ViewEncapsulation.ShadowDom, imports: [Button, CheckBox, Dialog, Title], template: "<ui5-dialog [open]=\"open()\" (ui5BeforeClose)=\"cancel.emit()\">\n <div slot=\"header\" class=\"add-card-dialog__header\">\n <ui5-title level=\"H5\">Add Card</ui5-title>\n </div>\n <div class=\"add-card-dialog\">\n @if (availableCards().length === 0) {\n <p class=\"add-card-dialog__empty\">No cards available.</p>\n }\n @for (ac of availableCards(); track ac.id) {\n @let alreadyAdded = addedCardsIds().has(ac.id);\n <div\n class=\"add-card-dialog__item\"\n [class.add-card-dialog__item--disabled]=\"alreadyAdded\"\n >\n <ui5-checkbox\n [checked]=\"alreadyAdded || selectedIds().has(ac.id)\"\n [disabled]=\"alreadyAdded\"\n (ui5Change)=\"toggle(ac.id)\"\n [text]=\"ac.label || ac.component\"\n />\n </div>\n }\n </div>\n <div slot=\"footer\" class=\"add-card-dialog__footer\">\n <ui5-button design=\"Emphasized\" (click)=\"confirmAdd()\">Add</ui5-button>\n <ui5-button design=\"Transparent\" (click)=\"cancel.emit()\">Cancel</ui5-button>\n </div>\n</ui5-dialog>\n", styles: [":host{display:contents}.add-card-dialog{margin:-1rem;min-width:300px}.add-card-dialog__header{display:flex;align-items:flex-start;padding:.75rem 1rem}.add-card-dialog__item{display:flex;align-items:center;padding:.5rem 1rem;border-bottom:1px solid var(--sapList_BorderColor, #e5e5e5)}.add-card-dialog__item:last-child{border-bottom:none}.add-card-dialog__item--disabled{opacity:.8}.add-card-dialog__empty{padding:1rem;margin:0;color:var(--sapContent_LabelColor, #6a6d70);font-size:.875rem}.add-card-dialog__footer{display:flex;justify-content:flex-end;gap:.5rem;padding:.5rem 1rem;margin:0 -1rem}\n"] }]
|
|
730
|
+
}], ctorParameters: () => [], propDecorators: { availableCards: [{ type: i0.Input, args: [{ isSignal: true, alias: "availableCards", required: false }] }], addedCardsIds: [{ type: i0.Input, args: [{ isSignal: true, alias: "addedCardsIds", required: false }] }], open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }], confirm: [{ type: i0.Output, args: ["confirm"] }], cancel: [{ type: i0.Output, args: ["cancel"] }] } });
|
|
731
|
+
|
|
732
|
+
const ELEMENT_SELECTOR_PATTERN = /^[a-z](?:[a-z0-9-]*)$/;
|
|
733
|
+
const dashboardCardRegistry = new Map();
|
|
734
|
+
function addComponentToRegistry(componentTypes) {
|
|
735
|
+
for (const componentType of componentTypes) {
|
|
736
|
+
const mirror = reflectComponentType(componentType);
|
|
737
|
+
if (!mirror) {
|
|
738
|
+
throw new Error(`Dashboard card registration failed: "${getTypeName(componentType)}" is not an Angular component.`);
|
|
739
|
+
}
|
|
740
|
+
if (!isStandalone(componentType)) {
|
|
741
|
+
throw new Error(`Dashboard card registration failed: "${mirror.selector}" must be a standalone Angular component.`);
|
|
742
|
+
}
|
|
743
|
+
const selector = toElementSelector(mirror.selector, componentType);
|
|
744
|
+
const existing = dashboardCardRegistry.get(selector);
|
|
745
|
+
if (existing && existing.componentType !== componentType) {
|
|
746
|
+
throw new Error(`Dashboard card registration failed: selector "${selector}" is already registered.`);
|
|
747
|
+
}
|
|
748
|
+
dashboardCardRegistry.set(selector, {
|
|
749
|
+
componentType,
|
|
750
|
+
selector,
|
|
751
|
+
inputs: createInputMap(mirror.inputs),
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
function getRegisteredDashboardCardComponent(selector) {
|
|
756
|
+
return dashboardCardRegistry.get(selector);
|
|
757
|
+
}
|
|
758
|
+
function resetDashboardCardRegistry() {
|
|
759
|
+
dashboardCardRegistry.clear();
|
|
760
|
+
}
|
|
761
|
+
function warnForUnknownDashboardCardInput(selector, inputName) {
|
|
762
|
+
if (!isDevMode())
|
|
763
|
+
return;
|
|
764
|
+
console.warn(`Dashboard card "${selector}" ignores unknown Angular input "${inputName}".`);
|
|
765
|
+
}
|
|
766
|
+
function createInputMap(inputs) {
|
|
767
|
+
const bindings = new Map();
|
|
768
|
+
for (const input of inputs) {
|
|
769
|
+
bindings.set(input.templateName, input.templateName);
|
|
770
|
+
bindings.set(input.propName, input.templateName);
|
|
771
|
+
}
|
|
772
|
+
return bindings;
|
|
773
|
+
}
|
|
774
|
+
function toElementSelector(selector, componentType) {
|
|
775
|
+
const normalized = selector.trim();
|
|
776
|
+
if (!ELEMENT_SELECTOR_PATTERN.test(normalized)) {
|
|
777
|
+
throw new Error(`Dashboard card registration failed: "${getTypeName(componentType)}" must use a single element selector. Received "${selector}".`);
|
|
778
|
+
}
|
|
779
|
+
return normalized;
|
|
780
|
+
}
|
|
781
|
+
function getTypeName(componentType) {
|
|
782
|
+
return componentType.name.replace(/^_+/, '') || 'AnonymousComponent';
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
const CARD_TYPES = {
|
|
786
|
+
WC: 'wc',
|
|
787
|
+
ANGULAR: 'angular',
|
|
788
|
+
SAP_UI: 'sap-ui',
|
|
789
|
+
};
|
|
790
|
+
|
|
791
|
+
function mountAngularCard(cfg, angularHost, onCleanup) {
|
|
792
|
+
const registeredComponent = getRegisteredDashboardCardComponent(cfg.component);
|
|
793
|
+
if (!registeredComponent) {
|
|
794
|
+
console.warn(`[DashboardCard] Angular component "${cfg.component}" is not registered`);
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
const componentRef = angularHost.createComponent(registeredComponent.componentType);
|
|
798
|
+
for (const [inputName, value] of Object.entries(cfg.componentInputs ?? {})) {
|
|
799
|
+
const templateName = registeredComponent.inputs.get(inputName);
|
|
800
|
+
if (!templateName) {
|
|
801
|
+
warnForUnknownDashboardCardInput(cfg.component, inputName);
|
|
802
|
+
continue;
|
|
803
|
+
}
|
|
804
|
+
componentRef.setInput(templateName, value);
|
|
805
|
+
}
|
|
806
|
+
componentRef.changeDetectorRef.detectChanges();
|
|
807
|
+
onCleanup(() => {
|
|
808
|
+
angularHost.clear();
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
function mountSapCard(cfg, container, onCleanup) {
|
|
813
|
+
const host = container.element.nativeElement;
|
|
814
|
+
let sapContainer = null;
|
|
815
|
+
let isDestroyed = false;
|
|
816
|
+
const sapRequire = window.sap?.ui?.require;
|
|
817
|
+
if (sapRequire) {
|
|
818
|
+
sapRequire(['sap/ui/core/ComponentContainer'], (ComponentContainer) => {
|
|
819
|
+
if (isDestroyed)
|
|
820
|
+
return;
|
|
821
|
+
const container = new ComponentContainer({
|
|
822
|
+
name: cfg.component,
|
|
823
|
+
manifest: true,
|
|
824
|
+
async: true,
|
|
825
|
+
settings: cfg.componentInputs ?? {},
|
|
826
|
+
});
|
|
827
|
+
container.placeAt(host);
|
|
828
|
+
sapContainer = container;
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
else {
|
|
832
|
+
console.error('[DashboardCard] SAP UI5 is not available on window.sap');
|
|
833
|
+
}
|
|
834
|
+
onCleanup(() => {
|
|
835
|
+
isDestroyed = true;
|
|
836
|
+
sapContainer?.destroy();
|
|
837
|
+
host.innerHTML = '';
|
|
838
|
+
});
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
function mountWcCard(cfg, container, onCleanup, renderer) {
|
|
842
|
+
const host = container.element.nativeElement;
|
|
843
|
+
const element = renderer.createElement(cfg.component);
|
|
844
|
+
for (const [key, value] of Object.entries(cfg.componentInputs ?? {})) {
|
|
845
|
+
renderer.setProperty(element, key, value);
|
|
846
|
+
}
|
|
847
|
+
renderer.appendChild(host, element);
|
|
848
|
+
onCleanup(() => {
|
|
849
|
+
host.innerHTML = '';
|
|
850
|
+
});
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
class DashboardCard {
|
|
854
|
+
card = input.required(...(ngDevMode ? [{ debugName: "card" }] : /* istanbul ignore next */ []));
|
|
855
|
+
editMode = input(false, ...(ngDevMode ? [{ debugName: "editMode" }] : /* istanbul ignore next */ []));
|
|
856
|
+
removeCard = output();
|
|
857
|
+
gridColumn = computed(() => {
|
|
858
|
+
const width = this.card().w ?? 12;
|
|
859
|
+
return this.createGridTrack(this.card().x, width);
|
|
860
|
+
}, ...(ngDevMode ? [{ debugName: "gridColumn" }] : /* istanbul ignore next */ []));
|
|
861
|
+
gridRow = computed(() => {
|
|
862
|
+
const height = this.card().h ?? 100;
|
|
863
|
+
return this.createGridTrack(this.card().y, height);
|
|
864
|
+
}, ...(ngDevMode ? [{ debugName: "gridRow" }] : /* istanbul ignore next */ []));
|
|
865
|
+
host = viewChild('elementHost', { ...(ngDevMode ? { debugName: "host" } : /* istanbul ignore next */ {}), read: ViewContainerRef });
|
|
866
|
+
renderer = inject(Renderer2);
|
|
867
|
+
constructor() {
|
|
868
|
+
effect((onCleanup) => {
|
|
869
|
+
const host = this.host();
|
|
870
|
+
const cfg = this.card();
|
|
871
|
+
if (!host || !cfg.component)
|
|
872
|
+
return;
|
|
873
|
+
host.clear();
|
|
874
|
+
host.element.nativeElement.innerHTML = '';
|
|
875
|
+
switch (cfg.type) {
|
|
876
|
+
case CARD_TYPES.SAP_UI:
|
|
877
|
+
mountSapCard(cfg, host, onCleanup);
|
|
878
|
+
break;
|
|
879
|
+
case CARD_TYPES.ANGULAR:
|
|
880
|
+
mountAngularCard(cfg, host, onCleanup);
|
|
881
|
+
break;
|
|
882
|
+
case CARD_TYPES.WC:
|
|
883
|
+
default:
|
|
884
|
+
mountWcCard(cfg, host, onCleanup, this.renderer);
|
|
885
|
+
break;
|
|
886
|
+
}
|
|
887
|
+
});
|
|
888
|
+
}
|
|
889
|
+
createGridTrack(start, span) {
|
|
890
|
+
if (start === undefined) {
|
|
891
|
+
return `span ${span}`;
|
|
892
|
+
}
|
|
893
|
+
return `${start + 1} / span ${span}`;
|
|
894
|
+
}
|
|
895
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: DashboardCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
896
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: DashboardCard, isStandalone: true, selector: "mfp-dashboard-card", inputs: { card: { classPropertyName: "card", publicName: "card", isSignal: true, isRequired: true, transformFunction: null }, editMode: { classPropertyName: "editMode", publicName: "editMode", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { removeCard: "removeCard" }, host: { properties: { "style.grid-column": "gridColumn()", "style.grid-row": "gridRow()" } }, viewQueries: [{ propertyName: "host", first: true, predicate: ["elementHost"], descendants: true, read: ViewContainerRef, isSignal: true }], ngImport: i0, template: "@if (card().component) {\n <div class=\"component-card\">\n @if (editMode()) {\n <div class=\"remove-wrapper\">\n <ui5-button\n class=\"card__remove\"\n design=\"Default\"\n icon=\"decline\"\n (click)=\"removeCard.emit()\"\n />\n </div>\n }\n <div\n [style.pointer-events]=\"editMode() ? 'none' : 'auto'\"\n class=\"component-host\"\n >\n <div #elementHost></div>\n </div>\n </div>\n} @else {\n <div class=\"card\">\n <div\n [style.pointer-events]=\"editMode() ? 'none' : 'auto'\"\n class=\"card__body\"\n >\n <ng-content />\n </div>\n </div>\n}", styles: [":host{display:block;min-width:0;min-height:0;overflow:visible}.card__remove{position:absolute;top:0;right:-.5rem;z-index:10;border-radius:50%!important;width:1.25rem!important;height:1.25rem!important;min-width:unset!important;padding:0!important;font-size:.625rem!important}.remove-wrapper{position:sticky;height:.5rem;top:0;right:0;z-index:10}.component-card{position:relative;height:100%;overflow:visible;isolation:isolate;padding:0 .5rem}.component-host{height:100%}.card{position:relative;display:flex;flex-direction:column;height:100%;border:1px solid var(--sapGroup_ContentBorderColor, #d9d9d9);border-radius:.5rem;background:var(--sapBackgroundColor, #fff);overflow:visible}.card__header{padding:.75rem 1rem;font-weight:600;border-bottom:1px solid var(--sapGroup_ContentBorderColor, #d9d9d9);background:var(--sapGroup_TitleBackground, #f5f5f5)}.card__body{flex:1;padding:1rem;overflow:auto}\n"], dependencies: [{ kind: "component", type: Button, selector: "ui5-button, [ui5-button]", inputs: ["design", "disabled", "icon", "endIcon", "submits", "tooltip", "accessibleName", "accessibleNameRef", "accessibilityAttributes", "accessibleDescription", "type", "accessibleRole", "loading", "loadingDelay"], outputs: ["ui5Click"], exportAs: ["ui5Button"] }] });
|
|
897
|
+
}
|
|
898
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: DashboardCard, decorators: [{
|
|
899
|
+
type: Component,
|
|
900
|
+
args: [{ selector: 'mfp-dashboard-card', imports: [Button], encapsulation: ViewEncapsulation.Emulated, host: {
|
|
901
|
+
'[style.grid-column]': 'gridColumn()',
|
|
902
|
+
'[style.grid-row]': 'gridRow()',
|
|
903
|
+
}, template: "@if (card().component) {\n <div class=\"component-card\">\n @if (editMode()) {\n <div class=\"remove-wrapper\">\n <ui5-button\n class=\"card__remove\"\n design=\"Default\"\n icon=\"decline\"\n (click)=\"removeCard.emit()\"\n />\n </div>\n }\n <div\n [style.pointer-events]=\"editMode() ? 'none' : 'auto'\"\n class=\"component-host\"\n >\n <div #elementHost></div>\n </div>\n </div>\n} @else {\n <div class=\"card\">\n <div\n [style.pointer-events]=\"editMode() ? 'none' : 'auto'\"\n class=\"card__body\"\n >\n <ng-content />\n </div>\n </div>\n}", styles: [":host{display:block;min-width:0;min-height:0;overflow:visible}.card__remove{position:absolute;top:0;right:-.5rem;z-index:10;border-radius:50%!important;width:1.25rem!important;height:1.25rem!important;min-width:unset!important;padding:0!important;font-size:.625rem!important}.remove-wrapper{position:sticky;height:.5rem;top:0;right:0;z-index:10}.component-card{position:relative;height:100%;overflow:visible;isolation:isolate;padding:0 .5rem}.component-host{height:100%}.card{position:relative;display:flex;flex-direction:column;height:100%;border:1px solid var(--sapGroup_ContentBorderColor, #d9d9d9);border-radius:.5rem;background:var(--sapBackgroundColor, #fff);overflow:visible}.card__header{padding:.75rem 1rem;font-weight:600;border-bottom:1px solid var(--sapGroup_ContentBorderColor, #d9d9d9);background:var(--sapGroup_TitleBackground, #f5f5f5)}.card__body{flex:1;padding:1rem;overflow:auto}\n"] }]
|
|
904
|
+
}], ctorParameters: () => [], propDecorators: { card: [{ type: i0.Input, args: [{ isSignal: true, alias: "card", required: true }] }], editMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "editMode", required: false }] }], removeCard: [{ type: i0.Output, args: ["removeCard"] }], host: [{ type: i0.ViewChild, args: ['elementHost', { ...{ read: ViewContainerRef }, isSignal: true }] }] } });
|
|
905
|
+
|
|
906
|
+
const COMPACT_BREAKPOINT = 726;
|
|
907
|
+
const CELL_HEIGHT = 10;
|
|
908
|
+
|
|
909
|
+
class DashboardSection {
|
|
910
|
+
section = input.required(...(ngDevMode ? [{ debugName: "section" }] : /* istanbul ignore next */ []));
|
|
911
|
+
cards = input([], ...(ngDevMode ? [{ debugName: "cards" }] : /* istanbul ignore next */ []));
|
|
912
|
+
columns = input(12, ...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
|
|
913
|
+
editMode = input(false, ...(ngDevMode ? [{ debugName: "editMode" }] : /* istanbul ignore next */ []));
|
|
914
|
+
removeSection = output();
|
|
915
|
+
removeCard = output();
|
|
916
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: DashboardSection, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
917
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: DashboardSection, isStandalone: true, selector: "mfp-dashboard-section", inputs: { section: { classPropertyName: "section", publicName: "section", isSignal: true, isRequired: true, transformFunction: null }, cards: { classPropertyName: "cards", publicName: "cards", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, editMode: { classPropertyName: "editMode", publicName: "editMode", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { removeSection: "removeSection", removeCard: "removeCard" }, host: { properties: { "style.grid-column": "\"span \" + (section().w ?? 12)" } }, ngImport: i0, template: "<div\n class=\"section\"\n [class.section--edit]=\"editMode() && section().editable !== false\"\n>\n @if (editMode() && section().editable !== false) {\n <ui5-button\n class=\"section__remove\"\n design=\"Default\"\n icon=\"decline\"\n (click)=\"removeSection.emit()\"\n >\n </ui5-button>\n }\n @if (section().title) {\n <div class=\"section__header\">\n <span class=\"section__title\">{{ section().title }}</span>\n </div>\n }\n <div class=\"section__grid\" [style.--cols]=\"columns()\">\n @for (card of cards(); track card.id) {\n <mfp-dashboard-card\n [card]=\"card\"\n [editMode]=\"editMode() && section().editable !== false\"\n (removeCard)=\"removeCard.emit($any(card.id))\"\n />\n }\n </div>\n</div>\n", styles: [":host{display:block;min-width:0}.section{position:relative;display:flex;flex-direction:column;border:1px solid transparent;border-top:none;border-radius:0 0 .5rem .5rem;padding:1.25rem .75rem .75rem}.section--edit{border-color:var(--sapGroup_ContentBorderColor, #d9d9d9)}.section__remove{position:absolute;top:-.75rem;right:-.75rem;border-radius:50%!important;width:1.25rem!important;height:1.25rem!important;min-width:unset!important;padding:0!important;font-size:.625rem!important}.section__header{position:absolute;top:0;left:0;right:0;transform:translateY(-50%);display:flex;align-items:center;pointer-events:none}.section__header:before,.section__header:after{content:\"\";flex:1;height:1px;background:transparent}.section__header:before{max-width:.75rem;flex:0 0 .75rem}.section--edit .section__header:before,.section--edit .section__header:after{background:var(--sapGroup_ContentBorderColor, #d9d9d9)}.section__title{font-size:1rem;font-weight:600;color:var(--sapTextColor, #fff);padding:0 .5rem;white-space:nowrap;pointer-events:all}.section__grid{display:grid;grid-template-columns:repeat(var(--cols, 12),1fr);grid-auto-rows:var(--row-height, 10px);gap:var(--row-gap, 0px)}@media(max-width:599px){.section__grid{grid-template-columns:1fr}}@media(min-width:600px)and (max-width:1023px){.section__grid{grid-template-columns:repeat(8,1fr)}}\n"], dependencies: [{ kind: "component", type: DashboardCard, selector: "mfp-dashboard-card", inputs: ["card", "editMode"], outputs: ["removeCard"] }, { kind: "component", type: Button, selector: "ui5-button, [ui5-button]", inputs: ["design", "disabled", "icon", "endIcon", "submits", "tooltip", "accessibleName", "accessibleNameRef", "accessibilityAttributes", "accessibleDescription", "type", "accessibleRole", "loading", "loadingDelay"], outputs: ["ui5Click"], exportAs: ["ui5Button"] }] });
|
|
918
|
+
}
|
|
919
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: DashboardSection, decorators: [{
|
|
920
|
+
type: Component,
|
|
921
|
+
args: [{ selector: 'mfp-dashboard-section', imports: [DashboardCard, Button], encapsulation: ViewEncapsulation.Emulated, host: {
|
|
922
|
+
'[style.grid-column]': '"span " + (section().w ?? 12)',
|
|
923
|
+
}, template: "<div\n class=\"section\"\n [class.section--edit]=\"editMode() && section().editable !== false\"\n>\n @if (editMode() && section().editable !== false) {\n <ui5-button\n class=\"section__remove\"\n design=\"Default\"\n icon=\"decline\"\n (click)=\"removeSection.emit()\"\n >\n </ui5-button>\n }\n @if (section().title) {\n <div class=\"section__header\">\n <span class=\"section__title\">{{ section().title }}</span>\n </div>\n }\n <div class=\"section__grid\" [style.--cols]=\"columns()\">\n @for (card of cards(); track card.id) {\n <mfp-dashboard-card\n [card]=\"card\"\n [editMode]=\"editMode() && section().editable !== false\"\n (removeCard)=\"removeCard.emit($any(card.id))\"\n />\n }\n </div>\n</div>\n", styles: [":host{display:block;min-width:0}.section{position:relative;display:flex;flex-direction:column;border:1px solid transparent;border-top:none;border-radius:0 0 .5rem .5rem;padding:1.25rem .75rem .75rem}.section--edit{border-color:var(--sapGroup_ContentBorderColor, #d9d9d9)}.section__remove{position:absolute;top:-.75rem;right:-.75rem;border-radius:50%!important;width:1.25rem!important;height:1.25rem!important;min-width:unset!important;padding:0!important;font-size:.625rem!important}.section__header{position:absolute;top:0;left:0;right:0;transform:translateY(-50%);display:flex;align-items:center;pointer-events:none}.section__header:before,.section__header:after{content:\"\";flex:1;height:1px;background:transparent}.section__header:before{max-width:.75rem;flex:0 0 .75rem}.section--edit .section__header:before,.section--edit .section__header:after{background:var(--sapGroup_ContentBorderColor, #d9d9d9)}.section__title{font-size:1rem;font-weight:600;color:var(--sapTextColor, #fff);padding:0 .5rem;white-space:nowrap;pointer-events:all}.section__grid{display:grid;grid-template-columns:repeat(var(--cols, 12),1fr);grid-auto-rows:var(--row-height, 10px);gap:var(--row-gap, 0px)}@media(max-width:599px){.section__grid{grid-template-columns:1fr}}@media(min-width:600px)and (max-width:1023px){.section__grid{grid-template-columns:repeat(8,1fr)}}\n"] }]
|
|
924
|
+
}], propDecorators: { section: [{ type: i0.Input, args: [{ isSignal: true, alias: "section", required: true }] }], cards: [{ type: i0.Input, args: [{ isSignal: true, alias: "cards", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], editMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "editMode", required: false }] }], removeSection: [{ type: i0.Output, args: ["removeSection"] }], removeCard: [{ type: i0.Output, args: ["removeCard"] }] } });
|
|
925
|
+
|
|
926
|
+
document.body.classList.add('ui5-content-density-compact');
|
|
927
|
+
class Dashboard {
|
|
928
|
+
static registerAngularComponents(componentTypes) {
|
|
929
|
+
addComponentToRegistry(componentTypes);
|
|
930
|
+
}
|
|
931
|
+
config = input.required(...(ngDevMode ? [{ debugName: "config" }] : /* istanbul ignore next */ []));
|
|
932
|
+
sections = model([], ...(ngDevMode ? [{ debugName: "sections" }] : /* istanbul ignore next */ []));
|
|
933
|
+
cards = model([], ...(ngDevMode ? [{ debugName: "cards" }] : /* istanbul ignore next */ []));
|
|
934
|
+
availableCards = input([], ...(ngDevMode ? [{ debugName: "availableCards" }] : /* istanbul ignore next */ []));
|
|
935
|
+
saved = output();
|
|
936
|
+
actionButtonClick = output();
|
|
937
|
+
editMode = signal(false, ...(ngDevMode ? [{ debugName: "editMode" }] : /* istanbul ignore next */ []));
|
|
938
|
+
compactToolbar = signal(false, ...(ngDevMode ? [{ debugName: "compactToolbar" }] : /* istanbul ignore next */ []));
|
|
939
|
+
toolbarMenuOpen = signal(false, ...(ngDevMode ? [{ debugName: "toolbarMenuOpen" }] : /* istanbul ignore next */ []));
|
|
940
|
+
sectionsSnapshot = [];
|
|
941
|
+
cardsSnapshot = [];
|
|
942
|
+
gridStackItems = viewChild.required('grid');
|
|
943
|
+
resizeObserver;
|
|
944
|
+
hostEl = inject((ElementRef));
|
|
945
|
+
gridOptions = computed(() => ({
|
|
946
|
+
cellHeight: CELL_HEIGHT,
|
|
947
|
+
disableResize: !this.editMode(),
|
|
948
|
+
disableDrag: !this.editMode(),
|
|
949
|
+
columnOpts: {
|
|
950
|
+
breakpointForWindow: true,
|
|
951
|
+
breakpoints: [
|
|
952
|
+
{ w: 1440, c: 12, layout: 'none' },
|
|
953
|
+
{ w: 1024, c: 8, layout: 'compact' },
|
|
954
|
+
{ w: 600, c: 1, layout: 'list' },
|
|
955
|
+
],
|
|
956
|
+
},
|
|
957
|
+
}), ...(ngDevMode ? [{ debugName: "gridOptions" }] : /* istanbul ignore next */ []));
|
|
958
|
+
cardDialogOpen = signal(false, ...(ngDevMode ? [{ debugName: "cardDialogOpen" }] : /* istanbul ignore next */ []));
|
|
959
|
+
customActions = computed(() => this.config().customActions ?? [], ...(ngDevMode ? [{ debugName: "customActions" }] : /* istanbul ignore next */ []));
|
|
960
|
+
addedCardsIds = computed(() => new Set(this.cards().map((c) => c.id)), ...(ngDevMode ? [{ debugName: "addedCardsIds" }] : /* istanbul ignore next */ []));
|
|
961
|
+
editViewButton = computed(() => ({
|
|
962
|
+
icon: 'action-settings',
|
|
963
|
+
design: 'Transparent',
|
|
964
|
+
tooltip: 'Edit View',
|
|
965
|
+
text: '',
|
|
966
|
+
...this.config().buttonsSettings?.editViewButton,
|
|
967
|
+
}), ...(ngDevMode ? [{ debugName: "editViewButton" }] : /* istanbul ignore next */ []));
|
|
968
|
+
addCardButton = computed(() => ({
|
|
969
|
+
icon: '',
|
|
970
|
+
design: 'Default',
|
|
971
|
+
tooltip: '',
|
|
972
|
+
text: '+ Add Card',
|
|
973
|
+
...this.config().buttonsSettings?.addCardButton,
|
|
974
|
+
}), ...(ngDevMode ? [{ debugName: "addCardButton" }] : /* istanbul ignore next */ []));
|
|
975
|
+
sectionCards = computed(() => {
|
|
976
|
+
const all = this.cards();
|
|
977
|
+
return (sectionId) => all.filter((c) => c.sectionId === sectionId);
|
|
978
|
+
}, ...(ngDevMode ? [{ debugName: "sectionCards" }] : /* istanbul ignore next */ []));
|
|
979
|
+
cardsPosition = new Map();
|
|
980
|
+
looseCards = linkedSignal(() => this.cards().filter((c) => !c.sectionId), ...(ngDevMode ? [{ debugName: "looseCards" }] : /* istanbul ignore next */ []));
|
|
981
|
+
newGridStackNodes = [];
|
|
982
|
+
ngOnInit() {
|
|
983
|
+
this.resizeObserver = new ResizeObserver((entries) => {
|
|
984
|
+
const width = entries[0]?.contentRect.width ?? 0;
|
|
985
|
+
this.compactToolbar.set(width < COMPACT_BREAKPOINT);
|
|
986
|
+
});
|
|
987
|
+
this.resizeObserver.observe(this.hostEl.nativeElement);
|
|
988
|
+
}
|
|
989
|
+
ngOnDestroy() {
|
|
990
|
+
this.resizeObserver?.disconnect();
|
|
991
|
+
}
|
|
992
|
+
onMenuItemClick(actionId, event) {
|
|
993
|
+
if (actionId === 'edit-view') {
|
|
994
|
+
this.enterEditMode();
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
997
|
+
const action = this.customActions().find((a) => a.action === actionId);
|
|
998
|
+
if (action) {
|
|
999
|
+
this.actionButtonClick.emit({ event: event, action });
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
enterEditMode() {
|
|
1003
|
+
const gridStackNodes = this.gridStackItems()
|
|
1004
|
+
.gridstackItems?.toArray()
|
|
1005
|
+
.map((node) => node.options);
|
|
1006
|
+
if (gridStackNodes) {
|
|
1007
|
+
this.saveCardsPosition(gridStackNodes);
|
|
1008
|
+
}
|
|
1009
|
+
this.sectionsSnapshot = [...this.sections()];
|
|
1010
|
+
this.cardsSnapshot = [...this.cards()];
|
|
1011
|
+
this.editMode.set(true);
|
|
1012
|
+
}
|
|
1013
|
+
saveEdit() {
|
|
1014
|
+
this.saveCardsPosition(this.newGridStackNodes);
|
|
1015
|
+
this.saved.emit({
|
|
1016
|
+
sections: this.sections(),
|
|
1017
|
+
cards: this.cards().map((c) => {
|
|
1018
|
+
const pos = this.cardsPosition.get(c.id);
|
|
1019
|
+
return { ...c, x: pos?.x, y: pos?.y };
|
|
1020
|
+
}),
|
|
1021
|
+
});
|
|
1022
|
+
this.editMode.set(false);
|
|
1023
|
+
}
|
|
1024
|
+
cancelEdit() {
|
|
1025
|
+
this.sections.set(this.sectionsSnapshot);
|
|
1026
|
+
this.cards.set(this.cardsSnapshot.map((c) => {
|
|
1027
|
+
const pos = this.cardsPosition.get(c.id);
|
|
1028
|
+
return { ...c, x: pos?.x, y: pos?.y };
|
|
1029
|
+
}));
|
|
1030
|
+
this.cardDialogOpen.set(false);
|
|
1031
|
+
this.editMode.set(false);
|
|
1032
|
+
}
|
|
1033
|
+
removeSection(id) {
|
|
1034
|
+
this.sections.update((list) => list.filter((s) => s.id !== id));
|
|
1035
|
+
this.cards.update((list) => list.filter((c) => c.sectionId !== id));
|
|
1036
|
+
}
|
|
1037
|
+
removeCard(id) {
|
|
1038
|
+
this.cardsPosition.delete(id);
|
|
1039
|
+
this.cards.update((list) => list.filter((c) => c.id !== id));
|
|
1040
|
+
}
|
|
1041
|
+
openCardPanel() {
|
|
1042
|
+
this.cardDialogOpen.set(true);
|
|
1043
|
+
}
|
|
1044
|
+
closeCardPanel() {
|
|
1045
|
+
this.cardDialogOpen.set(false);
|
|
1046
|
+
}
|
|
1047
|
+
onCardsAdded(cards) {
|
|
1048
|
+
if (cards.length > 0) {
|
|
1049
|
+
this.cards.update((list) => [
|
|
1050
|
+
...list,
|
|
1051
|
+
...cards.map((ac) => ({
|
|
1052
|
+
...ac,
|
|
1053
|
+
})),
|
|
1054
|
+
]);
|
|
1055
|
+
}
|
|
1056
|
+
this.closeCardPanel();
|
|
1057
|
+
}
|
|
1058
|
+
onOrderChange(event) {
|
|
1059
|
+
this.newGridStackNodes = event.nodes;
|
|
1060
|
+
}
|
|
1061
|
+
saveCardsPosition(items) {
|
|
1062
|
+
items.forEach((node) => {
|
|
1063
|
+
if (node.id) {
|
|
1064
|
+
this.cardsPosition.set(node.id, { x: node.x, y: node.y });
|
|
1065
|
+
}
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
1068
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: Dashboard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1069
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: Dashboard, isStandalone: true, selector: "mfp-dashboard", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null }, sections: { classPropertyName: "sections", publicName: "sections", isSignal: true, isRequired: false, transformFunction: null }, cards: { classPropertyName: "cards", publicName: "cards", isSignal: true, isRequired: false, transformFunction: null }, availableCards: { classPropertyName: "availableCards", publicName: "availableCards", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sections: "sectionsChange", cards: "cardsChange", saved: "saved", actionButtonClick: "actionButtonClick" }, host: { properties: { "style.background-image": "config().backgroundImageUrl ? \"url(\" + config().backgroundImageUrl + \")\" : null" } }, viewQueries: [{ propertyName: "gridStackItems", first: true, predicate: ["grid"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"mfp-dashboard\">\n <div class=\"mfp-dashboard__topbar\">\n <div class=\"mfp-dashboard__topbar-row\">\n <div class=\"mfp-dashboard__header\">\n @if (config().title) {\n <ui5-title level=\"H2\" size=\"H2\" wrapping-type=\"Normal\">{{\n config().title\n }}</ui5-title>\n }\n @if (config().description) {\n <ui5-text>{{ config().description }}</ui5-text>\n }\n </div>\n\n <div class=\"mfp-dashboard__toolbar\">\n @if (editMode()) {\n <ui5-button\n id=\"add-card-btn\"\n [design]=\"addCardButton().design\"\n [icon]=\"addCardButton().icon\"\n [tooltip]=\"addCardButton().tooltip\"\n (click)=\"openCardPanel()\"\n >{{ addCardButton().text }}</ui5-button\n >\n } @else if (compactToolbar()) {\n <ui5-button\n #menuBtn\n design=\"Transparent\"\n icon=\"menu2\"\n tooltip=\"Actions\"\n (click)=\"toolbarMenuOpen.update((v) => !v)\"\n ></ui5-button>\n <ui5-menu\n [open]=\"toolbarMenuOpen()\"\n [opener]=\"menuBtn.element\"\n (ui5Open)=\"toolbarMenuOpen.set(true)\"\n (ui5Close)=\"toolbarMenuOpen.set(false)\"\n (ui5ItemClick)=\"\n onMenuItemClick(\n $any($event).detail.item.dataset['action'],\n $event\n )\n \"\n >\n @for (action of customActions(); track action.action) {\n <ui5-menu-item\n [icon]=\"action.icon ?? ''\"\n [text]=\"action.text ?? ''\"\n [attr.data-action]=\"action.action\"\n ></ui5-menu-item>\n }\n @if (config().editable) {\n <ui5-menu-separator></ui5-menu-separator>\n <ui5-menu-item\n [icon]=\"editViewButton().icon\"\n [text]=\"editViewButton().text || 'Edit View'\"\n data-action=\"edit-view\"\n ></ui5-menu-item>\n }\n </ui5-menu>\n } @else {\n @for (action of customActions(); track action.action) {\n <ui5-button\n [design]=\"action.design ?? 'Default'\"\n [icon]=\"action.icon ?? ''\"\n [endIcon]=\"action?.endIcon\"\n [tooltip]=\"action.tooltip ?? ''\"\n (click)=\"\n actionButtonClick.emit({ event: $event, action: action })\n \"\n >{{ action.text }}</ui5-button\n >\n }\n @if (config().editable) {\n <ui5-button\n [design]=\"editViewButton().design\"\n [icon]=\"editViewButton().icon\"\n [tooltip]=\"editViewButton().tooltip\"\n (click)=\"enterEditMode()\"\n >{{ editViewButton().text }}</ui5-button\n >\n }\n }\n </div>\n </div>\n\n <div class=\"mfp-dashboard__subheader\">\n <slot name=\"dashboard-subheader\"></slot>\n </div>\n </div>\n <div class=\"mfp-sections-container\">\n @for (section of sections(); track section.id) {\n <mfp-dashboard-section\n [section]=\"section\"\n [cards]=\"sectionCards()(section.id)\"\n [columns]=\"12\"\n [editMode]=\"editMode()\"\n (removeSection)=\"removeSection(section.id)\"\n (removeCard)=\"removeCard($event)\"\n />\n }\n </div>\n <gridstack #grid (changeCB)=\"onOrderChange($event)\" [options]=\"gridOptions()\">\n @for (card of looseCards(); track card.id) {\n <gridstack-item\n [style.cursor]=\"editMode() ? 'pointer' : 'auto'\"\n [options]=\"card\"\n >\n <mfp-dashboard-card\n [card]=\"card\"\n [editMode]=\"editMode()\"\n (removeCard)=\"removeCard($any(card.id))\"\n />\n </gridstack-item>\n }\n </gridstack>\n\n @if (editMode()) {\n <div class=\"mfp-dashboard__edit-bar\">\n <ui5-button design=\"Emphasized\" (click)=\"saveEdit()\">Save</ui5-button>\n <ui5-button design=\"Transparent\" (click)=\"cancelEdit()\"\n >Cancel</ui5-button\n >\n </div>\n }\n</div>\n\n<mfp-add-card-dialog\n [open]=\"cardDialogOpen()\"\n [availableCards]=\"availableCards()\"\n [addedCardsIds]=\"addedCardsIds()\"\n (confirm)=\"onCardsAdded($event)\"\n (cancel)=\"closeCardPanel()\"\n/>\n", styles: [".grid-stack{position:relative}.grid-stack-rtl{direction:ltr}.grid-stack-rtl>.grid-stack-item{direction:rtl}.grid-stack-placeholder>.placeholder-content{background-color:#0000001a;margin:0;position:absolute;width:auto;z-index:0!important}.grid-stack>.grid-stack-item{position:absolute;padding:0;top:0;width:var(--gs-column-width);height:var(--gs-cell-height)}.grid-stack>.grid-stack-item>.grid-stack-item-content{margin:0;position:absolute;width:auto;overflow-x:hidden;overflow-y:auto}.grid-stack>.grid-stack-item.size-to-content:not(.size-to-content-max)>.grid-stack-item-content{overflow-y:hidden}.grid-stack:not(.grid-stack-rtl)>.grid-stack-item{left:0}.grid-stack.grid-stack-rtl>.grid-stack-item{right:0}.grid-stack>.grid-stack-item>.grid-stack-item-content,.grid-stack>.grid-stack-placeholder>.placeholder-content{top:var(--gs-item-margin-top);right:var(--gs-item-margin-right);bottom:var(--gs-item-margin-bottom);left:var(--gs-item-margin-left)}.grid-stack-item>.ui-resizable-handle{position:absolute;font-size:.1px;display:block;-ms-touch-action:none;touch-action:none}.grid-stack-item.ui-resizable-autohide>.ui-resizable-handle,.grid-stack-item.ui-resizable-disabled>.ui-resizable-handle{display:none}.grid-stack-item>.ui-resizable-ne,.grid-stack-item>.ui-resizable-nw,.grid-stack-item>.ui-resizable-se,.grid-stack-item>.ui-resizable-sw{background-image:url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" stroke=\"%23666\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewBox=\"0 0 20 20\"><path d=\"m10 3 2 2H8l2-2v14l-2-2h4l-2 2\"/></svg>');background-repeat:no-repeat;background-position:center}.grid-stack-item>.ui-resizable-ne{transform:rotate(45deg)}.grid-stack-item>.ui-resizable-sw{transform:rotate(45deg)}.grid-stack-item>.ui-resizable-nw{transform:rotate(-45deg)}.grid-stack-item>.ui-resizable-se{transform:rotate(-45deg)}.grid-stack-item>.ui-resizable-nw{cursor:nw-resize;width:20px;height:20px;top:var(--gs-item-margin-top);left:var(--gs-item-margin-left)}.grid-stack-item>.ui-resizable-n{cursor:n-resize;height:10px;top:var(--gs-item-margin-top);left:25px;right:25px}.grid-stack-item>.ui-resizable-ne{cursor:ne-resize;width:20px;height:20px;top:var(--gs-item-margin-top);right:var(--gs-item-margin-right)}.grid-stack-item>.ui-resizable-e{cursor:e-resize;width:10px;top:15px;bottom:15px;right:var(--gs-item-margin-right)}.grid-stack-item>.ui-resizable-se{cursor:se-resize;width:20px;height:20px;bottom:var(--gs-item-margin-bottom);right:var(--gs-item-margin-right)}.grid-stack-item>.ui-resizable-s{cursor:s-resize;height:10px;left:25px;bottom:var(--gs-item-margin-bottom);right:25px}.grid-stack-item>.ui-resizable-sw{cursor:sw-resize;width:20px;height:20px;bottom:var(--gs-item-margin-bottom);left:var(--gs-item-margin-left)}.grid-stack-item>.ui-resizable-w{cursor:w-resize;width:10px;top:15px;bottom:15px;left:var(--gs-item-margin-left)}.grid-stack-item.ui-draggable-dragging>.ui-resizable-handle{display:none!important}.grid-stack-item.ui-draggable-dragging{will-change:left,right,top}.grid-stack-item.ui-resizable-resizing{will-change:width,height}.ui-draggable-dragging,.ui-resizable-resizing{z-index:10000}.ui-draggable-dragging>.grid-stack-item-content,.ui-resizable-resizing>.grid-stack-item-content{box-shadow:1px 4px 6px #0003;opacity:.8}.grid-stack-animate,.grid-stack-animate .grid-stack-item{transition:left .3s,right .3s,top .3s,height .3s,width .3s}.grid-stack-animate .grid-stack-item.grid-stack-placeholder,.grid-stack-animate .grid-stack-item.ui-draggable-dragging,.grid-stack-animate .grid-stack-item.ui-resizable-resizing{transition:left 0s,right 0s,top 0s,height 0s,width 0s}.grid-stack>.grid-stack-item[gs-y=\"0\"]{top:0}.grid-stack:not(.grid-stack-rtl)>.grid-stack-item[gs-x=\"0\"]{left:0}.grid-stack.grid-stack-rtl>.grid-stack-item[gs-x=\"0\"]{right:0}mfp-dashboard,mfp-wc-dashboard{display:block;margin:0;padding:0;min-height:100%;background:var(--sapShellColor, #354a5e);background-size:cover;background-position:center;background-repeat:no-repeat;background-attachment:local}.mfp-dashboard{padding:2rem}.mfp-sections-container{display:grid;grid-template-columns:repeat(12,1fr);grid-auto-rows:auto;align-content:start;gap:1rem}@media(max-width:726px){.mfp-sections-container{grid-template-columns:1fr}}@media(min-width:727px)and (max-width:1200px){.mfp-sections-container{grid-template-columns:repeat(8,1fr)}}.mfp-dashboard__topbar{grid-column:1/-1;align-self:start;display:flex;flex-direction:column;gap:1rem;padding-bottom:3rem;width:100%;min-width:0}.mfp-dashboard__topbar-row{display:flex;align-items:flex-start;justify-content:space-between;gap:1rem}.mfp-dashboard__header{display:flex;flex-direction:column;gap:.25rem}.mfp-dashboard__toolbar{flex-shrink:0;display:flex;gap:.5rem}.mfp-dashboard__subheader{display:flex;align-items:center;gap:1rem}.mfp-dashboard__edit-bar{position:fixed;bottom:20px;left:16px;right:16px;z-index:1000;display:flex;justify-content:flex-end;align-items:center;gap:8px;padding:7px 16px;background:var(--sapPageFooter_Background, white);border-radius:var(--sapElement_BorderCornerRadius, 12px);box-shadow:0 0 0 1px #2235487a,0 2px 8px #2235484d;box-sizing:border-box;overflow:hidden}\n"], dependencies: [{ kind: "component", type: GridstackComponent, selector: "gridstack", inputs: ["options", "isEmpty"], outputs: ["addedCB", "changeCB", "disableCB", "dragCB", "dragStartCB", "dragStopCB", "droppedCB", "enableCB", "removedCB", "resizeCB", "resizeStartCB", "resizeStopCB"] }, { kind: "component", type: GridstackItemComponent, selector: "gridstack-item", inputs: ["options"] }, { kind: "component", type: AddCardDialog, selector: "mfp-add-card-dialog", inputs: ["availableCards", "addedCardsIds", "open"], outputs: ["confirm", "cancel"] }, { kind: "component", type: DashboardSection, selector: "mfp-dashboard-section", inputs: ["section", "cards", "columns", "editMode"], outputs: ["removeSection", "removeCard"] }, { kind: "component", type: DashboardCard, selector: "mfp-dashboard-card", inputs: ["card", "editMode"], outputs: ["removeCard"] }, { kind: "component", type: Button, selector: "ui5-button, [ui5-button]", inputs: ["design", "disabled", "icon", "endIcon", "submits", "tooltip", "accessibleName", "accessibleNameRef", "accessibilityAttributes", "accessibleDescription", "type", "accessibleRole", "loading", "loadingDelay"], outputs: ["ui5Click"], exportAs: ["ui5Button"] }, { kind: "component", type: Menu, selector: "ui5-menu, [ui5-menu]", inputs: ["headerText", "open", "placement", "horizontalAlign", "loading", "loadingDelay", "opener"], outputs: ["ui5ItemClick", "ui5BeforeOpen", "ui5Open", "ui5BeforeClose", "ui5Close"], exportAs: ["ui5Menu"] }, { kind: "component", type: MenuItem, selector: "ui5-menu-item, [ui5-menu-item]", inputs: ["text", "additionalText", "icon", "disabled", "loading", "loadingDelay", "accessibleName", "tooltip", "checked", "accessibilityAttributes", "type", "navigated", "highlight", "selected"], outputs: ["ui5BeforeOpen", "ui5Open", "ui5BeforeClose", "ui5Close", "ui5Check", "ui5DetailClick"], exportAs: ["ui5MenuItem"] }, { kind: "component", type: MenuSeparator, selector: "ui5-menu-separator, [ui5-menu-separator]", exportAs: ["ui5MenuSeparator"] }, { kind: "component", type: Title, selector: "ui5-title, [ui5-title]", inputs: ["wrappingType", "level", "size"], exportAs: ["ui5Title"] }, { kind: "component", type: Text, selector: "ui5-text, [ui5-text]", inputs: ["maxLines", "emptyIndicatorMode"], exportAs: ["ui5Text"] }], encapsulation: i0.ViewEncapsulation.None });
|
|
1070
|
+
}
|
|
1071
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: Dashboard, decorators: [{
|
|
1072
|
+
type: Component,
|
|
1073
|
+
args: [{ selector: 'mfp-dashboard', imports: [
|
|
1074
|
+
GridstackComponent,
|
|
1075
|
+
GridstackItemComponent,
|
|
1076
|
+
AddCardDialog,
|
|
1077
|
+
DashboardSection,
|
|
1078
|
+
DashboardCard,
|
|
1079
|
+
Button,
|
|
1080
|
+
Menu,
|
|
1081
|
+
MenuItem,
|
|
1082
|
+
MenuSeparator,
|
|
1083
|
+
Title,
|
|
1084
|
+
Text,
|
|
1085
|
+
], encapsulation: ViewEncapsulation.None, host: {
|
|
1086
|
+
'[style.background-image]': 'config().backgroundImageUrl ? "url(" + config().backgroundImageUrl + ")" : null',
|
|
1087
|
+
}, template: "<div class=\"mfp-dashboard\">\n <div class=\"mfp-dashboard__topbar\">\n <div class=\"mfp-dashboard__topbar-row\">\n <div class=\"mfp-dashboard__header\">\n @if (config().title) {\n <ui5-title level=\"H2\" size=\"H2\" wrapping-type=\"Normal\">{{\n config().title\n }}</ui5-title>\n }\n @if (config().description) {\n <ui5-text>{{ config().description }}</ui5-text>\n }\n </div>\n\n <div class=\"mfp-dashboard__toolbar\">\n @if (editMode()) {\n <ui5-button\n id=\"add-card-btn\"\n [design]=\"addCardButton().design\"\n [icon]=\"addCardButton().icon\"\n [tooltip]=\"addCardButton().tooltip\"\n (click)=\"openCardPanel()\"\n >{{ addCardButton().text }}</ui5-button\n >\n } @else if (compactToolbar()) {\n <ui5-button\n #menuBtn\n design=\"Transparent\"\n icon=\"menu2\"\n tooltip=\"Actions\"\n (click)=\"toolbarMenuOpen.update((v) => !v)\"\n ></ui5-button>\n <ui5-menu\n [open]=\"toolbarMenuOpen()\"\n [opener]=\"menuBtn.element\"\n (ui5Open)=\"toolbarMenuOpen.set(true)\"\n (ui5Close)=\"toolbarMenuOpen.set(false)\"\n (ui5ItemClick)=\"\n onMenuItemClick(\n $any($event).detail.item.dataset['action'],\n $event\n )\n \"\n >\n @for (action of customActions(); track action.action) {\n <ui5-menu-item\n [icon]=\"action.icon ?? ''\"\n [text]=\"action.text ?? ''\"\n [attr.data-action]=\"action.action\"\n ></ui5-menu-item>\n }\n @if (config().editable) {\n <ui5-menu-separator></ui5-menu-separator>\n <ui5-menu-item\n [icon]=\"editViewButton().icon\"\n [text]=\"editViewButton().text || 'Edit View'\"\n data-action=\"edit-view\"\n ></ui5-menu-item>\n }\n </ui5-menu>\n } @else {\n @for (action of customActions(); track action.action) {\n <ui5-button\n [design]=\"action.design ?? 'Default'\"\n [icon]=\"action.icon ?? ''\"\n [endIcon]=\"action?.endIcon\"\n [tooltip]=\"action.tooltip ?? ''\"\n (click)=\"\n actionButtonClick.emit({ event: $event, action: action })\n \"\n >{{ action.text }}</ui5-button\n >\n }\n @if (config().editable) {\n <ui5-button\n [design]=\"editViewButton().design\"\n [icon]=\"editViewButton().icon\"\n [tooltip]=\"editViewButton().tooltip\"\n (click)=\"enterEditMode()\"\n >{{ editViewButton().text }}</ui5-button\n >\n }\n }\n </div>\n </div>\n\n <div class=\"mfp-dashboard__subheader\">\n <slot name=\"dashboard-subheader\"></slot>\n </div>\n </div>\n <div class=\"mfp-sections-container\">\n @for (section of sections(); track section.id) {\n <mfp-dashboard-section\n [section]=\"section\"\n [cards]=\"sectionCards()(section.id)\"\n [columns]=\"12\"\n [editMode]=\"editMode()\"\n (removeSection)=\"removeSection(section.id)\"\n (removeCard)=\"removeCard($event)\"\n />\n }\n </div>\n <gridstack #grid (changeCB)=\"onOrderChange($event)\" [options]=\"gridOptions()\">\n @for (card of looseCards(); track card.id) {\n <gridstack-item\n [style.cursor]=\"editMode() ? 'pointer' : 'auto'\"\n [options]=\"card\"\n >\n <mfp-dashboard-card\n [card]=\"card\"\n [editMode]=\"editMode()\"\n (removeCard)=\"removeCard($any(card.id))\"\n />\n </gridstack-item>\n }\n </gridstack>\n\n @if (editMode()) {\n <div class=\"mfp-dashboard__edit-bar\">\n <ui5-button design=\"Emphasized\" (click)=\"saveEdit()\">Save</ui5-button>\n <ui5-button design=\"Transparent\" (click)=\"cancelEdit()\"\n >Cancel</ui5-button\n >\n </div>\n }\n</div>\n\n<mfp-add-card-dialog\n [open]=\"cardDialogOpen()\"\n [availableCards]=\"availableCards()\"\n [addedCardsIds]=\"addedCardsIds()\"\n (confirm)=\"onCardsAdded($event)\"\n (cancel)=\"closeCardPanel()\"\n/>\n", styles: [".grid-stack{position:relative}.grid-stack-rtl{direction:ltr}.grid-stack-rtl>.grid-stack-item{direction:rtl}.grid-stack-placeholder>.placeholder-content{background-color:#0000001a;margin:0;position:absolute;width:auto;z-index:0!important}.grid-stack>.grid-stack-item{position:absolute;padding:0;top:0;width:var(--gs-column-width);height:var(--gs-cell-height)}.grid-stack>.grid-stack-item>.grid-stack-item-content{margin:0;position:absolute;width:auto;overflow-x:hidden;overflow-y:auto}.grid-stack>.grid-stack-item.size-to-content:not(.size-to-content-max)>.grid-stack-item-content{overflow-y:hidden}.grid-stack:not(.grid-stack-rtl)>.grid-stack-item{left:0}.grid-stack.grid-stack-rtl>.grid-stack-item{right:0}.grid-stack>.grid-stack-item>.grid-stack-item-content,.grid-stack>.grid-stack-placeholder>.placeholder-content{top:var(--gs-item-margin-top);right:var(--gs-item-margin-right);bottom:var(--gs-item-margin-bottom);left:var(--gs-item-margin-left)}.grid-stack-item>.ui-resizable-handle{position:absolute;font-size:.1px;display:block;-ms-touch-action:none;touch-action:none}.grid-stack-item.ui-resizable-autohide>.ui-resizable-handle,.grid-stack-item.ui-resizable-disabled>.ui-resizable-handle{display:none}.grid-stack-item>.ui-resizable-ne,.grid-stack-item>.ui-resizable-nw,.grid-stack-item>.ui-resizable-se,.grid-stack-item>.ui-resizable-sw{background-image:url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" stroke=\"%23666\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" viewBox=\"0 0 20 20\"><path d=\"m10 3 2 2H8l2-2v14l-2-2h4l-2 2\"/></svg>');background-repeat:no-repeat;background-position:center}.grid-stack-item>.ui-resizable-ne{transform:rotate(45deg)}.grid-stack-item>.ui-resizable-sw{transform:rotate(45deg)}.grid-stack-item>.ui-resizable-nw{transform:rotate(-45deg)}.grid-stack-item>.ui-resizable-se{transform:rotate(-45deg)}.grid-stack-item>.ui-resizable-nw{cursor:nw-resize;width:20px;height:20px;top:var(--gs-item-margin-top);left:var(--gs-item-margin-left)}.grid-stack-item>.ui-resizable-n{cursor:n-resize;height:10px;top:var(--gs-item-margin-top);left:25px;right:25px}.grid-stack-item>.ui-resizable-ne{cursor:ne-resize;width:20px;height:20px;top:var(--gs-item-margin-top);right:var(--gs-item-margin-right)}.grid-stack-item>.ui-resizable-e{cursor:e-resize;width:10px;top:15px;bottom:15px;right:var(--gs-item-margin-right)}.grid-stack-item>.ui-resizable-se{cursor:se-resize;width:20px;height:20px;bottom:var(--gs-item-margin-bottom);right:var(--gs-item-margin-right)}.grid-stack-item>.ui-resizable-s{cursor:s-resize;height:10px;left:25px;bottom:var(--gs-item-margin-bottom);right:25px}.grid-stack-item>.ui-resizable-sw{cursor:sw-resize;width:20px;height:20px;bottom:var(--gs-item-margin-bottom);left:var(--gs-item-margin-left)}.grid-stack-item>.ui-resizable-w{cursor:w-resize;width:10px;top:15px;bottom:15px;left:var(--gs-item-margin-left)}.grid-stack-item.ui-draggable-dragging>.ui-resizable-handle{display:none!important}.grid-stack-item.ui-draggable-dragging{will-change:left,right,top}.grid-stack-item.ui-resizable-resizing{will-change:width,height}.ui-draggable-dragging,.ui-resizable-resizing{z-index:10000}.ui-draggable-dragging>.grid-stack-item-content,.ui-resizable-resizing>.grid-stack-item-content{box-shadow:1px 4px 6px #0003;opacity:.8}.grid-stack-animate,.grid-stack-animate .grid-stack-item{transition:left .3s,right .3s,top .3s,height .3s,width .3s}.grid-stack-animate .grid-stack-item.grid-stack-placeholder,.grid-stack-animate .grid-stack-item.ui-draggable-dragging,.grid-stack-animate .grid-stack-item.ui-resizable-resizing{transition:left 0s,right 0s,top 0s,height 0s,width 0s}.grid-stack>.grid-stack-item[gs-y=\"0\"]{top:0}.grid-stack:not(.grid-stack-rtl)>.grid-stack-item[gs-x=\"0\"]{left:0}.grid-stack.grid-stack-rtl>.grid-stack-item[gs-x=\"0\"]{right:0}mfp-dashboard,mfp-wc-dashboard{display:block;margin:0;padding:0;min-height:100%;background:var(--sapShellColor, #354a5e);background-size:cover;background-position:center;background-repeat:no-repeat;background-attachment:local}.mfp-dashboard{padding:2rem}.mfp-sections-container{display:grid;grid-template-columns:repeat(12,1fr);grid-auto-rows:auto;align-content:start;gap:1rem}@media(max-width:726px){.mfp-sections-container{grid-template-columns:1fr}}@media(min-width:727px)and (max-width:1200px){.mfp-sections-container{grid-template-columns:repeat(8,1fr)}}.mfp-dashboard__topbar{grid-column:1/-1;align-self:start;display:flex;flex-direction:column;gap:1rem;padding-bottom:3rem;width:100%;min-width:0}.mfp-dashboard__topbar-row{display:flex;align-items:flex-start;justify-content:space-between;gap:1rem}.mfp-dashboard__header{display:flex;flex-direction:column;gap:.25rem}.mfp-dashboard__toolbar{flex-shrink:0;display:flex;gap:.5rem}.mfp-dashboard__subheader{display:flex;align-items:center;gap:1rem}.mfp-dashboard__edit-bar{position:fixed;bottom:20px;left:16px;right:16px;z-index:1000;display:flex;justify-content:flex-end;align-items:center;gap:8px;padding:7px 16px;background:var(--sapPageFooter_Background, white);border-radius:var(--sapElement_BorderCornerRadius, 12px);box-shadow:0 0 0 1px #2235487a,0 2px 8px #2235484d;box-sizing:border-box;overflow:hidden}\n"] }]
|
|
1088
|
+
}], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: true }] }], sections: [{ type: i0.Input, args: [{ isSignal: true, alias: "sections", required: false }] }, { type: i0.Output, args: ["sectionsChange"] }], cards: [{ type: i0.Input, args: [{ isSignal: true, alias: "cards", required: false }] }, { type: i0.Output, args: ["cardsChange"] }], availableCards: [{ type: i0.Input, args: [{ isSignal: true, alias: "availableCards", required: false }] }], saved: [{ type: i0.Output, args: ["saved"] }], actionButtonClick: [{ type: i0.Output, args: ["actionButtonClick"] }], gridStackItems: [{ type: i0.ViewChild, args: ['grid', { isSignal: true }] }] } });
|
|
1089
|
+
|
|
1090
|
+
class VisitedServiceCard {
|
|
1091
|
+
serviceType = input.required(...(ngDevMode ? [{ debugName: "serviceType" }] : /* istanbul ignore next */ []));
|
|
1092
|
+
serviceName = input.required(...(ngDevMode ? [{ debugName: "serviceName" }] : /* istanbul ignore next */ []));
|
|
1093
|
+
serviceDescription = input.required(...(ngDevMode ? [{ debugName: "serviceDescription" }] : /* istanbul ignore next */ []));
|
|
1094
|
+
serviceIcon = input.required(...(ngDevMode ? [{ debugName: "serviceIcon" }] : /* istanbul ignore next */ []));
|
|
1095
|
+
path = input.required(...(ngDevMode ? [{ debugName: "path" }] : /* istanbul ignore next */ []));
|
|
1096
|
+
click = output();
|
|
1097
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: VisitedServiceCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1098
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.12", type: VisitedServiceCard, isStandalone: true, selector: "mfp-visited-service-card", inputs: { serviceType: { classPropertyName: "serviceType", publicName: "serviceType", isSignal: true, isRequired: true, transformFunction: null }, serviceName: { classPropertyName: "serviceName", publicName: "serviceName", isSignal: true, isRequired: true, transformFunction: null }, serviceDescription: { classPropertyName: "serviceDescription", publicName: "serviceDescription", isSignal: true, isRequired: true, transformFunction: null }, serviceIcon: { classPropertyName: "serviceIcon", publicName: "serviceIcon", isSignal: true, isRequired: true, transformFunction: null }, path: { classPropertyName: "path", publicName: "path", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { click: "click" }, ngImport: i0, template: "<div class=\"visited-card-wrapper\" (click)=\"click.emit(path())\">\n <span class=\"visited-card__type-badge\">{{ serviceType() }}</span>\n <ui5-card class=\"visited-card\">\n <ui5-card-header\n slot=\"header\"\n [titleText]=\"serviceName()\"\n [subtitleText]=\"serviceDescription()\"\n [interactive]=\"true\"\n >\n <ui5-icon\n slot=\"avatar\"\n [name]=\"serviceIcon()\"\n class=\"visited-card__icon\"\n ></ui5-icon>\n </ui5-card-header>\n </ui5-card>\n</div>\n", styles: [":host{display:block}.visited-card-wrapper{position:relative;padding-top:.625rem;cursor:pointer}.visited-card{width:100%}.visited-card__type-badge{position:absolute;top:0;right:.75rem;display:inline-block;padding:.125rem .625rem;border-radius:.75rem;background-color:#e8f3ff;color:#0070f2;font-size:var(--sapFontSmallSize, .75rem);font-weight:700;white-space:nowrap;z-index:1}.visited-card__icon{width:2.5rem;height:2.5rem;color:var(--sapHighlightColor, #0070f2)}\n"], dependencies: [{ kind: "component", type: Card, selector: "ui5-card, [ui5-card]", inputs: ["accessibleName", "accessibleNameRef", "loading", "loadingDelay"], exportAs: ["ui5Card"] }, { kind: "component", type: CardHeader, selector: "ui5-card-header, [ui5-card-header]", inputs: ["titleText", "subtitleText", "additionalText", "interactive"], outputs: ["ui5Click"], exportAs: ["ui5CardHeader"] }, { kind: "component", type: Icon, selector: "ui5-icon, [ui5-icon]", inputs: ["design", "name", "accessibleName", "showTooltip", "mode"], outputs: ["ui5Click"], exportAs: ["ui5Icon"] }], encapsulation: i0.ViewEncapsulation.ShadowDom });
|
|
1099
|
+
}
|
|
1100
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: VisitedServiceCard, decorators: [{
|
|
1101
|
+
type: Component,
|
|
1102
|
+
args: [{ selector: 'mfp-visited-service-card', encapsulation: ViewEncapsulation.ShadowDom, imports: [Card, CardHeader, Icon], template: "<div class=\"visited-card-wrapper\" (click)=\"click.emit(path())\">\n <span class=\"visited-card__type-badge\">{{ serviceType() }}</span>\n <ui5-card class=\"visited-card\">\n <ui5-card-header\n slot=\"header\"\n [titleText]=\"serviceName()\"\n [subtitleText]=\"serviceDescription()\"\n [interactive]=\"true\"\n >\n <ui5-icon\n slot=\"avatar\"\n [name]=\"serviceIcon()\"\n class=\"visited-card__icon\"\n ></ui5-icon>\n </ui5-card-header>\n </ui5-card>\n</div>\n", styles: [":host{display:block}.visited-card-wrapper{position:relative;padding-top:.625rem;cursor:pointer}.visited-card{width:100%}.visited-card__type-badge{position:absolute;top:0;right:.75rem;display:inline-block;padding:.125rem .625rem;border-radius:.75rem;background-color:#e8f3ff;color:#0070f2;font-size:var(--sapFontSmallSize, .75rem);font-weight:700;white-space:nowrap;z-index:1}.visited-card__icon{width:2.5rem;height:2.5rem;color:var(--sapHighlightColor, #0070f2)}\n"] }]
|
|
1103
|
+
}], propDecorators: { serviceType: [{ type: i0.Input, args: [{ isSignal: true, alias: "serviceType", required: true }] }], serviceName: [{ type: i0.Input, args: [{ isSignal: true, alias: "serviceName", required: true }] }], serviceDescription: [{ type: i0.Input, args: [{ isSignal: true, alias: "serviceDescription", required: true }] }], serviceIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "serviceIcon", required: true }] }], path: [{ type: i0.Input, args: [{ isSignal: true, alias: "path", required: true }] }], click: [{ type: i0.Output, args: ["click"] }] } });
|
|
1104
|
+
|
|
1105
|
+
class Favorites {
|
|
1106
|
+
items = [
|
|
1107
|
+
{ label: 'Create Account', icon: 'add', action: 'create-account' },
|
|
1108
|
+
{ label: 'Start Approval', icon: 'workflow-tasks', action: 'start-approval' },
|
|
1109
|
+
{ label: 'Add User to Account', icon: 'person-placeholder', action: 'add-user' },
|
|
1110
|
+
];
|
|
1111
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: Favorites, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1112
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: Favorites, isStandalone: true, selector: "mfp-favorites", ngImport: i0, template: "<div class=\"favorites\">\n <ui5-title level=\"H5\" class=\"favorites__title\">Favorites</ui5-title>\n <div class=\"favorites__list\">\n @for (item of items; track item.action) {\n <div class=\"favorites__item\">\n <ui5-icon name=\"{{ item.icon }}\" class=\"favorites__icon\"></ui5-icon>\n <span class=\"favorites__label\">{{ item.label }}</span>\n <ui5-button design=\"Transparent\" icon=\"{{ item.icon }}\">{{ item.label }}</ui5-button>\n </div>\n }\n </div>\n</div>\n", styles: [":host{display:block;height:100%}.favorites{display:flex;flex-direction:column;height:100%;background:var(--sapTile_Background, #fff);border-radius:var(--_ui5_card_border-radius, .5rem);border:var(--_ui5_card_border, 1px solid var(--sapTile_BorderColor, #e5e5e5));box-shadow:var(--_ui5_card_box_shadow, var(--sapContent_Shadow0));overflow:hidden}.favorites__title{padding:1rem 1rem .5rem;flex-shrink:0}.favorites__list{display:flex;flex-direction:column;flex:1;overflow-y:auto}.favorites__item{display:flex;align-items:center;gap:.75rem;padding:.5rem 1rem;border-bottom:1px solid var(--sapList_BorderColor, #e5e5e5)}.favorites__item:last-child{border-bottom:none}.favorites__icon{flex-shrink:0;color:var(--sapHighlightColor, #0070f2)}.favorites__label{flex:1;font-size:var(--sapFontSize, .875rem);color:var(--sapTextColor, #32363a)}\n"], dependencies: [{ kind: "component", type: Button, selector: "ui5-button, [ui5-button]", inputs: ["design", "disabled", "icon", "endIcon", "submits", "tooltip", "accessibleName", "accessibleNameRef", "accessibilityAttributes", "accessibleDescription", "type", "accessibleRole", "loading", "loadingDelay"], outputs: ["ui5Click"], exportAs: ["ui5Button"] }, { kind: "component", type: Icon, selector: "ui5-icon, [ui5-icon]", inputs: ["design", "name", "accessibleName", "showTooltip", "mode"], outputs: ["ui5Click"], exportAs: ["ui5Icon"] }, { kind: "component", type: Title, selector: "ui5-title, [ui5-title]", inputs: ["wrappingType", "level", "size"], exportAs: ["ui5Title"] }], encapsulation: i0.ViewEncapsulation.ShadowDom });
|
|
1113
|
+
}
|
|
1114
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: Favorites, decorators: [{
|
|
1115
|
+
type: Component,
|
|
1116
|
+
args: [{ selector: 'mfp-favorites', encapsulation: ViewEncapsulation.ShadowDom, imports: [Button, Icon, Title], template: "<div class=\"favorites\">\n <ui5-title level=\"H5\" class=\"favorites__title\">Favorites</ui5-title>\n <div class=\"favorites__list\">\n @for (item of items; track item.action) {\n <div class=\"favorites__item\">\n <ui5-icon name=\"{{ item.icon }}\" class=\"favorites__icon\"></ui5-icon>\n <span class=\"favorites__label\">{{ item.label }}</span>\n <ui5-button design=\"Transparent\" icon=\"{{ item.icon }}\">{{ item.label }}</ui5-button>\n </div>\n }\n </div>\n</div>\n", styles: [":host{display:block;height:100%}.favorites{display:flex;flex-direction:column;height:100%;background:var(--sapTile_Background, #fff);border-radius:var(--_ui5_card_border-radius, .5rem);border:var(--_ui5_card_border, 1px solid var(--sapTile_BorderColor, #e5e5e5));box-shadow:var(--_ui5_card_box_shadow, var(--sapContent_Shadow0));overflow:hidden}.favorites__title{padding:1rem 1rem .5rem;flex-shrink:0}.favorites__list{display:flex;flex-direction:column;flex:1;overflow-y:auto}.favorites__item{display:flex;align-items:center;gap:.75rem;padding:.5rem 1rem;border-bottom:1px solid var(--sapList_BorderColor, #e5e5e5)}.favorites__item:last-child{border-bottom:none}.favorites__icon{flex-shrink:0;color:var(--sapHighlightColor, #0070f2)}.favorites__label{flex:1;font-size:var(--sapFontSize, .875rem);color:var(--sapTextColor, #32363a)}\n"] }]
|
|
1117
|
+
}] });
|
|
1118
|
+
|
|
1119
|
+
class ServiceStatusCard {
|
|
1120
|
+
services = [
|
|
1121
|
+
{ name: 'API Gateway', icon: 'connected', status: 'operational' },
|
|
1122
|
+
{ name: 'Identity Service', icon: 'customer', status: 'operational' },
|
|
1123
|
+
{ name: 'Database Cluster', icon: 'database', status: 'degraded' },
|
|
1124
|
+
{ name: 'Message Queue', icon: 'email', status: 'operational' },
|
|
1125
|
+
{ name: 'Object Storage', icon: 'storage', status: 'maintenance' },
|
|
1126
|
+
{ name: 'Audit Log', icon: 'log', status: 'outage' },
|
|
1127
|
+
];
|
|
1128
|
+
statusConfig = {
|
|
1129
|
+
operational: { label: 'Operational', icon: 'status-positive', colorClass: 'status--operational' },
|
|
1130
|
+
degraded: { label: 'Degraded', icon: 'status-critical', colorClass: 'status--degraded' },
|
|
1131
|
+
outage: { label: 'Outage', icon: 'status-negative', colorClass: 'status--outage' },
|
|
1132
|
+
maintenance: { label: 'Maintenance', icon: 'status-inactive', colorClass: 'status--maintenance' },
|
|
1133
|
+
};
|
|
1134
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: ServiceStatusCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1135
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: ServiceStatusCard, isStandalone: true, selector: "mfp-service-status-card", ngImport: i0, template: "<div class=\"service-status\">\n <ui5-title level=\"H5\" class=\"service-status__title\">Service Availability</ui5-title>\n <div class=\"service-status__list\">\n @for (service of services; track service.name) {\n <div class=\"service-status__item\">\n <ui5-icon name=\"{{ service.icon }}\" class=\"service-status__icon\"></ui5-icon>\n <span class=\"service-status__name\">{{ service.name }}</span>\n <div class=\"service-status__status {{ statusConfig[service.status].colorClass }}\">\n <ui5-icon name=\"{{ statusConfig[service.status].icon }}\" class=\"service-status__status-icon\"></ui5-icon>\n <span class=\"service-status__status-label\">{{ statusConfig[service.status].label }}</span>\n </div>\n </div>\n }\n </div>\n</div>\n", styles: [":host{display:block;height:100%}.service-status{display:flex;flex-direction:column;height:100%;background:var(--sapTile_Background, #fff);border-radius:var(--_ui5_card_border-radius, .5rem);border:var(--_ui5_card_border, 1px solid var(--sapTile_BorderColor, #e5e5e5));box-shadow:var(--_ui5_card_box_shadow, var(--sapContent_Shadow0));overflow:hidden}.service-status__title{padding:1rem 1rem .5rem;flex-shrink:0}.service-status__list{display:flex;flex-direction:column;flex:1;overflow-y:auto}.service-status__item{display:flex;align-items:center;gap:.75rem;padding:.5rem 1rem;border-bottom:1px solid var(--sapList_BorderColor, #e5e5e5)}.service-status__item:last-child{border-bottom:none}.service-status__icon{flex-shrink:0;color:var(--sapHighlightColor, #0070f2)}.service-status__name{flex:1;font-size:var(--sapFontSize, .875rem);color:var(--sapTextColor, #32363a)}.service-status__status{display:flex;align-items:center;gap:.25rem;flex-shrink:0;font-size:.75rem;font-weight:500;border-radius:.75rem;padding:.125rem .5rem}.service-status__status--operational{color:var(--sapPositiveColor, #256f3a);background:var(--sapPositiveBackground, #f1fdf6)}.service-status__status--degraded{color:var(--sapCriticalColor, #e76500);background:var(--sapCriticalBackground, #fef7f1)}.service-status__status--outage{color:var(--sapNegativeColor, #aa0808);background:var(--sapNegativeBackground, #fff1f1)}.service-status__status--maintenance{color:var(--sapNeutralColor, #5b738b);background:var(--sapNeutralBackground, #f5f6f7)}.service-status__status-icon{font-size:.75rem;width:.875rem;height:.875rem}\n"], dependencies: [{ kind: "component", type: Icon, selector: "ui5-icon, [ui5-icon]", inputs: ["design", "name", "accessibleName", "showTooltip", "mode"], outputs: ["ui5Click"], exportAs: ["ui5Icon"] }, { kind: "component", type: Title, selector: "ui5-title, [ui5-title]", inputs: ["wrappingType", "level", "size"], exportAs: ["ui5Title"] }], encapsulation: i0.ViewEncapsulation.ShadowDom });
|
|
1136
|
+
}
|
|
1137
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: ServiceStatusCard, decorators: [{
|
|
1138
|
+
type: Component,
|
|
1139
|
+
args: [{ selector: 'mfp-service-status-card', encapsulation: ViewEncapsulation.ShadowDom, imports: [Icon, Title], template: "<div class=\"service-status\">\n <ui5-title level=\"H5\" class=\"service-status__title\">Service Availability</ui5-title>\n <div class=\"service-status__list\">\n @for (service of services; track service.name) {\n <div class=\"service-status__item\">\n <ui5-icon name=\"{{ service.icon }}\" class=\"service-status__icon\"></ui5-icon>\n <span class=\"service-status__name\">{{ service.name }}</span>\n <div class=\"service-status__status {{ statusConfig[service.status].colorClass }}\">\n <ui5-icon name=\"{{ statusConfig[service.status].icon }}\" class=\"service-status__status-icon\"></ui5-icon>\n <span class=\"service-status__status-label\">{{ statusConfig[service.status].label }}</span>\n </div>\n </div>\n }\n </div>\n</div>\n", styles: [":host{display:block;height:100%}.service-status{display:flex;flex-direction:column;height:100%;background:var(--sapTile_Background, #fff);border-radius:var(--_ui5_card_border-radius, .5rem);border:var(--_ui5_card_border, 1px solid var(--sapTile_BorderColor, #e5e5e5));box-shadow:var(--_ui5_card_box_shadow, var(--sapContent_Shadow0));overflow:hidden}.service-status__title{padding:1rem 1rem .5rem;flex-shrink:0}.service-status__list{display:flex;flex-direction:column;flex:1;overflow-y:auto}.service-status__item{display:flex;align-items:center;gap:.75rem;padding:.5rem 1rem;border-bottom:1px solid var(--sapList_BorderColor, #e5e5e5)}.service-status__item:last-child{border-bottom:none}.service-status__icon{flex-shrink:0;color:var(--sapHighlightColor, #0070f2)}.service-status__name{flex:1;font-size:var(--sapFontSize, .875rem);color:var(--sapTextColor, #32363a)}.service-status__status{display:flex;align-items:center;gap:.25rem;flex-shrink:0;font-size:.75rem;font-weight:500;border-radius:.75rem;padding:.125rem .5rem}.service-status__status--operational{color:var(--sapPositiveColor, #256f3a);background:var(--sapPositiveBackground, #f1fdf6)}.service-status__status--degraded{color:var(--sapCriticalColor, #e76500);background:var(--sapCriticalBackground, #fef7f1)}.service-status__status--outage{color:var(--sapNegativeColor, #aa0808);background:var(--sapNegativeBackground, #fff1f1)}.service-status__status--maintenance{color:var(--sapNeutralColor, #5b738b);background:var(--sapNeutralBackground, #f5f6f7)}.service-status__status-icon{font-size:.75rem;width:.875rem;height:.875rem}\n"] }]
|
|
1140
|
+
}] });
|
|
1141
|
+
|
|
1142
|
+
class WhatsNew {
|
|
1143
|
+
headlines = [
|
|
1144
|
+
{
|
|
1145
|
+
title: 'Kubernetes 1.32 Released',
|
|
1146
|
+
description: 'Improved scheduling, new APIs and graduated features.',
|
|
1147
|
+
icon: 'cloud',
|
|
1148
|
+
},
|
|
1149
|
+
{
|
|
1150
|
+
title: 'Angular 20 Signals Stable',
|
|
1151
|
+
description: 'Signal-based reactivity is now production-ready.',
|
|
1152
|
+
icon: 'developer-settings',
|
|
1153
|
+
},
|
|
1154
|
+
{
|
|
1155
|
+
title: 'WebAssembly WASI 2.0 Preview',
|
|
1156
|
+
description: 'System interface advances bring server-side WASM closer.',
|
|
1157
|
+
icon: 'technical-object',
|
|
1158
|
+
},
|
|
1159
|
+
{
|
|
1160
|
+
title: 'OpenTelemetry Hits 1.0',
|
|
1161
|
+
description: 'Unified observability standard lands in enterprise stacks.',
|
|
1162
|
+
icon: 'monitor-payments',
|
|
1163
|
+
},
|
|
1164
|
+
{
|
|
1165
|
+
title: 'Rust Enters Linux Kernel Mainstream',
|
|
1166
|
+
description: 'More subsystems accept Rust driver contributions.',
|
|
1167
|
+
icon: 'settings',
|
|
1168
|
+
},
|
|
1169
|
+
{
|
|
1170
|
+
title: 'TypeScript 5.8 Performance Boost',
|
|
1171
|
+
description: 'Declaration emit is up to 10x faster in large projects.',
|
|
1172
|
+
icon: 'accelerated',
|
|
1173
|
+
},
|
|
1174
|
+
];
|
|
1175
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: WhatsNew, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1176
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: WhatsNew, isStandalone: true, selector: "mfp-whats-new", ngImport: i0, template: "<div class=\"whats-new\">\n <ui5-title level=\"H5\" class=\"whats-new__title\">What's New</ui5-title>\n <ui5-list separators=\"Inner\" class=\"whats-new__list\">\n @for (item of headlines; track item.title) {\n <ui5-li icon=\"{{ item.icon }}\" description=\"{{ item.description }}\">\n {{ item.title }}\n </ui5-li>\n }\n </ui5-list>\n</div>\n", styles: [":host{display:block;height:100%}.whats-new{display:flex;flex-direction:column;height:100%;background:var(--sapTile_Background, #fff);border-radius:var(--_ui5_card_border-radius, .5rem);border:var(--_ui5_card_border, 1px solid var(--sapTile_BorderColor, #e5e5e5));box-shadow:var(--_ui5_card_box_shadow, var(--sapContent_Shadow0));overflow:hidden}.whats-new__title{padding:1rem 1rem .5rem;flex-shrink:0}.whats-new__list{flex:1;overflow-y:auto}\n"], dependencies: [{ kind: "component", type: List, selector: "ui5-list, [ui5-list]", inputs: ["headerText", "footerText", "indent", "selectionMode", "noDataText", "separators", "growing", "growingButtonText", "loading", "loadingDelay", "stickyHeader", "accessibleName", "accessibilityAttributes", "accessibleNameRef", "accessibleDescription", "accessibleDescriptionRef", "accessibleRole"], outputs: ["ui5ItemClick", "ui5ItemClose", "ui5ItemToggle", "ui5ItemDelete", "ui5SelectionChange", "ui5LoadMore", "ui5MoveOver", "ui5Move"], exportAs: ["ui5List"] }, { kind: "component", type: ListItemStandard, selector: "ui5-li, [ui5-li]", inputs: ["text", "description", "icon", "iconEnd", "additionalText", "additionalTextState", "movable", "accessibleName", "wrappingType", "type", "accessibilityAttributes", "navigated", "tooltip", "highlight", "selected"], outputs: ["ui5DetailClick"], exportAs: ["ui5ListItemStandard"] }, { kind: "component", type: Title, selector: "ui5-title, [ui5-title]", inputs: ["wrappingType", "level", "size"], exportAs: ["ui5Title"] }], encapsulation: i0.ViewEncapsulation.ShadowDom });
|
|
1177
|
+
}
|
|
1178
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: WhatsNew, decorators: [{
|
|
1179
|
+
type: Component,
|
|
1180
|
+
args: [{ selector: 'mfp-whats-new', encapsulation: ViewEncapsulation.ShadowDom, imports: [List, ListItemStandard, Title], template: "<div class=\"whats-new\">\n <ui5-title level=\"H5\" class=\"whats-new__title\">What's New</ui5-title>\n <ui5-list separators=\"Inner\" class=\"whats-new__list\">\n @for (item of headlines; track item.title) {\n <ui5-li icon=\"{{ item.icon }}\" description=\"{{ item.description }}\">\n {{ item.title }}\n </ui5-li>\n }\n </ui5-list>\n</div>\n", styles: [":host{display:block;height:100%}.whats-new{display:flex;flex-direction:column;height:100%;background:var(--sapTile_Background, #fff);border-radius:var(--_ui5_card_border-radius, .5rem);border:var(--_ui5_card_border, 1px solid var(--sapTile_BorderColor, #e5e5e5));box-shadow:var(--_ui5_card_box_shadow, var(--sapContent_Shadow0));overflow:hidden}.whats-new__title{padding:1rem 1rem .5rem;flex-shrink:0}.whats-new__list{flex:1;overflow-y:auto}\n"] }]
|
|
1181
|
+
}] });
|
|
1182
|
+
|
|
1183
|
+
/**
|
|
1184
|
+
* Generated bundle index. Do not edit.
|
|
1185
|
+
*/
|
|
1186
|
+
|
|
1187
|
+
export { AddCardDialog, CARD_TYPES, Dashboard, DashboardCard, DashboardSection, DeclarativeForm, DeclarativeTable, DeclarativeTableCard, Favorites, ServiceStatusCard, VisitedServiceCard, WhatsNew };
|
|
1188
|
+
//# sourceMappingURL=openmfp-ngx.mjs.map
|