@openmfp/ngx 0.14.11 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/openmfp-ngx.mjs +303 -77
- package/fesm2022/openmfp-ngx.mjs.map +1 -1
- package/package.json +1 -1
- package/types/openmfp-ngx.d.ts +32 -5
package/fesm2022/openmfp-ngx.mjs
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { JSONPath } from 'jsonpath-plus';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
|
-
import { input, computed, CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, Component,
|
|
3
|
+
import { input, computed, CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, Component, signal, ViewEncapsulation, forwardRef, output, linkedSignal, inject, effect, viewChild, Injector, afterNextRender, Injectable, reflectComponentType, isStandalone, isDevMode, ViewContainerRef, Renderer2, ElementRef, model, SecurityContext } from '@angular/core';
|
|
4
4
|
import { Icon } from '@fundamental-ngx/ui5-webcomponents/icon';
|
|
5
5
|
import { Link } from '@fundamental-ngx/ui5-webcomponents/link';
|
|
6
|
+
import '@ui5/webcomponents-icons/dist/navigation-down-arrow.js';
|
|
7
|
+
import '@ui5/webcomponents-icons/dist/navigation-right-arrow.js';
|
|
6
8
|
import { Tag } from '@fundamental-ngx/ui5-webcomponents/tag';
|
|
7
9
|
import { Button } from '@fundamental-ngx/ui5-webcomponents/button';
|
|
8
10
|
import '@ui5/webcomponents-icons/dist/AllIcons.js';
|
|
@@ -16,16 +18,16 @@ import { TableHeaderCell } from '@fundamental-ngx/ui5-webcomponents/table-header
|
|
|
16
18
|
import { TableHeaderRow } from '@fundamental-ngx/ui5-webcomponents/table-header-row';
|
|
17
19
|
import { TableRow } from '@fundamental-ngx/ui5-webcomponents/table-row';
|
|
18
20
|
import '@ui5/webcomponents-fiori/dist/illustrations/NoData.js';
|
|
21
|
+
import '@ui5/webcomponents-icons/dist/add.js';
|
|
22
|
+
import '@ui5/webcomponents-icons/dist/delete.js';
|
|
19
23
|
import * as i1 from '@angular/forms';
|
|
20
|
-
import { FormBuilder, FormControl, ReactiveFormsModule } from '@angular/forms';
|
|
24
|
+
import { FormBuilder, FormArray, FormControl, ReactiveFormsModule } from '@angular/forms';
|
|
21
25
|
import { Input } from '@fundamental-ngx/ui5-webcomponents/input';
|
|
22
26
|
import { Label } from '@fundamental-ngx/ui5-webcomponents/label';
|
|
23
27
|
import '@ui5/webcomponents-icons/dist/navigation-left-arrow.js';
|
|
24
|
-
import '@ui5/webcomponents-icons/dist/navigation-right-arrow.js';
|
|
25
28
|
import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';
|
|
26
29
|
import { Dialog } from '@fundamental-ngx/ui5-webcomponents/dialog';
|
|
27
30
|
import { Title } from '@fundamental-ngx/ui5-webcomponents/title';
|
|
28
|
-
import '@ui5/webcomponents-icons/dist/add.js';
|
|
29
31
|
import '@ui5/webcomponents-icons/dist/search.js';
|
|
30
32
|
import { debounceTime, map, filter } from 'rxjs';
|
|
31
33
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
@@ -45,68 +47,6 @@ import { Card } from '@fundamental-ngx/ui5-webcomponents/card';
|
|
|
45
47
|
import { CardHeader } from '@fundamental-ngx/ui5-webcomponents/card-header';
|
|
46
48
|
import { ListItemStandard } from '@fundamental-ngx/ui5-webcomponents/list-item-standard';
|
|
47
49
|
|
|
48
|
-
const parseStringValue = (value) => {
|
|
49
|
-
if (value === 'true') {
|
|
50
|
-
return true;
|
|
51
|
-
}
|
|
52
|
-
if (value === 'false') {
|
|
53
|
-
return false;
|
|
54
|
-
}
|
|
55
|
-
if (!isNaN(Number(value))) {
|
|
56
|
-
return Number(value);
|
|
57
|
-
}
|
|
58
|
-
return value;
|
|
59
|
-
};
|
|
60
|
-
const ruleResolver = (rule, resourceValue) => {
|
|
61
|
-
const parsedResouceValue = parseStringValue(resourceValue);
|
|
62
|
-
const parsedConditionValue = parseStringValue(rule.if.value);
|
|
63
|
-
switch (rule.if.condition) {
|
|
64
|
-
case 'equals':
|
|
65
|
-
return parsedResouceValue === parsedConditionValue;
|
|
66
|
-
case 'notEquals':
|
|
67
|
-
return parsedResouceValue !== parsedConditionValue;
|
|
68
|
-
case 'greaterThan':
|
|
69
|
-
return parsedResouceValue > parsedConditionValue;
|
|
70
|
-
case 'greaterThanOrEqual':
|
|
71
|
-
return parsedResouceValue >= parsedConditionValue;
|
|
72
|
-
case 'lessThan':
|
|
73
|
-
return parsedResouceValue < parsedConditionValue;
|
|
74
|
-
case 'lessThanOrEqual':
|
|
75
|
-
return parsedResouceValue <= parsedConditionValue;
|
|
76
|
-
case 'contains':
|
|
77
|
-
if (Array.isArray(parsedResouceValue)) {
|
|
78
|
-
return parsedResouceValue.includes(parsedConditionValue);
|
|
79
|
-
}
|
|
80
|
-
else if (typeof parsedResouceValue === 'string' &&
|
|
81
|
-
typeof parsedConditionValue === 'string') {
|
|
82
|
-
return parsedResouceValue.includes(parsedConditionValue);
|
|
83
|
-
}
|
|
84
|
-
return false;
|
|
85
|
-
}
|
|
86
|
-
return false;
|
|
87
|
-
};
|
|
88
|
-
const evaluateCssRules = (value, rules) => {
|
|
89
|
-
if (!rules) {
|
|
90
|
-
return {};
|
|
91
|
-
}
|
|
92
|
-
return rules.reduce((acc, rule) => {
|
|
93
|
-
const result = ruleResolver(rule, value);
|
|
94
|
-
if (result) {
|
|
95
|
-
return { ...acc, ...rule.styles };
|
|
96
|
-
}
|
|
97
|
-
return acc;
|
|
98
|
-
}, {});
|
|
99
|
-
};
|
|
100
|
-
const evaluateValueRules = (value, rules) => {
|
|
101
|
-
if (!rules)
|
|
102
|
-
return value;
|
|
103
|
-
for (const rule of rules) {
|
|
104
|
-
if (ruleResolver(rule, value))
|
|
105
|
-
return rule.then;
|
|
106
|
-
}
|
|
107
|
-
return value;
|
|
108
|
-
};
|
|
109
|
-
|
|
110
50
|
const getResourceValueByJsonPath = (resource, field) => {
|
|
111
51
|
const property = field.jsonPathExpression || field.property;
|
|
112
52
|
if (!property) {
|
|
@@ -201,6 +141,68 @@ function getFieldValue(field, resource) {
|
|
|
201
141
|
return field.value;
|
|
202
142
|
}
|
|
203
143
|
|
|
144
|
+
const parseStringValue = (value) => {
|
|
145
|
+
if (value === 'true') {
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
if (value === 'false') {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
if (!isNaN(Number(value))) {
|
|
152
|
+
return Number(value);
|
|
153
|
+
}
|
|
154
|
+
return value;
|
|
155
|
+
};
|
|
156
|
+
const ruleResolver = (rule, resourceValue) => {
|
|
157
|
+
const parsedResouceValue = parseStringValue(resourceValue);
|
|
158
|
+
const parsedConditionValue = parseStringValue(rule.if.value);
|
|
159
|
+
switch (rule.if.condition) {
|
|
160
|
+
case 'equals':
|
|
161
|
+
return parsedResouceValue === parsedConditionValue;
|
|
162
|
+
case 'notEquals':
|
|
163
|
+
return parsedResouceValue !== parsedConditionValue;
|
|
164
|
+
case 'greaterThan':
|
|
165
|
+
return parsedResouceValue > parsedConditionValue;
|
|
166
|
+
case 'greaterThanOrEqual':
|
|
167
|
+
return parsedResouceValue >= parsedConditionValue;
|
|
168
|
+
case 'lessThan':
|
|
169
|
+
return parsedResouceValue < parsedConditionValue;
|
|
170
|
+
case 'lessThanOrEqual':
|
|
171
|
+
return parsedResouceValue <= parsedConditionValue;
|
|
172
|
+
case 'contains':
|
|
173
|
+
if (Array.isArray(parsedResouceValue)) {
|
|
174
|
+
return parsedResouceValue.includes(parsedConditionValue);
|
|
175
|
+
}
|
|
176
|
+
else if (typeof parsedResouceValue === 'string' &&
|
|
177
|
+
typeof parsedConditionValue === 'string') {
|
|
178
|
+
return parsedResouceValue.includes(parsedConditionValue);
|
|
179
|
+
}
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
return false;
|
|
183
|
+
};
|
|
184
|
+
const evaluateCssRules = (value, rules) => {
|
|
185
|
+
if (!rules) {
|
|
186
|
+
return {};
|
|
187
|
+
}
|
|
188
|
+
return rules.reduce((acc, rule) => {
|
|
189
|
+
const result = ruleResolver(rule, value);
|
|
190
|
+
if (result) {
|
|
191
|
+
return { ...acc, ...rule.styles };
|
|
192
|
+
}
|
|
193
|
+
return acc;
|
|
194
|
+
}, {});
|
|
195
|
+
};
|
|
196
|
+
const evaluateValueRules = (value, rules) => {
|
|
197
|
+
if (!rules)
|
|
198
|
+
return value;
|
|
199
|
+
for (const rule of rules) {
|
|
200
|
+
if (ruleResolver(rule, value))
|
|
201
|
+
return rule.then;
|
|
202
|
+
}
|
|
203
|
+
return value;
|
|
204
|
+
};
|
|
205
|
+
|
|
204
206
|
const ICON_DESIGN_POSITIVE = 'Positive';
|
|
205
207
|
const ICON_DESIGN_NEGATIVE = 'Negative';
|
|
206
208
|
const ICON_NAME_POSITIVE = 'accept';
|
|
@@ -237,6 +239,77 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImpo
|
|
|
237
239
|
args: [{ selector: 'mfp-link-value', imports: [Link], changeDetection: ChangeDetectionStrategy.OnPush, schemas: [CUSTOM_ELEMENTS_SCHEMA], template: "<ui5-link\n target=\"_blank\"\n [attr.data-testid]=\"testId()\"\n [href]=\"urlValue()\"\n [tooltip]=\"urlValue()\"\n (click)=\"stopPropagation($event)\"\n>Link</ui5-link>\n" }]
|
|
238
240
|
}], propDecorators: { urlValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "urlValue", required: true }] }], testId: [{ type: i0.Input, args: [{ isSignal: true, alias: "testId", required: false }] }] } });
|
|
239
241
|
|
|
242
|
+
/**
|
|
243
|
+
* Read-only display for a `propertyCollection` field. Renders the resolved
|
|
244
|
+
* array as a column of collapsed cards; expanding a card lists each sub-field
|
|
245
|
+
* as a `label: value` row rendered through `mfp-resource-field`, so per-
|
|
246
|
+
* sub-field `uiSettings` still apply. Mirrors the styling of the editable
|
|
247
|
+
* `mfp-form-collection-field`.
|
|
248
|
+
*/
|
|
249
|
+
class ResourceCollectionField {
|
|
250
|
+
fieldDefinition = input.required(...(ngDevMode ? [{ debugName: "fieldDefinition" }] : /* istanbul ignore next */ []));
|
|
251
|
+
resource = input(...(ngDevMode ? [undefined, { debugName: "resource" }] : /* istanbul ignore next */ []));
|
|
252
|
+
expandedIndex = signal(null, ...(ngDevMode ? [{ debugName: "expandedIndex" }] : /* istanbul ignore next */ []));
|
|
253
|
+
collectionPath = computed(() => {
|
|
254
|
+
const property = this.fieldDefinition().property;
|
|
255
|
+
return typeof property === 'string' ? property : undefined;
|
|
256
|
+
}, ...(ngDevMode ? [{ debugName: "collectionPath" }] : /* istanbul ignore next */ []));
|
|
257
|
+
entries = computed(() => {
|
|
258
|
+
const value = getResourceValueByJsonPath(this.resource() ?? {}, {
|
|
259
|
+
property: this.collectionPath(),
|
|
260
|
+
});
|
|
261
|
+
return Array.isArray(value) ? value : [];
|
|
262
|
+
}, ...(ngDevMode ? [{ debugName: "entries" }] : /* istanbul ignore next */ []));
|
|
263
|
+
rows = computed(() => {
|
|
264
|
+
const parent = this.collectionPath();
|
|
265
|
+
return (this.fieldDefinition().propertyCollection ?? []).map((sub) => {
|
|
266
|
+
const property = typeof sub.property === 'string'
|
|
267
|
+
? this.stripParentPath(sub.property, parent)
|
|
268
|
+
: sub.property;
|
|
269
|
+
return {
|
|
270
|
+
label: sub.label ?? this.leafOf(property),
|
|
271
|
+
field: { ...sub, property },
|
|
272
|
+
};
|
|
273
|
+
});
|
|
274
|
+
}, ...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
|
|
275
|
+
toggle(index, event) {
|
|
276
|
+
event?.stopPropagation();
|
|
277
|
+
this.expandedIndex.set(this.expandedIndex() === index ? null : index);
|
|
278
|
+
}
|
|
279
|
+
previewFor(entry, index) {
|
|
280
|
+
for (const row of this.rows()) {
|
|
281
|
+
const raw = getResourceValueByJsonPath(entry, {
|
|
282
|
+
property: row.field.property,
|
|
283
|
+
});
|
|
284
|
+
if (raw !== undefined && raw !== null && String(raw).trim() !== '') {
|
|
285
|
+
return `${row.label}: ${String(raw)}`;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
const prefix = (this.fieldDefinition().label ?? '').trim() || 'Item';
|
|
289
|
+
return `${prefix} ${index + 1}`;
|
|
290
|
+
}
|
|
291
|
+
stripParentPath(name, parent) {
|
|
292
|
+
if (!parent)
|
|
293
|
+
return name;
|
|
294
|
+
if (name.startsWith(parent + '.')) {
|
|
295
|
+
return name.slice(parent.length + 1);
|
|
296
|
+
}
|
|
297
|
+
return name;
|
|
298
|
+
}
|
|
299
|
+
leafOf(property) {
|
|
300
|
+
if (typeof property !== 'string')
|
|
301
|
+
return '';
|
|
302
|
+
const segments = property.split('.');
|
|
303
|
+
return segments[segments.length - 1];
|
|
304
|
+
}
|
|
305
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: ResourceCollectionField, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
306
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: ResourceCollectionField, isStandalone: true, selector: "mfp-resource-collection-field", inputs: { fieldDefinition: { classPropertyName: "fieldDefinition", publicName: "fieldDefinition", isSignal: true, isRequired: true, transformFunction: null }, resource: { classPropertyName: "resource", publicName: "resource", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"collection\" data-testid=\"resource-collection-field\">\n @for (entry of entries(); track $index; let index = $index) {\n @let expanded = expandedIndex() === index;\n <div\n class=\"card\"\n [class.card--expanded]=\"expanded\"\n [attr.data-testid]=\"'resource-collection-item-' + index\"\n >\n <button\n type=\"button\"\n class=\"card__header\"\n [attr.aria-expanded]=\"expanded\"\n [attr.data-testid]=\"'resource-collection-item-' + index + '-toggle'\"\n (click)=\"toggle(index, $event)\"\n >\n <ui5-icon\n class=\"card__chevron\"\n [name]=\"expanded ? 'navigation-down-arrow' : 'navigation-right-arrow'\"\n />\n <span class=\"card__preview\">{{ previewFor(entry, index) }}</span>\n </button>\n\n @if (expanded) {\n <div class=\"card__body\" (click)=\"$event.stopPropagation()\">\n @for (row of rows(); track row.field.property) {\n <div class=\"row\">\n <span class=\"row__label\">{{ row.label }}:</span>\n <mfp-resource-field\n [fieldDefinition]=\"row.field\"\n [resource]=\"entry\"\n />\n </div>\n }\n </div>\n }\n </div>\n }\n</div>\n", styles: [":host{display:block;width:100%}.collection{display:flex;flex-direction:column;gap:.5rem;width:100%;padding-top:.25rem}.card{border:1px solid var(--sapGroup_ContentBorderColor, #d9d9d9);border-radius:.5rem;background:var(--sapGroup_TitleBackground, #fff);overflow:hidden}.card--expanded{box-shadow:0 1px 2px #0000000a}.card__header{display:flex;align-items:center;gap:.5rem;width:100%;padding:0 .75rem;background:transparent;border:0;text-align:left;cursor:pointer;font:inherit;min-height:1.5rem;color:var(--sapTextColor, #1d2d3e)}.card__header:hover{background:var(--sapList_Hover_Background, rgba(0, 0, 0, .03))}.card__chevron{flex:0 0 auto;color:var(--sapContent_IconColor, #6a6d70)}.card__preview{font-weight:500;color:var(--sapTextColor, #1d2d3e);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.card__body{display:flex;flex-direction:column;gap:.5rem;padding:.75rem 1rem 1rem;border-top:1px solid var(--sapGroup_ContentBorderColor, #d9d9d9)}.row{display:flex;gap:.25rem;align-items:baseline}.row__label{font-weight:500;color:var(--sapContent_LabelColor, #556b82);white-space:nowrap}\n"], dependencies: [{ kind: "component", type: i0.forwardRef(() => Icon), selector: "ui5-icon, [ui5-icon]", inputs: ["design", "name", "accessibleName", "showTooltip", "mode"], outputs: ["ui5Click"], exportAs: ["ui5Icon"] }, { kind: "component", type: i0.forwardRef(() => ResourceField), selector: "mfp-resource-field", inputs: ["fieldDefinition", "resource"], outputs: ["buttonClick"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.ShadowDom });
|
|
307
|
+
}
|
|
308
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: ResourceCollectionField, decorators: [{
|
|
309
|
+
type: Component,
|
|
310
|
+
args: [{ selector: 'mfp-resource-collection-field', imports: [Icon, forwardRef(() => ResourceField)], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.ShadowDom, schemas: [CUSTOM_ELEMENTS_SCHEMA], template: "<div class=\"collection\" data-testid=\"resource-collection-field\">\n @for (entry of entries(); track $index; let index = $index) {\n @let expanded = expandedIndex() === index;\n <div\n class=\"card\"\n [class.card--expanded]=\"expanded\"\n [attr.data-testid]=\"'resource-collection-item-' + index\"\n >\n <button\n type=\"button\"\n class=\"card__header\"\n [attr.aria-expanded]=\"expanded\"\n [attr.data-testid]=\"'resource-collection-item-' + index + '-toggle'\"\n (click)=\"toggle(index, $event)\"\n >\n <ui5-icon\n class=\"card__chevron\"\n [name]=\"expanded ? 'navigation-down-arrow' : 'navigation-right-arrow'\"\n />\n <span class=\"card__preview\">{{ previewFor(entry, index) }}</span>\n </button>\n\n @if (expanded) {\n <div class=\"card__body\" (click)=\"$event.stopPropagation()\">\n @for (row of rows(); track row.field.property) {\n <div class=\"row\">\n <span class=\"row__label\">{{ row.label }}:</span>\n <mfp-resource-field\n [fieldDefinition]=\"row.field\"\n [resource]=\"entry\"\n />\n </div>\n }\n </div>\n }\n </div>\n }\n</div>\n", styles: [":host{display:block;width:100%}.collection{display:flex;flex-direction:column;gap:.5rem;width:100%;padding-top:.25rem}.card{border:1px solid var(--sapGroup_ContentBorderColor, #d9d9d9);border-radius:.5rem;background:var(--sapGroup_TitleBackground, #fff);overflow:hidden}.card--expanded{box-shadow:0 1px 2px #0000000a}.card__header{display:flex;align-items:center;gap:.5rem;width:100%;padding:0 .75rem;background:transparent;border:0;text-align:left;cursor:pointer;font:inherit;min-height:1.5rem;color:var(--sapTextColor, #1d2d3e)}.card__header:hover{background:var(--sapList_Hover_Background, rgba(0, 0, 0, .03))}.card__chevron{flex:0 0 auto;color:var(--sapContent_IconColor, #6a6d70)}.card__preview{font-weight:500;color:var(--sapTextColor, #1d2d3e);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.card__body{display:flex;flex-direction:column;gap:.5rem;padding:.75rem 1rem 1rem;border-top:1px solid var(--sapGroup_ContentBorderColor, #d9d9d9)}.row{display:flex;gap:.25rem;align-items:baseline}.row__label{font-weight:500;color:var(--sapContent_LabelColor, #556b82);white-space:nowrap}\n"] }]
|
|
311
|
+
}], propDecorators: { fieldDefinition: [{ type: i0.Input, args: [{ isSignal: true, alias: "fieldDefinition", required: true }] }], resource: [{ type: i0.Input, args: [{ isSignal: true, alias: "resource", required: false }] }] } });
|
|
312
|
+
|
|
240
313
|
class SecretValue {
|
|
241
314
|
value = input.required(...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
242
315
|
isVisible = input(false, ...(ngDevMode ? [{ debugName: "isVisible" }] : /* istanbul ignore next */ []));
|
|
@@ -286,6 +359,7 @@ class ResourceField {
|
|
|
286
359
|
tags = computed(() => this.normalizeTagsArray(this.value()), ...(ngDevMode ? [{ debugName: "tags" }] : /* istanbul ignore next */ []));
|
|
287
360
|
isVisible = signal(false, ...(ngDevMode ? [{ debugName: "isVisible" }] : /* istanbul ignore next */ []));
|
|
288
361
|
copySuccess = signal(false, ...(ngDevMode ? [{ debugName: "copySuccess" }] : /* istanbul ignore next */ []));
|
|
362
|
+
isCollection = computed(() => !!this.fieldDefinition().propertyCollection?.length, ...(ngDevMode ? [{ debugName: "isCollection" }] : /* istanbul ignore next */ []));
|
|
289
363
|
toggleVisibility(e) {
|
|
290
364
|
e.stopPropagation();
|
|
291
365
|
this.isVisible.set(!this.isVisible());
|
|
@@ -308,11 +382,14 @@ class ResourceField {
|
|
|
308
382
|
}
|
|
309
383
|
normalizeTagsArray(value) {
|
|
310
384
|
if (Array.isArray(value)) {
|
|
311
|
-
return value.map(v => String(v).trim()).filter(v => v.length > 0);
|
|
385
|
+
return value.map((v) => String(v).trim()).filter((v) => v.length > 0);
|
|
312
386
|
}
|
|
313
387
|
if (typeof value === 'string') {
|
|
314
388
|
const separator = this.uiSettings()?.tagSettings?.valueSeparator ?? ',';
|
|
315
|
-
return value
|
|
389
|
+
return value
|
|
390
|
+
.split(separator)
|
|
391
|
+
.map((v) => v.trim())
|
|
392
|
+
.filter((v) => v.length > 0);
|
|
316
393
|
}
|
|
317
394
|
return [];
|
|
318
395
|
}
|
|
@@ -346,11 +423,25 @@ class ResourceField {
|
|
|
346
423
|
});
|
|
347
424
|
}
|
|
348
425
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: ResourceField, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
349
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: ResourceField, isStandalone: true, selector: "mfp-resource-field", 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.data-testid]=\"testId()\" [style]=\"cssStyles()\">\n
|
|
426
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: ResourceField, isStandalone: true, selector: "mfp-resource-field", 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" }, host: { properties: { "class.resource-field--collection": "isCollection()" } }, ngImport: i0, template: "@if (isCollection()) {\n <mfp-resource-collection-field\n [fieldDefinition]=\"fieldDefinition()\"\n [resource]=\"resource()\"\n />\n} @else {\n @let displayType = displayAs();\n <span [attr.data-testid]=\"testId()\" [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 mode=\"Interactive\"\n [accessibleName]=\"isVisible() ? 'Hide value' : 'Show value'\"\n [attr.data-testid]=\"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.data-testid]=\"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.data-testid]=\"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 [attr.data-testid]=\"testId() + '-button'\"\n [accessibleName]=\"buttonSettings?.text || buttonSettings?.tooltip || buttonSettings?.icon || ''\"\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 if (displayType === 'tag') {\n <mfp-tag-list-value\n [tagSettings]=\"uiSettings()?.tagSettings\"\n [tags]=\"tags()\"\n [testId]=\"testId() + '-tags'\"\n />\n } @else {\n {{ displayValue() }}\n }\n </span>\n\n @if (withCopyButton()) {\n <ui5-icon\n class=\"toggle-icon\"\n mode=\"Interactive\"\n [accessibleName]=\"copySuccess() ? 'Copied' : 'Copy value'\"\n [attr.data-testid]=\"testId() + '-copy'\"\n [design]=\"copySuccess() ? 'Positive' : 'Default'\"\n [name]=\"copySuccess() ? 'accept' : 'copy'\"\n (click)=\"copyValue($event)\"\n />\n }\n}\n", styles: [":host{display:inline-flex;align-items:center}:host(.resource-field--collection){display:block;width:100%}.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: i0.forwardRef(() => Icon), selector: "ui5-icon, [ui5-icon]", inputs: ["design", "name", "accessibleName", "showTooltip", "mode"], outputs: ["ui5Click"], exportAs: ["ui5Icon"] }, { kind: "component", type: i0.forwardRef(() => BooleanValue), selector: "mfp-boolean-value", inputs: ["boolValue", "testId"] }, { kind: "component", type: i0.forwardRef(() => LinkValue), selector: "mfp-link-value", inputs: ["urlValue", "testId"] }, { kind: "component", type: i0.forwardRef(() => SecretValue), selector: "mfp-secret-value", inputs: ["value", "isVisible", "testId"] }, { kind: "component", type: i0.forwardRef(() => 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: i0.forwardRef(() => TagListValue), selector: "mfp-tag-list-value", inputs: ["tags", "tagSettings", "testId"] }, { kind: "component", type: i0.forwardRef(() => ResourceCollectionField), selector: "mfp-resource-collection-field", inputs: ["fieldDefinition", "resource"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.ShadowDom });
|
|
350
427
|
}
|
|
351
428
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: ResourceField, decorators: [{
|
|
352
429
|
type: Component,
|
|
353
|
-
args: [{ selector: 'mfp-resource-field', imports: [
|
|
430
|
+
args: [{ selector: 'mfp-resource-field', imports: [
|
|
431
|
+
Icon,
|
|
432
|
+
BooleanValue,
|
|
433
|
+
LinkValue,
|
|
434
|
+
SecretValue,
|
|
435
|
+
Button,
|
|
436
|
+
TagListValue,
|
|
437
|
+
// `ResourceCollectionField` imports this component back (each expanded
|
|
438
|
+
// card renders its sub-fields via `mfp-resource-field`), so the two form
|
|
439
|
+
// a module-init cycle. `forwardRef` defers resolution until the template
|
|
440
|
+
// is instantiated, by which point both classes are defined.
|
|
441
|
+
forwardRef(() => ResourceCollectionField),
|
|
442
|
+
], host: {
|
|
443
|
+
'[class.resource-field--collection]': 'isCollection()',
|
|
444
|
+
}, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.ShadowDom, schemas: [CUSTOM_ELEMENTS_SCHEMA], template: "@if (isCollection()) {\n <mfp-resource-collection-field\n [fieldDefinition]=\"fieldDefinition()\"\n [resource]=\"resource()\"\n />\n} @else {\n @let displayType = displayAs();\n <span [attr.data-testid]=\"testId()\" [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 mode=\"Interactive\"\n [accessibleName]=\"isVisible() ? 'Hide value' : 'Show value'\"\n [attr.data-testid]=\"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.data-testid]=\"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.data-testid]=\"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 [attr.data-testid]=\"testId() + '-button'\"\n [accessibleName]=\"buttonSettings?.text || buttonSettings?.tooltip || buttonSettings?.icon || ''\"\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 if (displayType === 'tag') {\n <mfp-tag-list-value\n [tagSettings]=\"uiSettings()?.tagSettings\"\n [tags]=\"tags()\"\n [testId]=\"testId() + '-tags'\"\n />\n } @else {\n {{ displayValue() }}\n }\n </span>\n\n @if (withCopyButton()) {\n <ui5-icon\n class=\"toggle-icon\"\n mode=\"Interactive\"\n [accessibleName]=\"copySuccess() ? 'Copied' : 'Copy value'\"\n [attr.data-testid]=\"testId() + '-copy'\"\n [design]=\"copySuccess() ? 'Positive' : 'Default'\"\n [name]=\"copySuccess() ? 'accept' : 'copy'\"\n (click)=\"copyValue($event)\"\n />\n }\n}\n", styles: [":host{display:inline-flex;align-items:center}:host(.resource-field--collection){display:block;width:100%}.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"] }]
|
|
354
445
|
}], 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"] }] } });
|
|
355
446
|
|
|
356
447
|
const processGroupFields = (fields) => {
|
|
@@ -455,13 +546,80 @@ function setPropertyByPath(object, path, value) {
|
|
|
455
546
|
return object;
|
|
456
547
|
}
|
|
457
548
|
|
|
549
|
+
/**
|
|
550
|
+
* Internal collection editor rendered by `mfp-declarative-form` when a field
|
|
551
|
+
* carries `propertyCollection` sub-fields.
|
|
552
|
+
*
|
|
553
|
+
* Each entry renders as a collapsible card whose expanded body is a nested
|
|
554
|
+
* `<mfp-declarative-form>` bound to that entry. Live changes flow up
|
|
555
|
+
* through the nested form's `(formValueChange)` output — no `viewChildren`
|
|
556
|
+
* on the class, so this component can safely participate in the mutual
|
|
557
|
+
* import cycle with `DeclarativeForm`.
|
|
558
|
+
*/
|
|
559
|
+
class FormCollectionField {
|
|
560
|
+
fields = input.required(...(ngDevMode ? [{ debugName: "fields" }] : /* istanbul ignore next */ []));
|
|
561
|
+
label = input.required(...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
562
|
+
initialEntries = input([], ...(ngDevMode ? [{ debugName: "initialEntries" }] : /* istanbul ignore next */ []));
|
|
563
|
+
valueChange = output();
|
|
564
|
+
entries = linkedSignal(() => [
|
|
565
|
+
...(this.initialEntries() ?? []),
|
|
566
|
+
], ...(ngDevMode ? [{ debugName: "entries" }] : /* istanbul ignore next */ []));
|
|
567
|
+
expandedIndex = linkedSignal({ ...(ngDevMode ? { debugName: "expandedIndex" } : /* istanbul ignore next */ {}), source: this.initialEntries,
|
|
568
|
+
computation: () => null });
|
|
569
|
+
previewFor(entry, index) {
|
|
570
|
+
for (const field of this.fields()) {
|
|
571
|
+
const raw = entry[field.name];
|
|
572
|
+
if (raw !== undefined && raw !== null && String(raw).trim() !== '') {
|
|
573
|
+
return `${field.label ?? field.name}: ${String(raw)}`;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
const prefix = this.label().trim() || 'Item';
|
|
577
|
+
return `${prefix} ${index + 1}`;
|
|
578
|
+
}
|
|
579
|
+
toggle(index) {
|
|
580
|
+
this.expandedIndex.set(this.expandedIndex() === index ? null : index);
|
|
581
|
+
}
|
|
582
|
+
add() {
|
|
583
|
+
const next = [...this.entries(), {}];
|
|
584
|
+
this.entries.set(next);
|
|
585
|
+
this.expandedIndex.set(next.length - 1);
|
|
586
|
+
this.valueChange.emit(next);
|
|
587
|
+
}
|
|
588
|
+
remove(index) {
|
|
589
|
+
const next = this.entries().filter((_, i) => i !== index);
|
|
590
|
+
this.entries.set(next);
|
|
591
|
+
const expandedIndex = this.expandedIndex();
|
|
592
|
+
if (expandedIndex === index) {
|
|
593
|
+
this.expandedIndex.set(null);
|
|
594
|
+
}
|
|
595
|
+
else if (expandedIndex !== null && expandedIndex > index) {
|
|
596
|
+
// Preserve the same expanded row after removing an earlier one.
|
|
597
|
+
this.expandedIndex.set(expandedIndex - 1);
|
|
598
|
+
}
|
|
599
|
+
this.valueChange.emit(next);
|
|
600
|
+
}
|
|
601
|
+
onEntryValueChange(index, value) {
|
|
602
|
+
const next = this.entries().map((entry, i) => i === index ? { ...value } : entry);
|
|
603
|
+
this.entries.set(next);
|
|
604
|
+
this.valueChange.emit(next);
|
|
605
|
+
}
|
|
606
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: FormCollectionField, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
607
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: FormCollectionField, isStandalone: true, selector: "mfp-form-collection-field", inputs: { fields: { classPropertyName: "fields", publicName: "fields", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, initialEntries: { classPropertyName: "initialEntries", publicName: "initialEntries", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<div class=\"collection\" data-testid=\"collection-field\">\n @for (entry of entries(); track $index; let index = $index) {\n @let expanded = expandedIndex() === index;\n <div\n class=\"card\"\n [class.card--expanded]=\"expanded\"\n [attr.data-testid]=\"'collection-item-' + index\"\n >\n <button\n type=\"button\"\n class=\"card__header\"\n [attr.aria-expanded]=\"expanded\"\n [attr.data-testid]=\"'collection-item-' + index + '-toggle'\"\n (click)=\"toggle(index)\"\n >\n <ui5-icon\n class=\"card__chevron\"\n [name]=\"expanded ? 'navigation-down-arrow' : 'navigation-right-arrow'\"\n />\n <span class=\"card__preview\">{{ previewFor(entry, index) }}</span>\n <span class=\"card__spacer\"></span>\n <ui5-button\n class=\"card__action\"\n design=\"Transparent\"\n icon=\"delete\"\n accessibleName=\"Remove\"\n tooltip=\"Remove\"\n [attr.data-testid]=\"'collection-item-' + index + '-remove'\"\n (click)=\"remove(index); $event.stopPropagation()\"\n />\n </button>\n\n @if (expanded) {\n <div class=\"card__body\">\n <mfp-declarative-form\n [attr.data-testid]=\"'collection-item-' + index + '-form'\"\n [fields]=\"fields()\"\n [initialValues]=\"entry\"\n (formValueChange)=\"onEntryValueChange(index, $event)\"\n />\n </div>\n }\n </div>\n }\n\n <ui5-button\n class=\"collection__add\"\n design=\"Transparent\"\n icon=\"add\"\n accessibleName=\"Add\"\n data-testid=\"collection-add\"\n (click)=\"add()\"\n />\n</div>\n", styles: [":host{display:block;width:100%}.collection{display:flex;flex-direction:column;gap:.5rem;width:100%;padding-top:.25rem}.card{border:1px solid var(--sapGroup_ContentBorderColor, #d9d9d9);border-radius:.5rem;background:var(--sapGroup_TitleBackground, #fff);overflow:hidden}.card--expanded{box-shadow:0 1px 2px #0000000a}.card__header{display:flex;align-items:center;gap:.5rem;width:100%;padding:0 .75rem;background:transparent;border:0;text-align:left;cursor:pointer;font:inherit;min-height:1.5rem;color:var(--sapTextColor, #1d2d3e)}.card__header:hover{background:var(--sapList_Hover_Background, rgba(0, 0, 0, .03))}.card__chevron{flex:0 0 auto;color:var(--sapContent_IconColor, #6a6d70)}.card__preview{font-weight:500;color:var(--sapTextColor, #1d2d3e);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.card__spacer{flex:1}.card__action{flex:0 0 auto}.card__body{padding:.75rem 1rem 1rem;border-top:1px solid var(--sapGroup_ContentBorderColor, #d9d9d9)}.collection__add{align-self:flex-start;color:var(--sapButton_IconColor, #0070f2)}\n"], dependencies: [{ kind: "component", type: i0.forwardRef(() => 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: i0.forwardRef(() => Icon), selector: "ui5-icon, [ui5-icon]", inputs: ["design", "name", "accessibleName", "showTooltip", "mode"], outputs: ["ui5Click"], exportAs: ["ui5Icon"] }, { kind: "component", type: i0.forwardRef(() => DeclarativeForm), selector: "mfp-declarative-form", inputs: ["fields", "initialValues", "fieldErrors"], outputs: ["fieldChange", "formSubmit", "formValueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.ShadowDom });
|
|
608
|
+
}
|
|
609
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: FormCollectionField, decorators: [{
|
|
610
|
+
type: Component,
|
|
611
|
+
args: [{ selector: 'mfp-form-collection-field', standalone: true, imports: [Button, Icon, forwardRef(() => DeclarativeForm)], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.ShadowDom, template: "<div class=\"collection\" data-testid=\"collection-field\">\n @for (entry of entries(); track $index; let index = $index) {\n @let expanded = expandedIndex() === index;\n <div\n class=\"card\"\n [class.card--expanded]=\"expanded\"\n [attr.data-testid]=\"'collection-item-' + index\"\n >\n <button\n type=\"button\"\n class=\"card__header\"\n [attr.aria-expanded]=\"expanded\"\n [attr.data-testid]=\"'collection-item-' + index + '-toggle'\"\n (click)=\"toggle(index)\"\n >\n <ui5-icon\n class=\"card__chevron\"\n [name]=\"expanded ? 'navigation-down-arrow' : 'navigation-right-arrow'\"\n />\n <span class=\"card__preview\">{{ previewFor(entry, index) }}</span>\n <span class=\"card__spacer\"></span>\n <ui5-button\n class=\"card__action\"\n design=\"Transparent\"\n icon=\"delete\"\n accessibleName=\"Remove\"\n tooltip=\"Remove\"\n [attr.data-testid]=\"'collection-item-' + index + '-remove'\"\n (click)=\"remove(index); $event.stopPropagation()\"\n />\n </button>\n\n @if (expanded) {\n <div class=\"card__body\">\n <mfp-declarative-form\n [attr.data-testid]=\"'collection-item-' + index + '-form'\"\n [fields]=\"fields()\"\n [initialValues]=\"entry\"\n (formValueChange)=\"onEntryValueChange(index, $event)\"\n />\n </div>\n }\n </div>\n }\n\n <ui5-button\n class=\"collection__add\"\n design=\"Transparent\"\n icon=\"add\"\n accessibleName=\"Add\"\n data-testid=\"collection-add\"\n (click)=\"add()\"\n />\n</div>\n", styles: [":host{display:block;width:100%}.collection{display:flex;flex-direction:column;gap:.5rem;width:100%;padding-top:.25rem}.card{border:1px solid var(--sapGroup_ContentBorderColor, #d9d9d9);border-radius:.5rem;background:var(--sapGroup_TitleBackground, #fff);overflow:hidden}.card--expanded{box-shadow:0 1px 2px #0000000a}.card__header{display:flex;align-items:center;gap:.5rem;width:100%;padding:0 .75rem;background:transparent;border:0;text-align:left;cursor:pointer;font:inherit;min-height:1.5rem;color:var(--sapTextColor, #1d2d3e)}.card__header:hover{background:var(--sapList_Hover_Background, rgba(0, 0, 0, .03))}.card__chevron{flex:0 0 auto;color:var(--sapContent_IconColor, #6a6d70)}.card__preview{font-weight:500;color:var(--sapTextColor, #1d2d3e);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.card__spacer{flex:1}.card__action{flex:0 0 auto}.card__body{padding:.75rem 1rem 1rem;border-top:1px solid var(--sapGroup_ContentBorderColor, #d9d9d9)}.collection__add{align-self:flex-start;color:var(--sapButton_IconColor, #0070f2)}\n"] }]
|
|
612
|
+
}], propDecorators: { fields: [{ type: i0.Input, args: [{ isSignal: true, alias: "fields", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], initialEntries: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialEntries", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
|
|
613
|
+
|
|
458
614
|
class DeclarativeForm {
|
|
459
615
|
fields = input.required(...(ngDevMode ? [{ debugName: "fields" }] : /* istanbul ignore next */ []));
|
|
460
616
|
initialValues = input({}, ...(ngDevMode ? [{ debugName: "initialValues" }] : /* istanbul ignore next */ []));
|
|
461
617
|
fieldErrors = input({}, ...(ngDevMode ? [{ debugName: "fieldErrors" }] : /* istanbul ignore next */ []));
|
|
462
618
|
fieldChange = output();
|
|
463
619
|
formSubmit = output();
|
|
620
|
+
formValueChange = output();
|
|
464
621
|
form;
|
|
622
|
+
collectionSeeds = signal({}, ...(ngDevMode ? [{ debugName: "collectionSeeds" }] : /* istanbul ignore next */ []));
|
|
465
623
|
fb = inject(FormBuilder);
|
|
466
624
|
constructor() {
|
|
467
625
|
this.form = this.fb.group({});
|
|
@@ -490,6 +648,7 @@ class DeclarativeForm {
|
|
|
490
648
|
control.setValue(target.value);
|
|
491
649
|
control.markAsTouched();
|
|
492
650
|
control.markAsDirty();
|
|
651
|
+
this.formValueChange.emit(this.form.value);
|
|
493
652
|
if (field.validation === 'onChange') {
|
|
494
653
|
this.fieldChange.emit({
|
|
495
654
|
fieldProperty: field.name,
|
|
@@ -497,6 +656,24 @@ class DeclarativeForm {
|
|
|
497
656
|
});
|
|
498
657
|
}
|
|
499
658
|
}
|
|
659
|
+
onCollectionValueChange(field, entries) {
|
|
660
|
+
const array = this.form.controls[field.name];
|
|
661
|
+
if (!array)
|
|
662
|
+
return;
|
|
663
|
+
while (array.length)
|
|
664
|
+
array.removeAt(0);
|
|
665
|
+
for (const entry of entries) {
|
|
666
|
+
array.push(this.buildEntryGroup(entry));
|
|
667
|
+
}
|
|
668
|
+
array.markAsDirty();
|
|
669
|
+
array.markAsTouched();
|
|
670
|
+
if (field.validation === 'onChange' || field.validation === 'onBlur') {
|
|
671
|
+
this.fieldChange.emit({
|
|
672
|
+
fieldProperty: field.name,
|
|
673
|
+
value: entries,
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
}
|
|
500
677
|
getError(name) {
|
|
501
678
|
const control = this.form.controls[name];
|
|
502
679
|
const error = this.fieldErrors()[name];
|
|
@@ -520,17 +697,30 @@ class DeclarativeForm {
|
|
|
520
697
|
clear() {
|
|
521
698
|
this.form.reset();
|
|
522
699
|
}
|
|
700
|
+
collectionEntries(field) {
|
|
701
|
+
return this.collectionSeeds()[field.name] ?? [];
|
|
702
|
+
}
|
|
523
703
|
rebuildControls(fields) {
|
|
524
704
|
const existingControls = this.form.controls;
|
|
525
705
|
const nextFieldNames = new Set(fields.map((field) => field.name));
|
|
526
706
|
for (const field of fields) {
|
|
527
707
|
const existingControl = existingControls[field.name];
|
|
528
|
-
|
|
708
|
+
const wantsCollection = !!field.propertyCollection?.length;
|
|
709
|
+
const existingIsCollection = existingControl instanceof FormArray;
|
|
710
|
+
if (existingControl && wantsCollection !== existingIsCollection) {
|
|
711
|
+
this.form.removeControl(field.name);
|
|
712
|
+
}
|
|
713
|
+
else if (existingControl) {
|
|
529
714
|
existingControl.clearValidators();
|
|
530
715
|
existingControl.updateValueAndValidity({ emitEvent: false });
|
|
531
716
|
continue;
|
|
532
717
|
}
|
|
533
|
-
|
|
718
|
+
if (wantsCollection) {
|
|
719
|
+
this.form.addControl(field.name, this.fb.array([]));
|
|
720
|
+
}
|
|
721
|
+
else {
|
|
722
|
+
this.form.addControl(field.name, new FormControl(''));
|
|
723
|
+
}
|
|
534
724
|
}
|
|
535
725
|
for (const controlName of Object.keys(existingControls)) {
|
|
536
726
|
if (!nextFieldNames.has(controlName)) {
|
|
@@ -543,6 +733,25 @@ class DeclarativeForm {
|
|
|
543
733
|
if (!initialValues) {
|
|
544
734
|
return;
|
|
545
735
|
}
|
|
736
|
+
const seeds = {};
|
|
737
|
+
for (const field of this.fields()) {
|
|
738
|
+
if (!field.propertyCollection?.length)
|
|
739
|
+
continue;
|
|
740
|
+
const raw = initialValues[field.name];
|
|
741
|
+
const entries = Array.isArray(raw)
|
|
742
|
+
? raw
|
|
743
|
+
: [];
|
|
744
|
+
const array = this.form.controls[field.name];
|
|
745
|
+
if (array) {
|
|
746
|
+
while (array.length)
|
|
747
|
+
array.removeAt(0);
|
|
748
|
+
for (const entry of entries) {
|
|
749
|
+
array.push(this.buildEntryGroup(entry));
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
seeds[field.name] = entries;
|
|
753
|
+
}
|
|
754
|
+
this.collectionSeeds.set(seeds);
|
|
546
755
|
this.form.patchValue(initialValues, { emitEvent: false });
|
|
547
756
|
this.form.markAsPristine({ emitEvent: false });
|
|
548
757
|
this.form.updateValueAndValidity({ emitEvent: false });
|
|
@@ -550,17 +759,34 @@ class DeclarativeForm {
|
|
|
550
759
|
buildOutputValue() {
|
|
551
760
|
const result = {};
|
|
552
761
|
for (const key of Object.keys(this.form.controls)) {
|
|
553
|
-
|
|
762
|
+
const control = this.form.controls[key];
|
|
763
|
+
let value = control.value;
|
|
764
|
+
if (control instanceof FormArray) {
|
|
765
|
+
value = control.controls.map((group) => group.controls['entry']?.value ?? {});
|
|
766
|
+
}
|
|
767
|
+
setPropertyByPath(result, key, value);
|
|
554
768
|
}
|
|
555
769
|
return result;
|
|
556
770
|
}
|
|
771
|
+
buildEntryGroup(entry) {
|
|
772
|
+
return this.fb.group({
|
|
773
|
+
entry: new FormControl(entry),
|
|
774
|
+
});
|
|
775
|
+
}
|
|
557
776
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: DeclarativeForm, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
558
|
-
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 class=\"form\" data-testid=\"generic-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.data-testid]=\"'generic-form-field-container-' + field.name\"\n >\n <ui5-label\n show-colon\n [attr.data-testid]=\"'generic-form-field-label-' + field.name\"\n [required]=\"field.required\"\n >{{ field.label }}</ui5-label\n >\n @if (field.values?.length) {\n <ui5-select\n class=\"input\"\n [attr.data-testid]=\"'generic-form-field-' + field.name\"\n [disabled]=\"field.disabled\"\n [required]=\"field.required\"\n [value]=\"control.value\"\n [valueState]=\"getValueState(field.name)\"\n (blur)=\"onFieldBlur(field)\"\n (change)=\"setFormControlValue($event, field)\"\n (input)=\"setFormControlValue($event, field)\"\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.data-testid]=\"\n 'generic-form-field-' +\n field.name +\n '-option-' +\n (value || 'empty')\n \"\n [selected]=\"value === control.value\"\n [value]=\"value\"\n >{{ value }}</ui5-option\n >\n }\n </ui5-select>\n } @else {\n <ui5-input\n class=\"input\"\n [attr.data-testid]=\"'generic-form-field-' + field.name\"\n [disabled]=\"field.disabled\"\n [required]=\"field.required\"\n [value]=\"control.value\"\n [valueState]=\"getValueState(field.name)\"\n (blur)=\"onFieldBlur(field)\"\n (change)=\"setFormControlValue($event, field)\"\n (input)=\"setFormControlValue($event, field)\"\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 });
|
|
777
|
+
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", formValueChange: "formValueChange" }, ngImport: i0, template: "<section class=\"form\" data-testid=\"generic-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.data-testid]=\"'generic-form-field-container-' + field.name\"\n >\n <ui5-label\n show-colon\n [attr.data-testid]=\"'generic-form-field-label-' + field.name\"\n [required]=\"field.required\"\n >{{ field.label }}</ui5-label\n >\n @if (field.propertyCollection?.length) {\n <mfp-form-collection-field\n [attr.data-testid]=\"'generic-form-field-' + field.name\"\n [fields]=\"field.propertyCollection!\"\n [label]=\"field.label\"\n [initialEntries]=\"collectionEntries(field)\"\n (valueChange)=\"onCollectionValueChange(field, $event)\"\n />\n @if (getError(field.name); as error) {\n <div class=\"collection-error\" role=\"alert\">{{ error }}</div>\n }\n } @else if (field.values?.length) {\n <ui5-select\n class=\"input\"\n [attr.data-testid]=\"'generic-form-field-' + field.name\"\n [disabled]=\"field.disabled\"\n [required]=\"field.required\"\n [value]=\"control.value\"\n [valueState]=\"getValueState(field.name)\"\n (blur)=\"onFieldBlur(field)\"\n (change)=\"setFormControlValue($event, field)\"\n (input)=\"setFormControlValue($event, field)\"\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.data-testid]=\"\n 'generic-form-field-' +\n field.name +\n '-option-' +\n (value || 'empty')\n \"\n [selected]=\"value === control.value\"\n [value]=\"value\"\n >{{ value }}</ui5-option\n >\n }\n </ui5-select>\n } @else {\n <ui5-input\n class=\"input\"\n [attr.data-testid]=\"'generic-form-field-' + field.name\"\n [disabled]=\"field.disabled\"\n [required]=\"field.required\"\n [value]=\"control.value\"\n [valueState]=\"getValueState(field.name)\"\n (blur)=\"onFieldBlur(field)\"\n (change)=\"setFormControlValue($event, field)\"\n (input)=\"setFormControlValue($event, field)\"\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%}.collection-error{color:var(--sapNegativeColor, #b00);font-size:.75rem;margin-top:.25rem}\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"] }, { kind: "component", type: FormCollectionField, selector: "mfp-form-collection-field", inputs: ["fields", "label", "initialEntries"], outputs: ["valueChange"] }], encapsulation: i0.ViewEncapsulation.ShadowDom });
|
|
559
778
|
}
|
|
560
779
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: DeclarativeForm, decorators: [{
|
|
561
780
|
type: Component,
|
|
562
|
-
args: [{ selector: 'mfp-declarative-form', imports: [
|
|
563
|
-
|
|
781
|
+
args: [{ selector: 'mfp-declarative-form', imports: [
|
|
782
|
+
ReactiveFormsModule,
|
|
783
|
+
Input,
|
|
784
|
+
Label,
|
|
785
|
+
Select,
|
|
786
|
+
Option,
|
|
787
|
+
FormCollectionField,
|
|
788
|
+
], encapsulation: ViewEncapsulation.ShadowDom, template: "<section class=\"form\" data-testid=\"generic-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.data-testid]=\"'generic-form-field-container-' + field.name\"\n >\n <ui5-label\n show-colon\n [attr.data-testid]=\"'generic-form-field-label-' + field.name\"\n [required]=\"field.required\"\n >{{ field.label }}</ui5-label\n >\n @if (field.propertyCollection?.length) {\n <mfp-form-collection-field\n [attr.data-testid]=\"'generic-form-field-' + field.name\"\n [fields]=\"field.propertyCollection!\"\n [label]=\"field.label\"\n [initialEntries]=\"collectionEntries(field)\"\n (valueChange)=\"onCollectionValueChange(field, $event)\"\n />\n @if (getError(field.name); as error) {\n <div class=\"collection-error\" role=\"alert\">{{ error }}</div>\n }\n } @else if (field.values?.length) {\n <ui5-select\n class=\"input\"\n [attr.data-testid]=\"'generic-form-field-' + field.name\"\n [disabled]=\"field.disabled\"\n [required]=\"field.required\"\n [value]=\"control.value\"\n [valueState]=\"getValueState(field.name)\"\n (blur)=\"onFieldBlur(field)\"\n (change)=\"setFormControlValue($event, field)\"\n (input)=\"setFormControlValue($event, field)\"\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.data-testid]=\"\n 'generic-form-field-' +\n field.name +\n '-option-' +\n (value || 'empty')\n \"\n [selected]=\"value === control.value\"\n [value]=\"value\"\n >{{ value }}</ui5-option\n >\n }\n </ui5-select>\n } @else {\n <ui5-input\n class=\"input\"\n [attr.data-testid]=\"'generic-form-field-' + field.name\"\n [disabled]=\"field.disabled\"\n [required]=\"field.required\"\n [value]=\"control.value\"\n [valueState]=\"getValueState(field.name)\"\n (blur)=\"onFieldBlur(field)\"\n (change)=\"setFormControlValue($event, field)\"\n (input)=\"setFormControlValue($event, field)\"\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%}.collection-error{color:var(--sapNegativeColor, #b00);font-size:.75rem;margin-top:.25rem}\n"] }]
|
|
789
|
+
}], 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"] }], formValueChange: [{ type: i0.Output, args: ["formValueChange"] }] } });
|
|
564
790
|
|
|
565
791
|
/**
|
|
566
792
|
* Horizontal filter-tab strip rendered above the table by
|
|
@@ -863,7 +1089,7 @@ class DeclarativeTableCard {
|
|
|
863
1089
|
}, {});
|
|
864
1090
|
}
|
|
865
1091
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: DeclarativeTableCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
866
|
-
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", filterTabChanged: "filterTabChanged" }, viewQueries: [{ propertyName: "searchInputRef", first: true, predicate: ["searchInput"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"card\" data-testid=\"generic-table-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 [accessibleName]=\"tooltip\"\n [showTooltip]=\"true\"\n />\n }\n }\n </div>\n <div class=\"card__actions\">\n @if (resourcesSearchable()) {\n @if (searchExpanded()) {\n <ui5-input\n #searchInput\n data-testid=\"generic-table-card-search-input\"\n [class]=\"\n 'card__search-input card__search-input--' +\n (searchCollapsing() ? 'leave' : 'enter')\n \"\n [formControl]=\"searchControl\"\n (animationend)=\"onSearchAnimationEnd()\"\n (blur)=\"onSearchBlur()\"\n />\n }\n <ui5-button\n class=\"card__search-btn\"\n data-testid=\"generic-table-card-search-btn\"\n design=\"Transparent\"\n [accessibleName]=\"searchButtonConfig()?.tooltip ?? 'Search'\"\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 data-testid=\"generic-table-card-create-btn\"\n [accessibleName]=\"createButtonConfig()?.text || createButtonConfig()?.tooltip || 'Create'\"\n [design]=\"createButtonConfig()?.design ?? 'Transparent'\"\n [icon]=\"createButtonConfig()?.icon ?? 'add'\"\n [tooltip]=\"createButtonConfig()?.tooltip\"\n (click)=\"createDialogOpen.set(true)\"\n >\n {{ createButtonConfig()?.text ?? '' }}\n </ui5-button>\n }\n </div>\n </div>\n\n @if (hasFilterTabs()) {\n <mfp-filter-tabs\n [tabs]=\"filterTabs()\"\n (tabChanged)=\"onFilterTabChanged($event)\"\n />\n }\n\n <div class=\"card__body\">\n @if (tableConfig(); as config) {\n <mfp-declarative-table\n [columns]=\"effectiveColumns()\"\n [growMode]=\"config.growMode ?? 'Button'\"\n [hasMore]=\"config.hasMore ?? false\"\n [height]=\"config.height\"\n [loadMoreButtonText]=\"config.loadMoreButtonText ?? 'Load More'\"\n [paginationLimit]=\"config.paginationLimit ?? 5\"\n [resources]=\"resources()\"\n [totalItemsCount]=\"config.totalItemsCount\"\n (buttonClick)=\"onButtonClick($event)\"\n (loadMoreResources)=\"loadMoreResources.emit()\"\n (paginationLimitChanged)=\"paginationLimitChanged.emit($event)\"\n (tableRowClicked)=\"tableRowClicked.emit($event)\"\n />\n }\n </div>\n</div>\n\n@if (createFormConfig(); as config) {\n <ui5-dialog\n data-testid=\"generic-table-card-create-dialog\"\n [accessibleName]=\"config.title ?? 'Create'\"\n [open]=\"createDialogOpen()\"\n (ui5BeforeClose)=\"createDialogOpen.set(false)\"\n >\n <div class=\"dialog__header\" slot=\"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 [fieldErrors]=\"createFormState().fieldErrors ?? {}\"\n [fields]=\"config.fields\"\n (fieldChange)=\"onCreateFieldChange($event)\"\n (formSubmit)=\"onCreateSubmit($event)\"\n />\n </div>\n <div class=\"dialog__footer\" slot=\"footer\">\n <ui5-button\n data-testid=\"generic-table-card-create-confirm\"\n design=\"Emphasized\"\n [disabled]=\"hasErrors(createFormState())\"\n (click)=\"createForm.submit()\"\n >\n {{ config.confirmLabel ?? 'Save' }}\n </ui5-button>\n <ui5-button data-testid=\"generic-table-card-create-cancel\" 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 data-testid=\"generic-table-card-edit-dialog\"\n [accessibleName]=\"config.title ?? 'Edit'\"\n [open]=\"editDialogOpen()\"\n (ui5BeforeClose)=\"editDialogOpen.set(false)\"\n >\n <div class=\"dialog__header\" slot=\"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 [fieldErrors]=\"editFormState().fieldErrors ?? {}\"\n [fields]=\"config.fields\"\n [initialValues]=\"editInitialValue()\"\n (fieldChange)=\"onEditFieldChange($event)\"\n (formSubmit)=\"onEditSubmit($event)\"\n />\n </div>\n <div class=\"dialog__footer\" slot=\"footer\">\n <ui5-button\n data-testid=\"generic-table-card-edit-confirm\"\n design=\"Emphasized\"\n [disabled]=\"hasErrors(editFormState())\"\n (click)=\"editForm.submit()\"\n >\n {{ config.confirmLabel ?? 'Edit' }}\n </ui5-button>\n <ui5-button data-testid=\"generic-table-card-edit-cancel\" 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 data-testid=\"generic-table-card-delete-dialog\"\n [accessibleName]=\"config.title ?? 'Confirm Delete'\"\n [open]=\"deleteDialogOpen()\"\n (ui5BeforeClose)=\"deleteDialogOpen.set(false)\"\n >\n <div class=\"dialog__header\" slot=\"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 class=\"dialog__footer\" slot=\"footer\">\n <ui5-button data-testid=\"generic-table-card-delete-confirm\" design=\"Negative\" (click)=\"onDeleteSubmit()\">\n {{ config.confirmLabel ?? 'Delete' }}\n </ui5-button>\n <ui5-button data-testid=\"generic-table-card-delete-cancel\" 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}.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", "growMode", "loadMoreButtonText", "height"], outputs: ["buttonClick", "tableRowClicked", "loadMoreResources", "paginationLimitChanged"] }, { kind: "component", type: DeclarativeForm, selector: "mfp-declarative-form", inputs: ["fields", "initialValues", "fieldErrors"], outputs: ["fieldChange", "formSubmit"] }, { kind: "component", type: FilterTabs, selector: "mfp-filter-tabs", inputs: ["tabs"], outputs: ["tabChanged"] }, { 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 });
|
|
1092
|
+
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", filterTabChanged: "filterTabChanged" }, viewQueries: [{ propertyName: "searchInputRef", first: true, predicate: ["searchInput"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"card\" data-testid=\"generic-table-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 [accessibleName]=\"tooltip\"\n [showTooltip]=\"true\"\n />\n }\n }\n </div>\n <div class=\"card__actions\">\n @if (resourcesSearchable()) {\n @if (searchExpanded()) {\n <ui5-input\n #searchInput\n data-testid=\"generic-table-card-search-input\"\n [class]=\"\n 'card__search-input card__search-input--' +\n (searchCollapsing() ? 'leave' : 'enter')\n \"\n [formControl]=\"searchControl\"\n (animationend)=\"onSearchAnimationEnd()\"\n (blur)=\"onSearchBlur()\"\n />\n }\n <ui5-button\n class=\"card__search-btn\"\n data-testid=\"generic-table-card-search-btn\"\n design=\"Transparent\"\n [accessibleName]=\"searchButtonConfig()?.tooltip ?? 'Search'\"\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 data-testid=\"generic-table-card-create-btn\"\n [accessibleName]=\"createButtonConfig()?.text || createButtonConfig()?.tooltip || 'Create'\"\n [design]=\"createButtonConfig()?.design ?? 'Transparent'\"\n [icon]=\"createButtonConfig()?.icon ?? 'add'\"\n [tooltip]=\"createButtonConfig()?.tooltip\"\n (click)=\"createDialogOpen.set(true)\"\n >\n {{ createButtonConfig()?.text ?? '' }}\n </ui5-button>\n }\n </div>\n </div>\n\n @if (hasFilterTabs()) {\n <mfp-filter-tabs\n [tabs]=\"filterTabs()\"\n (tabChanged)=\"onFilterTabChanged($event)\"\n />\n }\n\n <div class=\"card__body\">\n @if (tableConfig(); as config) {\n <mfp-declarative-table\n [columns]=\"effectiveColumns()\"\n [growMode]=\"config.growMode ?? 'Button'\"\n [hasMore]=\"config.hasMore ?? false\"\n [height]=\"config.height\"\n [loadMoreButtonText]=\"config.loadMoreButtonText ?? 'Load More'\"\n [paginationLimit]=\"config.paginationLimit ?? 5\"\n [resources]=\"resources()\"\n [totalItemsCount]=\"config.totalItemsCount\"\n (buttonClick)=\"onButtonClick($event)\"\n (loadMoreResources)=\"loadMoreResources.emit()\"\n (paginationLimitChanged)=\"paginationLimitChanged.emit($event)\"\n (tableRowClicked)=\"tableRowClicked.emit($event)\"\n />\n }\n </div>\n</div>\n\n@if (createFormConfig(); as config) {\n <ui5-dialog\n data-testid=\"generic-table-card-create-dialog\"\n [accessibleName]=\"config.title ?? 'Create'\"\n [open]=\"createDialogOpen()\"\n (ui5BeforeClose)=\"createDialogOpen.set(false)\"\n >\n <div class=\"dialog__header\" slot=\"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 [fieldErrors]=\"createFormState().fieldErrors ?? {}\"\n [fields]=\"config.fields\"\n (fieldChange)=\"onCreateFieldChange($event)\"\n (formSubmit)=\"onCreateSubmit($event)\"\n />\n </div>\n <div class=\"dialog__footer\" slot=\"footer\">\n <ui5-button\n data-testid=\"generic-table-card-create-confirm\"\n design=\"Emphasized\"\n [disabled]=\"hasErrors(createFormState())\"\n (click)=\"createForm.submit()\"\n >\n {{ config.confirmLabel ?? 'Save' }}\n </ui5-button>\n <ui5-button data-testid=\"generic-table-card-create-cancel\" 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 data-testid=\"generic-table-card-edit-dialog\"\n [accessibleName]=\"config.title ?? 'Edit'\"\n [open]=\"editDialogOpen()\"\n (ui5BeforeClose)=\"editDialogOpen.set(false)\"\n >\n <div class=\"dialog__header\" slot=\"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 [fieldErrors]=\"editFormState().fieldErrors ?? {}\"\n [fields]=\"config.fields\"\n [initialValues]=\"editInitialValue()\"\n (fieldChange)=\"onEditFieldChange($event)\"\n (formSubmit)=\"onEditSubmit($event)\"\n />\n </div>\n <div class=\"dialog__footer\" slot=\"footer\">\n <ui5-button\n data-testid=\"generic-table-card-edit-confirm\"\n design=\"Emphasized\"\n [disabled]=\"hasErrors(editFormState())\"\n (click)=\"editForm.submit()\"\n >\n {{ config.confirmLabel ?? 'Edit' }}\n </ui5-button>\n <ui5-button data-testid=\"generic-table-card-edit-cancel\" 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 data-testid=\"generic-table-card-delete-dialog\"\n [accessibleName]=\"config.title ?? 'Confirm Delete'\"\n [open]=\"deleteDialogOpen()\"\n (ui5BeforeClose)=\"deleteDialogOpen.set(false)\"\n >\n <div class=\"dialog__header\" slot=\"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 class=\"dialog__footer\" slot=\"footer\">\n <ui5-button data-testid=\"generic-table-card-delete-confirm\" design=\"Negative\" (click)=\"onDeleteSubmit()\">\n {{ config.confirmLabel ?? 'Delete' }}\n </ui5-button>\n <ui5-button data-testid=\"generic-table-card-delete-cancel\" 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}.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", "growMode", "loadMoreButtonText", "height"], outputs: ["buttonClick", "tableRowClicked", "loadMoreResources", "paginationLimitChanged"] }, { kind: "component", type: DeclarativeForm, selector: "mfp-declarative-form", inputs: ["fields", "initialValues", "fieldErrors"], outputs: ["fieldChange", "formSubmit", "formValueChange"] }, { kind: "component", type: FilterTabs, selector: "mfp-filter-tabs", inputs: ["tabs"], outputs: ["tabChanged"] }, { 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 });
|
|
867
1093
|
}
|
|
868
1094
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: DeclarativeTableCard, decorators: [{
|
|
869
1095
|
type: Component,
|