@openmfp/ngx 1.1.0 → 1.3.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 +68 -4
- package/package.json +1 -1
- package/types/openmfp-ngx.d.ts +27 -0
package/fesm2022/openmfp-ngx.mjs
CHANGED
|
@@ -27,6 +27,7 @@ import '@ui5/webcomponents-icons/dist/add.js';
|
|
|
27
27
|
import '@ui5/webcomponents-icons/dist/delete.js';
|
|
28
28
|
import '@ui5/webcomponents-icons/dist/navigation-down-arrow.js';
|
|
29
29
|
import '@ui5/webcomponents-icons/dist/navigation-right-arrow.js';
|
|
30
|
+
import { InputIcon } from '@fundamental-ngx/ui5-webcomponents/input-icon';
|
|
30
31
|
import { Label } from '@fundamental-ngx/ui5-webcomponents/label';
|
|
31
32
|
import { Option } from '@fundamental-ngx/ui5-webcomponents/option';
|
|
32
33
|
import { Select } from '@fundamental-ngx/ui5-webcomponents/select';
|
|
@@ -1232,7 +1233,6 @@ class Dashboard {
|
|
|
1232
1233
|
...(ngDevMode ? [{ debugName: "isDashboardEditable" }] : /* istanbul ignore next */ []));
|
|
1233
1234
|
gridOptions = computed(() => ({
|
|
1234
1235
|
cellHeight: CELL_HEIGHT,
|
|
1235
|
-
sizeToContent: true,
|
|
1236
1236
|
disableResize: !this.editMode(),
|
|
1237
1237
|
disableDrag: !this.editMode(),
|
|
1238
1238
|
marginBottom: 0,
|
|
@@ -1833,6 +1833,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
1833
1833
|
], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.ShadowDom, schemas: [CUSTOM_ELEMENTS_SCHEMA], template: "<ui5-dialog\n data-testid=\"generic-table-card-delete-dialog\"\n [accessibleName]=\"config()?.title ?? 'Confirm Delete'\"\n [open]=\"open()\"\n (ui5BeforeClose)=\"cancelled.emit()\"\n>\n <div class=\"dialog__header\" slot=\"header\">\n <ui5-icon class=\"dialog__header-icon\" design=\"Critical\" name=\"alert\" />\n <ui5-title level=\"H5\">\n <span\n [innerHTML]=\"config()?.title ?? 'Confirm Delete' | sanitizeHtml\"\n ></span>\n </ui5-title>\n </div>\n @if (config()?.message || requiresConfirmation()) {\n <div class=\"dialog__body\">\n @if (config()?.message; as message) {\n <p class=\"dialog__message\" [innerHTML]=\"message | sanitizeHtml\"></p>\n }\n @if (requiresConfirmation()) {\n <p class=\"dialog__message\">\n <ui5-input\n class=\"dialog__confirmation-input\"\n data-testid=\"generic-table-card-delete-confirmation-input\"\n [formControl]=\"confirmationControl\"\n [placeholder]=\"\n config()?.confirmationPlaceholder ?? 'Type to confirm'\n \"\n [valueState]=\"\n confirmationControl.invalid && confirmationControl.dirty\n ? 'Negative'\n : 'None'\n \"\n />\n </p>\n }\n </div>\n }\n <div class=\"dialog__footer\" slot=\"footer\">\n <ui5-button\n data-testid=\"generic-table-card-delete-confirm\"\n design=\"Negative\"\n [disabled]=\"confirmDisabled()\"\n (click)=\"onConfirm()\"\n >\n {{ config()?.confirmLabel ?? 'Delete' }}\n </ui5-button>\n <ui5-button\n data-testid=\"generic-table-card-delete-cancel\"\n design=\"Transparent\"\n (click)=\"cancelled.emit()\"\n >\n {{ config()?.cancelLabel ?? 'Cancel' }}\n </ui5-button>\n </div>\n</ui5-dialog>\n", styles: [":host{display:contents}.dialog__header{display:flex;align-items:center;gap:.5rem;padding:.75rem 1rem}.dialog__header-icon{color:var(--sapCriticalColor)}.dialog__body{margin:-1rem;min-width:320px;padding:1rem}.dialog__message{margin:0;font-size:.875rem;max-width:320px}.dialog__message--critical{color:var(--sapCriticalElementColor)}.dialog__confirmation-input{display:block;width:100%;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"] }]
|
|
1834
1834
|
}], ctorParameters: () => [], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }], config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], confirmed: [{ type: i0.Output, args: ["confirmed"] }], cancelled: [{ type: i0.Output, args: ["cancelled"] }] } });
|
|
1835
1835
|
|
|
1836
|
+
function coerceBoolean(value) {
|
|
1837
|
+
if (typeof value === 'boolean')
|
|
1838
|
+
return value;
|
|
1839
|
+
if (typeof value === 'string') {
|
|
1840
|
+
return value.trim().toLowerCase() === 'true';
|
|
1841
|
+
}
|
|
1842
|
+
return false;
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1836
1845
|
const FORBIDDEN_SEGMENTS = new Set(['__proto__', 'prototype', 'constructor']);
|
|
1837
1846
|
function setPropertyByPath(object, path, value) {
|
|
1838
1847
|
const segments = path.split('.').filter(Boolean);
|
|
@@ -1942,6 +1951,8 @@ class DeclarativeForm {
|
|
|
1942
1951
|
form;
|
|
1943
1952
|
collectionSeeds = signal({}, /* @ts-ignore */
|
|
1944
1953
|
...(ngDevMode ? [{ debugName: "collectionSeeds" }] : /* istanbul ignore next */ []));
|
|
1954
|
+
passwordVisible = signal({}, /* @ts-ignore */
|
|
1955
|
+
...(ngDevMode ? [{ debugName: "passwordVisible" }] : /* istanbul ignore next */ []));
|
|
1945
1956
|
fb = inject(FormBuilder);
|
|
1946
1957
|
constructor() {
|
|
1947
1958
|
this.form = this.fb.group({});
|
|
@@ -1978,6 +1989,47 @@ class DeclarativeForm {
|
|
|
1978
1989
|
});
|
|
1979
1990
|
}
|
|
1980
1991
|
}
|
|
1992
|
+
setSwitchValue($event, field) {
|
|
1993
|
+
const target = $event.target;
|
|
1994
|
+
const control = this.form.controls[field.name];
|
|
1995
|
+
const checked = coerceBoolean(target.checked);
|
|
1996
|
+
control.setValue(checked);
|
|
1997
|
+
control.markAsTouched();
|
|
1998
|
+
control.markAsDirty();
|
|
1999
|
+
this.formValueChange.emit(this.form.value);
|
|
2000
|
+
if (field.validation === 'onChange') {
|
|
2001
|
+
this.fieldChange.emit({
|
|
2002
|
+
fieldProperty: field.name,
|
|
2003
|
+
value: checked,
|
|
2004
|
+
});
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
coerceChecked = coerceBoolean;
|
|
2008
|
+
isPasswordVisible(field) {
|
|
2009
|
+
return this.passwordVisible()[field.name] ?? false;
|
|
2010
|
+
}
|
|
2011
|
+
passwordInputType(field) {
|
|
2012
|
+
if (field.inputType !== 'Password') {
|
|
2013
|
+
return 'Text';
|
|
2014
|
+
}
|
|
2015
|
+
return this.isPasswordVisible(field) ? 'Text' : 'Password';
|
|
2016
|
+
}
|
|
2017
|
+
passwordToggleIcon(field) {
|
|
2018
|
+
return this.isPasswordVisible(field) ? 'hide' : 'show';
|
|
2019
|
+
}
|
|
2020
|
+
passwordToggleLabel(field) {
|
|
2021
|
+
return this.isPasswordVisible(field) ? 'Hide value' : 'Show value';
|
|
2022
|
+
}
|
|
2023
|
+
shouldShowPasswordToggle(field) {
|
|
2024
|
+
return field.inputType === 'Password' && (field.showPasswordToggle ?? true);
|
|
2025
|
+
}
|
|
2026
|
+
togglePasswordVisibility(field, event) {
|
|
2027
|
+
event.stopPropagation();
|
|
2028
|
+
this.passwordVisible.update((state) => ({
|
|
2029
|
+
...state,
|
|
2030
|
+
[field.name]: !this.isPasswordVisible(field),
|
|
2031
|
+
}));
|
|
2032
|
+
}
|
|
1981
2033
|
onCollectionValueChange(field, entries) {
|
|
1982
2034
|
const array = this.form.controls[field.name];
|
|
1983
2035
|
if (!array)
|
|
@@ -2018,6 +2070,7 @@ class DeclarativeForm {
|
|
|
2018
2070
|
}
|
|
2019
2071
|
clear() {
|
|
2020
2072
|
this.form.reset();
|
|
2073
|
+
this.passwordVisible.set({});
|
|
2021
2074
|
}
|
|
2022
2075
|
collectionEntries(field) {
|
|
2023
2076
|
return this.collectionSeeds()[field.name] ?? [];
|
|
@@ -2040,6 +2093,9 @@ class DeclarativeForm {
|
|
|
2040
2093
|
if (wantsCollection) {
|
|
2041
2094
|
this.form.addControl(field.name, this.fb.array([]));
|
|
2042
2095
|
}
|
|
2096
|
+
else if (field.inputType === 'Switch') {
|
|
2097
|
+
this.form.addControl(field.name, new FormControl(false));
|
|
2098
|
+
}
|
|
2043
2099
|
else {
|
|
2044
2100
|
this.form.addControl(field.name, new FormControl(''));
|
|
2045
2101
|
}
|
|
@@ -2074,7 +2130,13 @@ class DeclarativeForm {
|
|
|
2074
2130
|
seeds[field.name] = entries;
|
|
2075
2131
|
}
|
|
2076
2132
|
this.collectionSeeds.set(seeds);
|
|
2077
|
-
|
|
2133
|
+
const normalizedValues = { ...initialValues };
|
|
2134
|
+
for (const field of this.fields()) {
|
|
2135
|
+
if (field.inputType === 'Switch') {
|
|
2136
|
+
normalizedValues[field.name] = coerceBoolean(normalizedValues[field.name]);
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
this.form.patchValue(normalizedValues, { emitEvent: false });
|
|
2078
2140
|
this.form.markAsPristine({ emitEvent: false });
|
|
2079
2141
|
this.form.updateValueAndValidity({ emitEvent: false });
|
|
2080
2142
|
}
|
|
@@ -2096,18 +2158,20 @@ class DeclarativeForm {
|
|
|
2096
2158
|
});
|
|
2097
2159
|
}
|
|
2098
2160
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: DeclarativeForm, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2099
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: DeclarativeForm, isStandalone: true, selector: "mfp-declarative-form", inputs: { fields: { classPropertyName: "fields", publicName: "fields", isSignal: true, isRequired: false, 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 [attr.for]=\"'generic-form-field-input-' + 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 [initialEntries]=\"collectionEntries(field)\"\n [label]=\"field.label\"\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 [id]=\"'generic-form-field-input-' + field.name\"\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 [id]=\"'generic-form-field-input-' + field.name\"\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: ["accessibleDescription", "accessibleDescriptionRef", "accessibleName", "accessibleNameRef", "disabled", "filter", "maxlength", "name", "noTypeahead", "open", "placeholder", "readonly", "required", "showClearIcon", "showSuggestions", "type", "value", "valueState"], outputs: ["ui5Change", "ui5Close", "ui5Input", "ui5Open", "ui5Select", "ui5SelectionChange"], exportAs: ["ui5Input"] }, { kind: "component", type: Label, selector: "ui5-label, [ui5-label]", inputs: ["for", "required", "showColon", "wrappingType"], exportAs: ["ui5Label"] }, { kind: "component", type: Select, selector: "ui5-select, [ui5-select]", inputs: ["accessibleDescription", "accessibleDescriptionRef", "accessibleName", "accessibleNameRef", "disabled", "name", "readonly", "required", "textSeparator", "tooltip", "value", "valueState"], outputs: ["ui5Change", "ui5Close", "ui5LiveChange", "ui5Open"], exportAs: ["ui5Select"] }, { kind: "component", type: Option, selector: "ui5-option, [ui5-option]", inputs: ["additionalText", "icon", "selected", "tooltip", "value"], outputs: ["ui5Click"], exportAs: ["ui5Option"] }, { kind: "component", type: FormCollectionField, selector: "mfp-form-collection-field", inputs: ["fields", "label", "initialEntries"], outputs: ["valueChange"] }], encapsulation: i0.ViewEncapsulation.ShadowDom });
|
|
2161
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: DeclarativeForm, isStandalone: true, selector: "mfp-declarative-form", inputs: { fields: { classPropertyName: "fields", publicName: "fields", isSignal: true, isRequired: false, 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 [class.inputs--switch]=\"field.inputType === 'Switch'\"\n >\n <ui5-label\n show-colon\n [attr.data-testid]=\"'generic-form-field-label-' + field.name\"\n [attr.for]=\"\n field.inputType === 'Switch'\n ? null\n : 'generic-form-field-input-' + field.name\n \"\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 [initialEntries]=\"collectionEntries(field)\"\n [label]=\"field.label\"\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 [id]=\"'generic-form-field-input-' + field.name\"\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 if (field.inputType === 'Switch') {\n <ui5-switch\n class=\"switch\"\n [accessibleName]=\"field.label\"\n [attr.data-testid]=\"'generic-form-field-' + field.name\"\n [checked]=\"coerceChecked(control.value)\"\n [disabled]=\"field.disabled\"\n (blur)=\"onFieldBlur(field)\"\n (ui5Change)=\"setSwitchValue($event, field)\"\n />\n @if (getError(field.name); as error) {\n <div class=\"field-error\" role=\"alert\">{{ error }}</div>\n }\n } @else {\n <ui5-input\n class=\"input\"\n [attr.data-testid]=\"'generic-form-field-' + field.name\"\n [attr.title]=\"field.placeholder || null\"\n [disabled]=\"field.disabled\"\n [id]=\"'generic-form-field-input-' + field.name\"\n [placeholder]=\"field.placeholder ?? ''\"\n [required]=\"field.required\"\n [type]=\"passwordInputType(field)\"\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 (shouldShowPasswordToggle(field)) {\n <ui5-input-icon\n slot=\"icon\"\n [accessibleName]=\"passwordToggleLabel(field)\"\n [attr.data-testid]=\"\n 'generic-form-field-' + field.name + '-password-toggle'\n \"\n [name]=\"passwordToggleIcon(field)\"\n (ui5Click)=\"togglePasswordVisibility(field, $event)\"\n />\n }\n @if (getError(field.name); as error) {\n <div slot=\"valueStateMessage\">{{ error }}</div>\n }\n </ui5-input>\n }\n @if (field.hint && !field.propertyCollection?.length) {\n <div\n class=\"field-hint\"\n [attr.data-testid]=\"'generic-form-field-hint-' + field.name\"\n >\n {{ field.hint }}\n </div>\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%}.inputs--switch{flex-direction:row;align-items:center;gap:.75rem}.switch{flex-shrink:0}.inputs--switch .field-hint{flex-basis:100%}.collection-error,.field-error{color:var(--sapNegativeColor, #b00);font-size:.75rem;margin-top:.25rem}.inputs--switch .field-error{flex-basis:100%}.field-hint{color:var(--sapContent_LabelColor, #6a6d70);font-size:.75rem;line-height:1.4;margin-top:.25rem;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: ["accessibleDescription", "accessibleDescriptionRef", "accessibleName", "accessibleNameRef", "disabled", "filter", "maxlength", "name", "noTypeahead", "open", "placeholder", "readonly", "required", "showClearIcon", "showSuggestions", "type", "value", "valueState"], outputs: ["ui5Change", "ui5Close", "ui5Input", "ui5Open", "ui5Select", "ui5SelectionChange"], exportAs: ["ui5Input"] }, { kind: "component", type: InputIcon, selector: "ui5-input-icon, [ui5-input-icon]", inputs: ["accessibleName", "name"], outputs: ["ui5Click"], exportAs: ["ui5InputIcon"] }, { kind: "component", type: Label, selector: "ui5-label, [ui5-label]", inputs: ["for", "required", "showColon", "wrappingType"], exportAs: ["ui5Label"] }, { kind: "component", type: Select, selector: "ui5-select, [ui5-select]", inputs: ["accessibleDescription", "accessibleDescriptionRef", "accessibleName", "accessibleNameRef", "disabled", "name", "readonly", "required", "textSeparator", "tooltip", "value", "valueState"], outputs: ["ui5Change", "ui5Close", "ui5LiveChange", "ui5Open"], exportAs: ["ui5Select"] }, { kind: "component", type: Option, selector: "ui5-option, [ui5-option]", inputs: ["additionalText", "icon", "selected", "tooltip", "value"], outputs: ["ui5Click"], exportAs: ["ui5Option"] }, { kind: "component", type: Switch, selector: "ui5-switch, [ui5-switch]", inputs: ["accessibleName", "accessibleNameRef", "checked", "design", "disabled", "name", "readonly", "required", "textOff", "textOn", "tooltip", "value"], outputs: ["ui5Change"], exportAs: ["ui5Switch"] }, { kind: "component", type: FormCollectionField, selector: "mfp-form-collection-field", inputs: ["fields", "label", "initialEntries"], outputs: ["valueChange"] }], encapsulation: i0.ViewEncapsulation.ShadowDom });
|
|
2100
2162
|
}
|
|
2101
2163
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: DeclarativeForm, decorators: [{
|
|
2102
2164
|
type: Component,
|
|
2103
2165
|
args: [{ selector: 'mfp-declarative-form', imports: [
|
|
2104
2166
|
ReactiveFormsModule,
|
|
2105
2167
|
Input,
|
|
2168
|
+
InputIcon,
|
|
2106
2169
|
Label,
|
|
2107
2170
|
Select,
|
|
2108
2171
|
Option,
|
|
2172
|
+
Switch,
|
|
2109
2173
|
FormCollectionField,
|
|
2110
|
-
], 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 [attr.for]=\"'generic-form-field-input-' + 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 [initialEntries]=\"collectionEntries(field)\"\n [label]=\"field.label\"\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 [id]=\"'generic-form-field-input-' + field.name\"\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 [id]=\"'generic-form-field-input-' + field.name\"\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"] }]
|
|
2174
|
+
], 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 [class.inputs--switch]=\"field.inputType === 'Switch'\"\n >\n <ui5-label\n show-colon\n [attr.data-testid]=\"'generic-form-field-label-' + field.name\"\n [attr.for]=\"\n field.inputType === 'Switch'\n ? null\n : 'generic-form-field-input-' + field.name\n \"\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 [initialEntries]=\"collectionEntries(field)\"\n [label]=\"field.label\"\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 [id]=\"'generic-form-field-input-' + field.name\"\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 if (field.inputType === 'Switch') {\n <ui5-switch\n class=\"switch\"\n [accessibleName]=\"field.label\"\n [attr.data-testid]=\"'generic-form-field-' + field.name\"\n [checked]=\"coerceChecked(control.value)\"\n [disabled]=\"field.disabled\"\n (blur)=\"onFieldBlur(field)\"\n (ui5Change)=\"setSwitchValue($event, field)\"\n />\n @if (getError(field.name); as error) {\n <div class=\"field-error\" role=\"alert\">{{ error }}</div>\n }\n } @else {\n <ui5-input\n class=\"input\"\n [attr.data-testid]=\"'generic-form-field-' + field.name\"\n [attr.title]=\"field.placeholder || null\"\n [disabled]=\"field.disabled\"\n [id]=\"'generic-form-field-input-' + field.name\"\n [placeholder]=\"field.placeholder ?? ''\"\n [required]=\"field.required\"\n [type]=\"passwordInputType(field)\"\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 (shouldShowPasswordToggle(field)) {\n <ui5-input-icon\n slot=\"icon\"\n [accessibleName]=\"passwordToggleLabel(field)\"\n [attr.data-testid]=\"\n 'generic-form-field-' + field.name + '-password-toggle'\n \"\n [name]=\"passwordToggleIcon(field)\"\n (ui5Click)=\"togglePasswordVisibility(field, $event)\"\n />\n }\n @if (getError(field.name); as error) {\n <div slot=\"valueStateMessage\">{{ error }}</div>\n }\n </ui5-input>\n }\n @if (field.hint && !field.propertyCollection?.length) {\n <div\n class=\"field-hint\"\n [attr.data-testid]=\"'generic-form-field-hint-' + field.name\"\n >\n {{ field.hint }}\n </div>\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%}.inputs--switch{flex-direction:row;align-items:center;gap:.75rem}.switch{flex-shrink:0}.inputs--switch .field-hint{flex-basis:100%}.collection-error,.field-error{color:var(--sapNegativeColor, #b00);font-size:.75rem;margin-top:.25rem}.inputs--switch .field-error{flex-basis:100%}.field-hint{color:var(--sapContent_LabelColor, #6a6d70);font-size:.75rem;line-height:1.4;margin-top:.25rem;width:100%}\n"] }]
|
|
2111
2175
|
}], ctorParameters: () => [], propDecorators: { fields: [{ type: i0.Input, args: [{ isSignal: true, alias: "fields", required: false }] }], 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"] }] } });
|
|
2112
2176
|
|
|
2113
2177
|
class ResourceFormDialog {
|
package/package.json
CHANGED
package/types/openmfp-ngx.d.ts
CHANGED
|
@@ -526,6 +526,22 @@ interface FormFieldDefinition {
|
|
|
526
526
|
values?: string[];
|
|
527
527
|
/** When `true`, the field is disabled (not interactive). */
|
|
528
528
|
disabled?: boolean;
|
|
529
|
+
/** UI5 input type rendered by `mfp-declarative-form`. */
|
|
530
|
+
inputType?: 'Text' | 'Password' | 'Switch';
|
|
531
|
+
/**
|
|
532
|
+
* When `inputType` is `Password`, renders a show/hide icon inside the input.
|
|
533
|
+
* Defaults to `true`; set to `false` to keep the field masked without a toggle.
|
|
534
|
+
*/
|
|
535
|
+
showPasswordToggle?: boolean;
|
|
536
|
+
/** UI5 input placeholder (functional, not example values). */
|
|
537
|
+
placeholder?: string;
|
|
538
|
+
/** Persistent help text below the control; never submitted with the form. */
|
|
539
|
+
hint?: string;
|
|
540
|
+
/**
|
|
541
|
+
* When `true`, the host should omit this field from read queries and skip
|
|
542
|
+
* empty values on edit submit (e.g. write-only secrets).
|
|
543
|
+
*/
|
|
544
|
+
writeOnly?: boolean;
|
|
529
545
|
/** Controls when `fieldChange` is emitted for this field. When omitted, `fieldChange` is never emitted. */
|
|
530
546
|
validation?: 'onBlur' | 'onChange';
|
|
531
547
|
/**
|
|
@@ -553,6 +569,8 @@ interface FormFieldChangeEvent {
|
|
|
553
569
|
/** Map of field names to their current validation error messages (`null` = no error). */
|
|
554
570
|
type FormFieldErrors = Record<string, string | null>;
|
|
555
571
|
|
|
572
|
+
declare function coerceBoolean(value: unknown): boolean;
|
|
573
|
+
|
|
556
574
|
declare class DeclarativeForm<T extends GenericResource> {
|
|
557
575
|
readonly fields: _angular_core.InputSignal<FormFieldDefinition[]>;
|
|
558
576
|
readonly initialValues: _angular_core.InputSignal<T>;
|
|
@@ -562,9 +580,18 @@ declare class DeclarativeForm<T extends GenericResource> {
|
|
|
562
580
|
readonly formValueChange: _angular_core.OutputEmitterRef<Record<string, unknown>>;
|
|
563
581
|
readonly form: FormGroup;
|
|
564
582
|
protected readonly collectionSeeds: _angular_core.WritableSignal<Record<string, Record<string, unknown>[]>>;
|
|
583
|
+
private readonly passwordVisible;
|
|
565
584
|
private readonly fb;
|
|
566
585
|
constructor();
|
|
567
586
|
setFormControlValue($event: Event, field: FormFieldDefinition): void;
|
|
587
|
+
setSwitchValue($event: Event, field: FormFieldDefinition): void;
|
|
588
|
+
protected readonly coerceChecked: typeof coerceBoolean;
|
|
589
|
+
isPasswordVisible(field: FormFieldDefinition): boolean;
|
|
590
|
+
passwordInputType(field: FormFieldDefinition): 'Text' | 'Password';
|
|
591
|
+
passwordToggleIcon(field: FormFieldDefinition): 'hide' | 'show';
|
|
592
|
+
passwordToggleLabel(field: FormFieldDefinition): string;
|
|
593
|
+
shouldShowPasswordToggle(field: FormFieldDefinition): boolean;
|
|
594
|
+
togglePasswordVisibility(field: FormFieldDefinition, event: Event): void;
|
|
568
595
|
onCollectionValueChange(field: FormFieldDefinition, entries: Record<string, unknown>[]): void;
|
|
569
596
|
getError(name: string): string | null;
|
|
570
597
|
getValueState(name: string): 'None' | 'Negative';
|