@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.
@@ -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, output, signal, ViewEncapsulation, inject, effect, viewChild, Injector, afterNextRender, Injectable, reflectComponentType, isStandalone, isDevMode, ViewContainerRef, Renderer2, ElementRef, model, SecurityContext, linkedSignal } from '@angular/core';
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.split(separator).map(v => v.trim()).filter(v => v.length > 0);
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 @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 >&nbsp;\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", styles: [":host{display:inline-flex;align-items:center}.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"] }, { kind: "component", type: TagListValue, selector: "mfp-tag-list-value", inputs: ["tags", "tagSettings", "testId"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.ShadowDom });
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 >&nbsp;\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: [Icon, BooleanValue, LinkValue, SecretValue, Button, TagListValue], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.ShadowDom, schemas: [CUSTOM_ELEMENTS_SCHEMA], template: "@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 >&nbsp;\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", styles: [":host{display:inline-flex;align-items:center}.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"] }]
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 >&nbsp;\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
- if (existingControl) {
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
- this.form.addControl(field.name, new FormControl(''));
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
- setPropertyByPath(result, key, this.form.controls[key].value);
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: [ReactiveFormsModule, Input, Label, Select, Option], 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.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"] }]
563
- }], 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"] }] } });
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,