import { NgTemplateOutlet } from '@angular/common';
import * as i0 from '@angular/core';
import { input, booleanAttribute, inject, computed, Component, signal, HostBinding, ChangeDetectionStrategy, InjectionToken, Injectable, contentChild, ElementRef, isSignal, Injector, HostListener, Directive, NgModule } from '@angular/core';
import { SiResponsiveContainerDirective } from '@siemens/element-ng/resize-observer';
import { NgControl, RequiredValidator, FormControl } from '@angular/forms';
import { SiTranslatePipe, t } from '@siemens/element-translate-ng/translate';
import { SiTooltipService } from '@siemens/element-ng/tooltip';

/**
 * Copyright (c) Siemens 2016 - 2026
 * SPDX-License-Identifier: MIT
 */
class SiFormContainerComponent {
    /**
     * Set the form entity to the container to enable the overall form validation on in
     * the form container edit panel.
     */
    form = input(...(ngDevMode ? [undefined, { debugName: "form" }] : []));
    /**
     * A form container in readonly mode is only displaying the form content without ability
     * to change it. The edit panel with typically save and cancel buttons is hidden. Set
     * to true to display the edit panel.
     *
     * @defaultValue false
     */
    readonly = input(false, { ...(ngDevMode ? { debugName: "readonly" } : {}), transform: booleanAttribute });
    /**
     * The container hosts the form within a siResizeContainer to configure the breakpoint for
     * different screen sizes. Optionally, change the container breakpoints with the contentContainerBreakpoints
     * input.
     */
    contentContainerBreakpoints = input(...(ngDevMode ? [undefined, { debugName: "contentContainerBreakpoints" }] : []));
    /**
     * In some scenarios, one may not want the form container to be responsible for the layout relevant
     * `si-container-[xs|...]` classes, but let this be done by a different, nested component, e.g. by a
     * group box. In these cases, the property should be set to true.
     *
     * @defaultValue false
     */
    disableContainerBreakpoints = input(false, { ...(ngDevMode ? { debugName: "disableContainerBreakpoints" } : {}), transform: booleanAttribute });
    /**
     * Every validation error has an errorCode. This map holds translate keys for error codes. The keys can
     * be used to display a translated message for each validation error. The defaults old english readable
     * key defaults for the Angular standard validators of the `Validators` class like `min`, `max` or `required`.
     *
     * Use the input to set your own translate keys for the form validators you need.
     */
    errorCodeTranslateKeyMap = input(...(ngDevMode ? [undefined, { debugName: "errorCodeTranslateKeyMap" }] : []));
    /**
     * A map the maps from control names of the form to their translate keys.
     * The initial map is empty and the user is responsible to add the required
     * translate keys.
     *
     * @defaultValue
     * ```
     * new Map<string, string>()
     * ```
     */
    controlNameTranslateKeyMap = input(new Map(), ...(ngDevMode ? [{ debugName: "controlNameTranslateKeyMap" }] : []));
    /**
     * Disables the automatic error printing in all nested {@link SiFormItemComponent}. Error printing will be enabled by default in v46.
     *
     * @defaultValue false
     */
    disableErrorPrinting = input(false, { ...(ngDevMode ? { debugName: "disableErrorPrinting" } : {}), transform: booleanAttribute });
    /**
     * A custom width value to be applied to all labels.
     *
     * @example labelWidth="100px".
     */
    labelWidth = input(...(ngDevMode ? [undefined, { debugName: "labelWidth" }] : []));
    hasParentContainer = !!inject(SiFormContainerComponent, {
        optional: true,
        skipSelf: true
    });
    /** @internal */
    formErrorMapper = computed(() => {
        const map = this.errorCodeTranslateKeyMap();
        let customMapper;
        if (map instanceof Map) {
            customMapper = Object.fromEntries(map.entries());
        }
        else if (map) {
            customMapper = map;
        }
        return {
            ...customMapper
        };
    }, ...(ngDevMode ? [{ debugName: "formErrorMapper" }] : []));
    /**
     * Indicates whether the user interacted with the form.
     * @returns `true`, if the user selected at least one form element and
     * [blurred]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/blur_event} by losing focus again
     * (e.g. by setting focus on another element), or by editing the content of a form element.
     * Otherwise `false`.
     */
    get userInteractedWithForm() {
        const form = this.form();
        return !!form && (form.dirty || form.touched);
    }
    /**
     * Indicates whether content editor message shall be styled as success.
     * @returns `true`, if {@link SiFormContainerComponent.userInteractedWithForm} is true and
     * the form is valid.
     */
    get validFormContainerMessage() {
        return this.userInteractedWithForm && this.form().status === 'VALID';
    }
    /**
     * Indicates whether the content editor message shall be styled as warning.
     * @returns `true`, if {@link SiFormContainerComponent.userInteractedWithForm} is true and
     * the form is invalid.
     */
    get invalidFormContainerMessage() {
        return this.userInteractedWithForm && this.form().status === 'INVALID';
    }
    getControlNameTranslateKey(controlName) {
        const controlNameTranslateKeyMap = this.controlNameTranslateKeyMap();
        if (!controlNameTranslateKeyMap) {
            return undefined;
        }
        return controlNameTranslateKeyMap.get(controlName) ?? controlName;
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiFormContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
    static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: SiFormContainerComponent, isStandalone: true, selector: "si-form-container", inputs: { form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, contentContainerBreakpoints: { classPropertyName: "contentContainerBreakpoints", publicName: "contentContainerBreakpoints", isSignal: true, isRequired: false, transformFunction: null }, disableContainerBreakpoints: { classPropertyName: "disableContainerBreakpoints", publicName: "disableContainerBreakpoints", isSignal: true, isRequired: false, transformFunction: null }, errorCodeTranslateKeyMap: { classPropertyName: "errorCodeTranslateKeyMap", publicName: "errorCodeTranslateKeyMap", isSignal: true, isRequired: false, transformFunction: null }, controlNameTranslateKeyMap: { classPropertyName: "controlNameTranslateKeyMap", publicName: "controlNameTranslateKeyMap", isSignal: true, isRequired: false, transformFunction: null }, disableErrorPrinting: { classPropertyName: "disableErrorPrinting", publicName: "disableErrorPrinting", isSignal: true, isRequired: false, transformFunction: null }, labelWidth: { classPropertyName: "labelWidth", publicName: "labelWidth", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style.--si-form-label-width": "labelWidth()" } }, ngImport: i0, template: "<div class=\"d-flex flex-column align-content-stretch flex-grow-1 flex-shrink-1\">\n  @if (disableContainerBreakpoints() || hasParentContainer) {\n    <div class=\"flex-grow-1 flex-shrink-1\">\n      <ng-container *ngTemplateOutlet=\"contentTemplate\" />\n    </div>\n  } @else {\n    <div\n      class=\"flex-grow-1 flex-shrink-1\"\n      siResponsiveContainer\n      [breakpoints]=\"contentContainerBreakpoints()\"\n    >\n      <ng-container *ngTemplateOutlet=\"contentTemplate\" />\n    </div>\n  }\n\n  @if (!readonly()) {\n    <div class=\"d-flex flex-row flex-nowrap flex-grow-0 flex-shrink-0 py-4\">\n      <div\n        class=\"d-flex flex-grow-1 flex-shrink-1 justify-content-end text-end overflow-hidden mx-6 my-auto\"\n        [class.text-success]=\"validFormContainerMessage\"\n        [class.text-danger]=\"invalidFormContainerMessage\"\n      >\n        <ng-content select=\"[si-form-container-message]\" />\n      </div>\n\n      <div class=\"m-auto flex-grow-0 flex-shrink-0\">\n        <ng-content select=\"[si-form-container-buttons]\" />\n      </div>\n    </div>\n  }\n</div>\n\n<ng-template #contentTemplate>\n  <ng-content select=\"[si-form-container-content]\" />\n</ng-template>\n", styles: [":host{display:flex;flex:1 1 auto;flex-direction:column}:host ::slotted([si-form-container-message]),:host ::ng-deep [si-form-container-message]{max-inline-size:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.form-container-edit-help{text-decoration:underline;cursor:pointer}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: SiResponsiveContainerDirective, selector: "[siResponsiveContainer]", inputs: ["resizeThrottle", "breakpoints"], exportAs: ["siResponsiveContainer"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiFormContainerComponent, decorators: [{
            type: Component,
            args: [{ selector: 'si-form-container', imports: [NgTemplateOutlet, SiResponsiveContainerDirective], host: {
                        '[style.--si-form-label-width]': 'labelWidth()'
                    }, template: "<div class=\"d-flex flex-column align-content-stretch flex-grow-1 flex-shrink-1\">\n  @if (disableContainerBreakpoints() || hasParentContainer) {\n    <div class=\"flex-grow-1 flex-shrink-1\">\n      <ng-container *ngTemplateOutlet=\"contentTemplate\" />\n    </div>\n  } @else {\n    <div\n      class=\"flex-grow-1 flex-shrink-1\"\n      siResponsiveContainer\n      [breakpoints]=\"contentContainerBreakpoints()\"\n    >\n      <ng-container *ngTemplateOutlet=\"contentTemplate\" />\n    </div>\n  }\n\n  @if (!readonly()) {\n    <div class=\"d-flex flex-row flex-nowrap flex-grow-0 flex-shrink-0 py-4\">\n      <div\n        class=\"d-flex flex-grow-1 flex-shrink-1 justify-content-end text-end overflow-hidden mx-6 my-auto\"\n        [class.text-success]=\"validFormContainerMessage\"\n        [class.text-danger]=\"invalidFormContainerMessage\"\n      >\n        <ng-content select=\"[si-form-container-message]\" />\n      </div>\n\n      <div class=\"m-auto flex-grow-0 flex-shrink-0\">\n        <ng-content select=\"[si-form-container-buttons]\" />\n      </div>\n    </div>\n  }\n</div>\n\n<ng-template #contentTemplate>\n  <ng-content select=\"[si-form-container-content]\" />\n</ng-template>\n", styles: [":host{display:flex;flex:1 1 auto;flex-direction:column}:host ::slotted([si-form-container-message]),:host ::ng-deep [si-form-container-message]{max-inline-size:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.form-container-edit-help{text-decoration:underline;cursor:pointer}\n"] }]
        }], propDecorators: { form: [{ type: i0.Input, args: [{ isSignal: true, alias: "form", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], contentContainerBreakpoints: [{ type: i0.Input, args: [{ isSignal: true, alias: "contentContainerBreakpoints", required: false }] }], disableContainerBreakpoints: [{ type: i0.Input, args: [{ isSignal: true, alias: "disableContainerBreakpoints", required: false }] }], errorCodeTranslateKeyMap: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorCodeTranslateKeyMap", required: false }] }], controlNameTranslateKeyMap: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlNameTranslateKeyMap", required: false }] }], disableErrorPrinting: [{ type: i0.Input, args: [{ isSignal: true, alias: "disableErrorPrinting", required: false }] }], labelWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelWidth", required: false }] }] } });

/**
 * Copyright (c) Siemens 2016 - 2026
 * SPDX-License-Identifier: MIT
 */
class SiFormFieldsetComponent {
    static labelIdCounter = 0;
    /** The label for the entire fieldset. */
    label = input.required(...(ngDevMode ? [{ debugName: "label" }] : []));
    /** Overrides the parent label width. */
    labelWidth = input(...(ngDevMode ? [undefined, { debugName: "labelWidth" }] : []));
    /**
     * Adds a required marker to the label
     *
     * @defaultValue false
     */
    required = input(false, { ...(ngDevMode ? { debugName: "required" } : {}), transform: booleanAttribute });
    /**
     * Switches all child inputs to inline mode
     *
     * @defaultValue false
     */
    inline = input(false, { ...(ngDevMode ? { debugName: "inline" } : {}), transform: booleanAttribute });
    formItems = signal([], ...(ngDevMode ? [{ debugName: "formItems" }] : []));
    /** @internal */
    hasOnlyRadios = computed(() => {
        // Check if the fieldset only contains radio buttons.
        // We can safely assume that, if all items have the same control name and if there are at least 2 items.
        const items = this.formItems();
        if (items.length > 1) {
            const first = items[0];
            return items.every(item => item.ngControl()?.name === first.ngControl()?.name);
        }
        return false;
    }, ...(ngDevMode ? [{ debugName: "hasOnlyRadios" }] : []));
    errors = computed(() => 
    // All errors should be the same for radios, so we just take the first.
    this.hasOnlyRadios() ? this.formItems()[0].errors() : [], ...(ngDevMode ? [{ debugName: "errors" }] : []));
    touched = signal(false, ...(ngDevMode ? [{ debugName: "touched" }] : []));
    isRequired = computed(() => this.required() || (this.hasOnlyRadios() && this.formItems().every(item => item.required())), ...(ngDevMode ? [{ debugName: "isRequired" }] : []));
    labelId = `__si-form-fieldset-label-${SiFormFieldsetComponent.labelIdCounter++}`;
    ngDoCheck() {
        this.touched.set(this.formItems().some(item => item.ngControl()?.touched));
    }
    /** @internal */
    registerFormItem(item) {
        this.formItems.update(items => [...items, item]);
    }
    /** @internal */
    unregisterFormItem(item) {
        this.formItems.update(items => items.filter(i => i !== item));
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiFormFieldsetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
    static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: SiFormFieldsetComponent, isStandalone: true, selector: "si-form-fieldset", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, labelWidth: { classPropertyName: "labelWidth", publicName: "labelWidth", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, inline: { classPropertyName: "inline", publicName: "inline", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "group" }, properties: { "style.--si-form-label-width": "labelWidth()", "attr.aria-labelledby": "this.labelId" }, classAttribute: "si-form-input" }, ngImport: i0, template: "<div class=\"form-label d-flex gap-2\">\n  <span [class.required]=\"isRequired()\" [id]=\"labelId\">{{ label() | translate }}</span>\n  <ng-content select=\"[si-help-button]\" />\n</div>\n<div>\n  <ng-content />\n  @if (hasOnlyRadios()) {\n    <div class=\"invalid-feedback\" [class.d-block]=\"touched()\">\n      @for (error of errors(); track error) {\n        <div>\n          {{ error.message | translate: error.params }}\n        </div>\n      }\n    </div>\n  }\n</div>\n", styles: [":host.si-form-input{display:flex;flex-direction:column;margin-block-end:12px}:host.si-form-input .form-item-content{flex-grow:1}.form-label:empty{display:none}:host(.form-check):not(.form-check-inline){display:block}:host(.form-check):has(.form-check){padding-block:0}:host-context(.si-container-sm) :host.si-form-input,:host-context(.si-container-md) :host.si-form-input,:host-context(.si-container-lg) :host.si-form-input,:host-context(.si-container-xl) :host.si-form-input,:host-context(.si-container-xxl) :host.si-form-input{display:flex;flex-direction:row;align-items:center}:host-context(.si-container-sm) .form-label,:host-context(.si-container-md) .form-label,:host-context(.si-container-lg) .form-label,:host-context(.si-container-xl) .form-label,:host-context(.si-container-xxl) .form-label{align-self:start;padding-block:8px;padding-inline:16px;inline-size:var(--si-form-label-width, 16.6666666667%);line-height:1.1428571429}:host-context(.si-container-sm) .form-label:empty,:host-context(.si-container-md) .form-label:empty,:host-context(.si-container-lg) .form-label:empty,:host-context(.si-container-xl) .form-label:empty,:host-context(.si-container-xxl) .form-label:empty{display:block}:host-context(.si-container-sm) :host-context(si-form-fieldset) .form-label,:host-context(.si-container-md) :host-context(si-form-fieldset) .form-label,:host-context(.si-container-lg) :host-context(si-form-fieldset) .form-label,:host-context(.si-container-xl) :host-context(si-form-fieldset) .form-label,:host-context(.si-container-xxl) :host-context(si-form-fieldset) .form-label{padding-block:2px 0}:host-context(.si-container-sm) :host(.form-check) .form-label,:host-context(.si-container-md) :host(.form-check) .form-label,:host-context(.si-container-lg) :host(.form-check) .form-label,:host-context(.si-container-xl) :host(.form-check) .form-label,:host-context(.si-container-xxl) :host(.form-check) .form-label{padding-block:2px}\n"], dependencies: [{ kind: "pipe", type: SiTranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiFormFieldsetComponent, decorators: [{
            type: Component,
            args: [{ selector: 'si-form-fieldset', imports: [SiTranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, host: {
                        role: 'group',
                        class: 'si-form-input',
                        '[style.--si-form-label-width]': 'labelWidth()'
                    }, template: "<div class=\"form-label d-flex gap-2\">\n  <span [class.required]=\"isRequired()\" [id]=\"labelId\">{{ label() | translate }}</span>\n  <ng-content select=\"[si-help-button]\" />\n</div>\n<div>\n  <ng-content />\n  @if (hasOnlyRadios()) {\n    <div class=\"invalid-feedback\" [class.d-block]=\"touched()\">\n      @for (error of errors(); track error) {\n        <div>\n          {{ error.message | translate: error.params }}\n        </div>\n      }\n    </div>\n  }\n</div>\n", styles: [":host.si-form-input{display:flex;flex-direction:column;margin-block-end:12px}:host.si-form-input .form-item-content{flex-grow:1}.form-label:empty{display:none}:host(.form-check):not(.form-check-inline){display:block}:host(.form-check):has(.form-check){padding-block:0}:host-context(.si-container-sm) :host.si-form-input,:host-context(.si-container-md) :host.si-form-input,:host-context(.si-container-lg) :host.si-form-input,:host-context(.si-container-xl) :host.si-form-input,:host-context(.si-container-xxl) :host.si-form-input{display:flex;flex-direction:row;align-items:center}:host-context(.si-container-sm) .form-label,:host-context(.si-container-md) .form-label,:host-context(.si-container-lg) .form-label,:host-context(.si-container-xl) .form-label,:host-context(.si-container-xxl) .form-label{align-self:start;padding-block:8px;padding-inline:16px;inline-size:var(--si-form-label-width, 16.6666666667%);line-height:1.1428571429}:host-context(.si-container-sm) .form-label:empty,:host-context(.si-container-md) .form-label:empty,:host-context(.si-container-lg) .form-label:empty,:host-context(.si-container-xl) .form-label:empty,:host-context(.si-container-xxl) .form-label:empty{display:block}:host-context(.si-container-sm) :host-context(si-form-fieldset) .form-label,:host-context(.si-container-md) :host-context(si-form-fieldset) .form-label,:host-context(.si-container-lg) :host-context(si-form-fieldset) .form-label,:host-context(.si-container-xl) :host-context(si-form-fieldset) .form-label,:host-context(.si-container-xxl) :host-context(si-form-fieldset) .form-label{padding-block:2px 0}:host-context(.si-container-sm) :host(.form-check) .form-label,:host-context(.si-container-md) :host(.form-check) .form-label,:host-context(.si-container-lg) :host(.form-check) .form-label,:host-context(.si-container-xl) :host(.form-check) .form-label,:host-context(.si-container-xxl) :host(.form-check) .form-label{padding-block:2px}\n"] }]
        }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], labelWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelWidth", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], inline: [{ type: i0.Input, args: [{ isSignal: true, alias: "inline", required: false }] }], labelId: [{
                type: HostBinding,
                args: ['attr.aria-labelledby']
            }] } });

/**
 * Copyright (c) Siemens 2016 - 2026
 * SPDX-License-Identifier: MIT
 */
const SI_FORM_ITEM_CONTROL = new InjectionToken('si.form-item.control');

/**
 * Copyright (c) Siemens 2016 - 2026
 * SPDX-License-Identifier: MIT
 */
/**
 * Creates the map of default error resolver.
 * This is a function as $localize requires an injection context.
 *
 * @internal
 */
const buildDefaults = () => ({
    // Phone number specific
    notSupportedPhoneNumberCountry: t(() => $localize `:@@SI_FORM_CONTAINER.ERROR.PHONE_COUNTRY:Unsupported country/region code`),
    invalidPhoneNumberFormat: t(() => $localize `:@@SI_FORM_CONTAINER.ERROR.INVALID_PHONE:Invalid phone number`),
    // IP address specific
    ipv4Address: t(() => $localize `:@@SI_FORM_CONTAINER.ERROR.IPV4:Invalid IPv4 address`),
    ipv6Address: t(() => $localize `:@@SI_FORM_CONTAINER.ERROR.IPV6:Invalid IPv6 address`),
    // Min / max
    max: t(() => $localize `:@@SI_FORM_CONTAINER.ERROR.MAX:Max. {{max}}`),
    min: t(() => $localize `:@@SI_FORM_CONTAINER.ERROR.MIN:Min. {{min}}`),
    maxlength: t(() => $localize `:@@SI_FORM_CONTAINER.ERROR.MAX_LENGTH:Max. {{requiredLength}} characters`),
    minlength: t(() => $localize `:@@SI_FORM_CONTAINER.ERROR.MIN_LENGTH:Min. {{requiredLength}} characters`),
    // Date specific
    dateFormat: t(() => $localize `:@@SI_FORM_CONTAINER.ERROR.DATE_FORMAT:Invalid date`),
    endBeforeStart: t(() => $localize `:@@SI_FORM_CONTAINER.ERROR.END_BEFORE_START:End date before start date`),
    invalidEndDateFormat: t(() => $localize `:@@SI_FORM_CONTAINER.ERROR.DATE_FORMAT_END:Invalid end date`),
    invalidStartDateFormat: t(() => $localize `:@@SI_FORM_CONTAINER.ERROR.DATE_FORMAT_START:Invalid start date`),
    maxDate: t(() => $localize `:@@SI_FORM_CONTAINER.ERROR.MAX_DATE:Date prior to {{maxString}} required`),
    minDate: t(() => $localize `:@@SI_FORM_CONTAINER.ERROR.MIN_DATE:Date after {{minString}} required`),
    maxTime: t(() => $localize `:@@SI_FORM_CONTAINER.ERROR.MAX_TIME:Time prior to {{maxString}} required`),
    minTime: t(() => $localize `:@@SI_FORM_CONTAINER.ERROR.MIN_TIME:Time after {{minString}} required`),
    rangeAfterMaxDate: t(() => $localize `:@@SI_FORM_CONTAINER.ERROR.RANGE_AFTER_MAX_DATE:Period prior to {{maxString}} required`),
    rangeBeforeMinDate: t(() => $localize `:@@SI_FORM_CONTAINER.ERROR.RANGE_BEFORE_MIN_DATE:Period after {{minString}} required`),
    // Time units
    hours: t(() => $localize `:@@SI_FORM_CONTAINER.ERROR.HOURS:Integer between 0 and {{max}} required`),
    minutes: t(() => $localize `:@@SI_FORM_CONTAINER.ERROR.MINUTES:Integer between 0 and 59 required`),
    seconds: t(() => $localize `:@@SI_FORM_CONTAINER.ERROR.SECONDS:Integer between 0 and 59 required`),
    milliseconds: t(() => $localize `:@@SI_FORM_CONTAINER.ERROR.MILLISECONDS:Integer between 0 and 999 required`),
    // Various
    email: t(() => $localize `:@@SI_FORM_CONTAINER.ERROR.EMAIL:Invalid email address`),
    numberFormat: t(() => $localize `:@@SI_FORM_CONTAINER.ERROR.NUMBER_FORMAT:Number required`),
    pattern: t(() => $localize `:@@SI_FORM_CONTAINER.ERROR.PATTERN:The value does not match the predefined pattern.`),
    required: t(() => $localize `:@@SI_FORM_CONTAINER.ERROR.REQUIRED:Required`),
    requiredTrue: t(() => $localize `:@@SI_FORM_CONTAINER.ERROR.REQUIRED_TRUE:Required`)
});
/**
 * A service to resolve the validation error of an Angular control to a message or translation key.
 *
 * It can be provided using {@link provideFormValidationErrorMapper}.
 * If not provided, there will be a default instance in the root injector using only the default keys.
 *
 * There can be multiple instances to support providing in a lazy loaded module.
 * If the service cannot find a message, it will try to resolve it using a parent service if available.
 *
 * @internal
 */
class SiFormValidationErrorService {
    errorMapper;
    parent = inject(SiFormValidationErrorService, {
        optional: true,
        skipSelf: true
    });
    // eslint-disable-next-line @angular-eslint/prefer-inject
    constructor(errorMapper) {
        this.errorMapper = errorMapper;
    }
    /**
     * Resolves the provided form errors to a list of {@link SiFormError}.
     * To resolution order is:
     * 1. componentMapper
     * 2. containerMapper using the controlName
     * 3. containerMapper without controlName
     * 4. errorMapper of the service using the controlName
     * 5. errorMapper of the service
     * 6: parent service
     */
    resolveErrors(controlName, errors, componentMapper, containerMapper) {
        if (!errors) {
            return [];
        }
        /*
         * Converts the angular error object (like: {required: true, minLength: {actualLength: 1, requiredLength: 3}})
         * to SiFormError[].
         * Therefore, the error key is look up in the error mappers.
         * If it can be resolved, the error will be printed using the resolved text.
         */
        return Object.entries(errors).map(([key, params]) => this.resolveError(key, controlName, params, componentMapper, containerMapper));
    }
    resolveError(key, controlName, params, componentMapper, containerMapper) {
        return (this.resolveMessage(key, controlName, params, componentMapper) ??
            this.resolveMessage(key, controlName, params, containerMapper) ??
            this.resolveMessage(key, controlName, params, this.errorMapper) ??
            this.parent?.resolveError(key, controlName, params) ?? { key, params });
    }
    resolveMessage(key, controlName, params, mapper) {
        if (!mapper) {
            return undefined;
        }
        const resolver = mapper[`${controlName}.${key}`] ?? mapper[key];
        if (resolver) {
            const message = typeof resolver === 'function' ? resolver(params) : resolver;
            return message ? { key, message, params } : undefined;
        }
        return undefined;
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiFormValidationErrorService, deps: "invalid", target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiFormValidationErrorService, providedIn: 'root', useFactory: () => new SiFormValidationErrorService(buildDefaults()) });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiFormValidationErrorService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                    useFactory: () => new SiFormValidationErrorService(buildDefaults())
                }]
        }], ctorParameters: () => [{ type: undefined }] });

/**
 * This will be used by the `si-form-item` to create a form item control if none is provided.
 * This is typically the case for native form controls like `input`, `textarea` and `select`.
 * It allows accessing the control's id and type.
 */
class SiFormFieldNativeControl {
    element;
    static idCounter = 0;
    static supportedTypes = ['INPUT', 'TEXTAREA', 'SELECT', 'METER', 'PROGRESS'];
    static createForElementRef(element) {
        if (!element) {
            return undefined;
        }
        if (!SiFormFieldNativeControl.supportedTypes.includes(element.nativeElement.tagName)) {
            return undefined;
        }
        return new SiFormFieldNativeControl(element.nativeElement);
    }
    constructor(element) {
        this.element = element;
        if (!element.id) {
            element.id = `__si-form-field-native-control-${SiFormFieldNativeControl.idCounter++}`;
        }
        this.errormessageId = `${this.id}-errormessage`;
        if (element instanceof HTMLInputElement) {
            this.isFormCheck = element.type === 'checkbox' || element.type === 'radio';
        }
        else {
            this.isFormCheck = false;
        }
        element.setAttribute('aria-describedby', this.errormessageId);
    }
    isFormCheck;
    get id() {
        return this.element?.id;
    }
    set id(value) {
        this.element.id = value;
    }
    errormessageId;
}

/**
 * Copyright (c) Siemens 2016 - 2026
 * SPDX-License-Identifier: MIT
 */
class SiFormItemComponent {
    /**
     * The label to be displayed in the form item.
     * It will be translated if a translation key is available.
     */
    label = input.required(...(ngDevMode ? [{ debugName: "label" }] : []));
    /**
     * A custom width value to be applied to the label.
     * A value of 0 is allowed as well to visually hide away the label area.
     *
     * Numbers will be converted to pixels.
     * Using numbers is discouraged and might be dropped.
     *
     * @example labelWidth="100px"
     */
    labelWidth = input(...(ngDevMode ? [undefined, { debugName: "labelWidth" }] : []));
    /**
     * Disables the automatic error printing. Error printing will be enabled by default in v46.
     *
     * @defaultValue false
     */
    disableErrorPrinting = input(false, { ...(ngDevMode ? { debugName: "disableErrorPrinting" } : {}), transform: booleanAttribute });
    formErrorMapper = input(...(ngDevMode ? [undefined, { debugName: "formErrorMapper" }] : []));
    /**
     * Defines that this form item is required for the overall form to be valid.
     *
     * @defaultValue false
     */
    // eslint-disable-next-line @angular-eslint/no-input-rename
    requiredInput = input(false, { ...(ngDevMode ? { debugName: "requiredInput" } : {}), transform: booleanAttribute, alias: 'required' });
    fieldControlQuery = contentChild(SI_FORM_ITEM_CONTROL, { ...(ngDevMode ? { debugName: "fieldControlQuery" } : {}), descendants: true });
    /** @internal */
    ngControl = contentChild(NgControl, { ...(ngDevMode ? { debugName: "ngControl" } : {}), descendants: true });
    controlElementRef = contentChild(NgControl, { ...(ngDevMode ? { debugName: "controlElementRef" } : {}), read: ElementRef,
        descendants: true });
    requiredValidator = contentChild(RequiredValidator, { ...(ngDevMode ? { debugName: "requiredValidator" } : {}), descendants: true });
    /** @internal */
    errors = signal([], ...(ngDevMode ? [{ debugName: "errors" }] : []));
    fieldset = inject(SiFormFieldsetComponent, { optional: true });
    container = inject(SiFormContainerComponent, { optional: true });
    get formItemId() {
        const fieldControl = this.fieldControl();
        return isSignal(fieldControl?.id) ? (fieldControl.id() ?? null) : (fieldControl?.id ?? null);
    }
    get formItemLabelledBy() {
        const fieldControl = this.fieldControl();
        return isSignal(fieldControl?.labelledby)
            ? (fieldControl.labelledby() ?? null)
            : (fieldControl?.labelledby ?? null);
    }
    get formItemErrormessageId() {
        const fieldControl = this.fieldControl();
        return isSignal(fieldControl?.errormessageId)
            ? (fieldControl?.errormessageId() ?? null)
            : (fieldControl?.errormessageId ?? null);
    }
    fieldControl = computed(() => this.fieldControlQuery() ?? this.fieldControlNative(), ...(ngDevMode ? [{ debugName: "fieldControl" }] : []));
    validationErrorService = inject(SiFormValidationErrorService);
    requiredTestControl = new FormControl('');
    validator;
    previousErrors;
    hasRequiredValidator = signal(false, ...(ngDevMode ? [{ debugName: "hasRequiredValidator" }] : []));
    fieldControlNative = signal(undefined, ...(ngDevMode ? [{ debugName: "fieldControlNative" }] : []));
    labelWidthCssVar = computed(() => {
        const labelWidth = this.labelWidth();
        if (typeof labelWidth === 'number') {
            return `${labelWidth}px`;
        }
        return labelWidth;
    }, ...(ngDevMode ? [{ debugName: "labelWidthCssVar" }] : []));
    printErrors = computed(() => {
        return !this.disableErrorPrinting() && !(this.container?.disableErrorPrinting() ?? false);
    }, ...(ngDevMode ? [{ debugName: "printErrors" }] : []));
    /** @internal */
    required = computed(() => this.requiredInput() || this.hasRequiredValidator(), ...(ngDevMode ? [{ debugName: "required" }] : []));
    ngOnChanges(changes) {
        if (changes.formErrorMapper) {
            this.updateValidationMessages();
        }
    }
    ngOnInit() {
        this.fieldset?.registerFormItem(this);
    }
    ngAfterContentInit() {
        if (!this.fieldControlQuery()) {
            this.fieldControlNative.set(SiFormFieldNativeControl.createForElementRef(this.controlElementRef()));
        }
    }
    ngAfterContentChecked() {
        this.updateRequiredState();
        this.updateValidationMessages();
    }
    ngOnDestroy() {
        this.fieldset?.unregisterFormItem(this);
    }
    updateRequiredState() {
        const requiredValidator = this.requiredValidator();
        if (requiredValidator) {
            // the easy case: required validator is applied in the template: <input required>
            this.hasRequiredValidator.set(booleanAttribute(requiredValidator.required));
        }
        else {
            // No required validator is applied via template, it could still be applied in the code.
            // If validators are applied via code, the validator object will change of controls.
            // So we only need to check for required if the validator object was changed.
            const validator = this.ngControl()?.control?.validator;
            if (this.validator !== validator) {
                this.validator = validator;
                this.requiredTestControl.setValidators(this.validator ?? null);
                this.requiredTestControl.updateValueAndValidity();
                this.hasRequiredValidator.set(this.requiredTestControl.errors?.required ?? false);
            }
        }
    }
    updateValidationMessages() {
        const control = this.ngControl();
        const currentErrors = control?.errors;
        if (this.previousErrors !== currentErrors) {
            if (control && this.printErrors()) {
                this.errors.set(this.validationErrorService
                    .resolveErrors(control.name, control.errors, this.formErrorMapper(), this.container?.formErrorMapper())
                    .filter(error => error.message));
                this.previousErrors = currentErrors;
            }
            else if (this.errors().length) {
                this.errors.set([]);
            }
        }
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiFormItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
    static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: SiFormItemComponent, isStandalone: true, selector: "si-form-item", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, labelWidth: { classPropertyName: "labelWidth", publicName: "labelWidth", isSignal: true, isRequired: false, transformFunction: null }, disableErrorPrinting: { classPropertyName: "disableErrorPrinting", publicName: "disableErrorPrinting", isSignal: true, isRequired: false, transformFunction: null }, formErrorMapper: { classPropertyName: "formErrorMapper", publicName: "formErrorMapper", isSignal: true, isRequired: false, transformFunction: null }, requiredInput: { classPropertyName: "requiredInput", publicName: "required", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.required": "required()", "style.--si-form-label-width": "labelWidthCssVar()", "class.form-check": "fieldControl()?.isFormCheck", "class.form-check-inline": "fieldset?.inline()", "class.si-form-input": "!fieldset" } }, queries: [{ propertyName: "fieldControlQuery", first: true, predicate: SI_FORM_ITEM_CONTROL, descendants: true, isSignal: true }, { propertyName: "ngControl", first: true, predicate: NgControl, descendants: true, isSignal: true }, { propertyName: "controlElementRef", first: true, predicate: NgControl, descendants: true, read: ElementRef, isSignal: true }, { propertyName: "requiredValidator", first: true, predicate: RequiredValidator, descendants: true, isSignal: true }], usesOnChanges: true, ngImport: i0, template: "<!--Block the space if no form-field is provided. Otherwise it looks weird in the formly field inline representation.-->\n@if (fieldControl()?.isFormCheck && !fieldset) {\n  <div class=\"form-label\"></div>\n}\n@if (fieldControl()?.isFormCheck) {\n  <div class=\"form-item-content\">\n    <ng-container [ngTemplateOutlet]=\"content\" />\n    <ng-container [ngTemplateOutlet]=\"labelTemplate\" />\n    <ng-container [ngTemplateOutlet]=\"feedbackTemplate\" />\n  </div>\n} @else {\n  <ng-container [ngTemplateOutlet]=\"labelTemplate\" />\n  <div class=\"form-item-content\">\n    <ng-container [ngTemplateOutlet]=\"content\" />\n    <ng-container [ngTemplateOutlet]=\"feedbackTemplate\" />\n  </div>\n}\n\n<ng-template #labelTemplate>\n  @if (label()) {\n    <div\n      class=\"d-flex gap-2\"\n      [class.form-label]=\"!fieldControl()?.isFormCheck\"\n      [class.form-check-label]=\"fieldControl()?.isFormCheck\"\n    >\n      @let labeledBy = formItemLabelledBy;\n      @let required = this.required() && !this.fieldset?.hasOnlyRadios();\n      @if (labeledBy) {\n        <span [id]=\"labeledBy\" [class.required]=\"required\">{{ label()! | translate }}</span>\n      } @else {\n        <label [for]=\"formItemId\" [class.required]=\"required\">{{ label()! | translate }}</label>\n      }\n      <ng-content select=\"[si-help-button]\" />\n    </div>\n  }\n</ng-template>\n<ng-template #feedbackTemplate>\n  @if (printErrors() && !fieldset?.hasOnlyRadios()) {\n    <div class=\"invalid-feedback\" [attr.id]=\"formItemErrormessageId\">\n      @for (error of errors(); track error.key) {\n        <div>\n          {{ error.message | translate: error.params }}\n        </div>\n      }\n    </div>\n  }\n  <ng-content select=\".warning-feedback, .info-feedback, .invalid-feedback\" />\n</ng-template>\n<ng-template #content>\n  <ng-content />\n</ng-template>\n", styles: [":host.si-form-input{display:flex;flex-direction:column;margin-block-end:12px}:host.si-form-input .form-item-content{flex-grow:1}.form-label:empty{display:none}:host(.form-check):not(.form-check-inline){display:block}:host(.form-check):has(.form-check){padding-block:0}:host-context(.si-container-sm) :host.si-form-input,:host-context(.si-container-md) :host.si-form-input,:host-context(.si-container-lg) :host.si-form-input,:host-context(.si-container-xl) :host.si-form-input,:host-context(.si-container-xxl) :host.si-form-input{display:flex;flex-direction:row;align-items:center}:host-context(.si-container-sm) .form-label,:host-context(.si-container-md) .form-label,:host-context(.si-container-lg) .form-label,:host-context(.si-container-xl) .form-label,:host-context(.si-container-xxl) .form-label{align-self:start;padding-block:8px;padding-inline:16px;inline-size:var(--si-form-label-width, 16.6666666667%);line-height:1.1428571429}:host-context(.si-container-sm) .form-label:empty,:host-context(.si-container-md) .form-label:empty,:host-context(.si-container-lg) .form-label:empty,:host-context(.si-container-xl) .form-label:empty,:host-context(.si-container-xxl) .form-label:empty{display:block}:host-context(.si-container-sm) :host-context(si-form-fieldset) .form-label,:host-context(.si-container-md) :host-context(si-form-fieldset) .form-label,:host-context(.si-container-lg) :host-context(si-form-fieldset) .form-label,:host-context(.si-container-xl) :host-context(si-form-fieldset) .form-label,:host-context(.si-container-xxl) :host-context(si-form-fieldset) .form-label{padding-block:2px 0}:host-context(.si-container-sm) :host(.form-check) .form-label,:host-context(.si-container-md) :host(.form-check) .form-label,:host-context(.si-container-lg) :host(.form-check) .form-label,:host-context(.si-container-xl) :host(.form-check) .form-label,:host-context(.si-container-xxl) :host(.form-check) .form-label{padding-block:2px}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: SiTranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiFormItemComponent, decorators: [{
            type: Component,
            args: [{ selector: 'si-form-item', imports: [SiTranslatePipe, NgTemplateOutlet], changeDetection: ChangeDetectionStrategy.OnPush, host: {
                        '[class.required]': 'required()',
                        '[style.--si-form-label-width]': 'labelWidthCssVar()',
                        '[class.form-check]': 'fieldControl()?.isFormCheck',
                        '[class.form-check-inline]': 'fieldset?.inline()',
                        '[class.si-form-input]': '!fieldset'
                    }, template: "<!--Block the space if no form-field is provided. Otherwise it looks weird in the formly field inline representation.-->\n@if (fieldControl()?.isFormCheck && !fieldset) {\n  <div class=\"form-label\"></div>\n}\n@if (fieldControl()?.isFormCheck) {\n  <div class=\"form-item-content\">\n    <ng-container [ngTemplateOutlet]=\"content\" />\n    <ng-container [ngTemplateOutlet]=\"labelTemplate\" />\n    <ng-container [ngTemplateOutlet]=\"feedbackTemplate\" />\n  </div>\n} @else {\n  <ng-container [ngTemplateOutlet]=\"labelTemplate\" />\n  <div class=\"form-item-content\">\n    <ng-container [ngTemplateOutlet]=\"content\" />\n    <ng-container [ngTemplateOutlet]=\"feedbackTemplate\" />\n  </div>\n}\n\n<ng-template #labelTemplate>\n  @if (label()) {\n    <div\n      class=\"d-flex gap-2\"\n      [class.form-label]=\"!fieldControl()?.isFormCheck\"\n      [class.form-check-label]=\"fieldControl()?.isFormCheck\"\n    >\n      @let labeledBy = formItemLabelledBy;\n      @let required = this.required() && !this.fieldset?.hasOnlyRadios();\n      @if (labeledBy) {\n        <span [id]=\"labeledBy\" [class.required]=\"required\">{{ label()! | translate }}</span>\n      } @else {\n        <label [for]=\"formItemId\" [class.required]=\"required\">{{ label()! | translate }}</label>\n      }\n      <ng-content select=\"[si-help-button]\" />\n    </div>\n  }\n</ng-template>\n<ng-template #feedbackTemplate>\n  @if (printErrors() && !fieldset?.hasOnlyRadios()) {\n    <div class=\"invalid-feedback\" [attr.id]=\"formItemErrormessageId\">\n      @for (error of errors(); track error.key) {\n        <div>\n          {{ error.message | translate: error.params }}\n        </div>\n      }\n    </div>\n  }\n  <ng-content select=\".warning-feedback, .info-feedback, .invalid-feedback\" />\n</ng-template>\n<ng-template #content>\n  <ng-content />\n</ng-template>\n", styles: [":host.si-form-input{display:flex;flex-direction:column;margin-block-end:12px}:host.si-form-input .form-item-content{flex-grow:1}.form-label:empty{display:none}:host(.form-check):not(.form-check-inline){display:block}:host(.form-check):has(.form-check){padding-block:0}:host-context(.si-container-sm) :host.si-form-input,:host-context(.si-container-md) :host.si-form-input,:host-context(.si-container-lg) :host.si-form-input,:host-context(.si-container-xl) :host.si-form-input,:host-context(.si-container-xxl) :host.si-form-input{display:flex;flex-direction:row;align-items:center}:host-context(.si-container-sm) .form-label,:host-context(.si-container-md) .form-label,:host-context(.si-container-lg) .form-label,:host-context(.si-container-xl) .form-label,:host-context(.si-container-xxl) .form-label{align-self:start;padding-block:8px;padding-inline:16px;inline-size:var(--si-form-label-width, 16.6666666667%);line-height:1.1428571429}:host-context(.si-container-sm) .form-label:empty,:host-context(.si-container-md) .form-label:empty,:host-context(.si-container-lg) .form-label:empty,:host-context(.si-container-xl) .form-label:empty,:host-context(.si-container-xxl) .form-label:empty{display:block}:host-context(.si-container-sm) :host-context(si-form-fieldset) .form-label,:host-context(.si-container-md) :host-context(si-form-fieldset) .form-label,:host-context(.si-container-lg) :host-context(si-form-fieldset) .form-label,:host-context(.si-container-xl) :host-context(si-form-fieldset) .form-label,:host-context(.si-container-xxl) :host-context(si-form-fieldset) .form-label{padding-block:2px 0}:host-context(.si-container-sm) :host(.form-check) .form-label,:host-context(.si-container-md) :host(.form-check) .form-label,:host-context(.si-container-lg) :host(.form-check) .form-label,:host-context(.si-container-xl) :host(.form-check) .form-label,:host-context(.si-container-xxl) :host(.form-check) .form-label{padding-block:2px}\n"] }]
        }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], labelWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelWidth", required: false }] }], disableErrorPrinting: [{ type: i0.Input, args: [{ isSignal: true, alias: "disableErrorPrinting", required: false }] }], formErrorMapper: [{ type: i0.Input, args: [{ isSignal: true, alias: "formErrorMapper", required: false }] }], requiredInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], fieldControlQuery: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SI_FORM_ITEM_CONTROL), { ...{ descendants: true }, isSignal: true }] }], ngControl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NgControl), { ...{ descendants: true }, isSignal: true }] }], controlElementRef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NgControl), { ...{
                            read: ElementRef,
                            descendants: true
                        }, isSignal: true }] }], requiredValidator: [{ type: i0.ContentChild, args: [i0.forwardRef(() => RequiredValidator), { ...{ descendants: true }, isSignal: true }] }] } });

/**
 * Copyright (c) Siemens 2016 - 2026
 * SPDX-License-Identifier: MIT
 */
const SI_FORM_VALIDATION_TOOLTIP_DATA = new InjectionToken('SiFormValidationTooltipData');
class SiFormValidationTooltipComponent {
    errors = inject(SI_FORM_VALIDATION_TOOLTIP_DATA);
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiFormValidationTooltipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
    static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: SiFormValidationTooltipComponent, isStandalone: true, selector: "si-form-validation-tooltip", host: { classAttribute: "d-flex flex-column gap-2 text-start" }, ngImport: i0, template: `
    @for (error of errors(); track error.key) {
      <div>{{ error.message | translate: error.params }}</div>
    }
  `, isInline: true, dependencies: [{ kind: "pipe", type: SiTranslatePipe, name: "translate" }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiFormValidationTooltipComponent, decorators: [{
            type: Component,
            args: [{
                    selector: 'si-form-validation-tooltip',
                    imports: [SiTranslatePipe],
                    template: `
    @for (error of errors(); track error.key) {
      <div>{{ error.message | translate: error.params }}</div>
    }
  `,
                    host: {
                        'class': 'd-flex flex-column gap-2 text-start'
                    }
                }]
        }] });

/**
 * Copyright (c) Siemens 2016 - 2026
 * SPDX-License-Identifier: MIT
 */
/**
 * Directive to show a tooltip with validation errors of a form control.
 *
 * In general, `si-form-item` should be used.
 * This directive is intended only for cases where the tooltip cannot be shown inline.
 * This is typically the case for tables and lists where the validation message would break the layout.
 *
 * @example
 * ```html
 * <input siFormValidationTooltip [formControl]="control" />
 * ```
 */
class SiFormValidationTooltipDirective {
    static idCounter = 0;
    tooltipService = inject(SiTooltipService);
    formValidationService = inject(SiFormValidationErrorService);
    formContainer = inject(SiFormContainerComponent, { optional: true });
    formErrorMapper = input(...(ngDevMode ? [undefined, { debugName: "formErrorMapper" }] : []));
    ngControl = inject(NgControl);
    elementRef = inject(ElementRef);
    tooltipRef;
    errors = signal([], ...(ngDevMode ? [{ debugName: "errors" }] : []));
    currentErrors = null;
    touched = null;
    // Use a counter to track how many events are matched that keep the tooltip active.
    // Active means we are listening to error changes.
    activationCount = 0;
    describedBy = `__si-form-validation-tooltip-${SiFormValidationTooltipDirective.idCounter++}`;
    ngDoCheck() {
        if (this.activationCount &&
            (this.currentErrors !== this.ngControl.errors || this.touched !== this.ngControl.touched)) {
            this.currentErrors = this.ngControl.errors;
            this.touched = this.ngControl.touched;
            this.updateErrors();
        }
    }
    ngOnDestroy() {
        this.destroyTooltip();
    }
    increaseActivation() {
        this.activationCount++;
        if (this.activationCount === 1) {
            this.updateErrors();
        }
    }
    updateErrors() {
        const errors = this.formValidationService
            .resolveErrors(this.ngControl.name, this.currentErrors, this.formErrorMapper(), this.formContainer?.formErrorMapper())
            .filter(error => !!error.message);
        if (!this.tooltipRef && errors.length && this.ngControl.touched) {
            this.tooltipRef = this.tooltipService.createTooltip({
                placement: 'auto',
                element: this.elementRef,
                describedBy: this.describedBy,
                injector: Injector.create({
                    providers: [{ provide: SI_FORM_VALIDATION_TOOLTIP_DATA, useValue: this.errors }]
                }),
                tooltip: () => SiFormValidationTooltipComponent,
                tooltipContext: () => undefined
            });
            this.tooltipRef.show();
        }
        else if (this.tooltipRef && (!errors.length || this.ngControl.pristine)) {
            this.destroyTooltip();
        }
        this.errors.set(errors);
    }
    decreaseActivation() {
        this.activationCount--;
        if (!this.activationCount) {
            this.destroyTooltip();
        }
    }
    destroyTooltip() {
        this.tooltipRef?.destroy();
        this.tooltipRef = undefined;
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiFormValidationTooltipDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
    static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.8", type: SiFormValidationTooltipDirective, isStandalone: true, selector: "[siFormValidationTooltip]", inputs: { formErrorMapper: { classPropertyName: "formErrorMapper", publicName: "formErrorMapper", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "focus": "increaseActivation()", "mouseenter": "increaseActivation()", "blur": "decreaseActivation()", "mouseleave": "decreaseActivation()" }, properties: { "attr.aria-describedby": "describedBy" } }, providers: [SiTooltipService], ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiFormValidationTooltipDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[siFormValidationTooltip]',
                    providers: [SiTooltipService],
                    host: {
                        '[attr.aria-describedby]': 'describedBy'
                    }
                }]
        }], propDecorators: { formErrorMapper: [{ type: i0.Input, args: [{ isSignal: true, alias: "formErrorMapper", required: false }] }], increaseActivation: [{
                type: HostListener,
                args: ['focus']
            }, {
                type: HostListener,
                args: ['mouseenter']
            }], decreaseActivation: [{
                type: HostListener,
                args: ['blur']
            }, {
                type: HostListener,
                args: ['mouseleave']
            }] } });

/**
 * The error mapper is used to resolve an angular validation error to a {@link TranslatableString}.
 * It will be merged with already existing error mappers.
 */
const provideFormValidationErrorMapper = (mapper) => ({
    provide: SiFormValidationErrorService,
    // defaults must always be provided in case this is called in app.config
    useFactory: () => new SiFormValidationErrorService({ ...buildDefaults(), ...mapper })
});

/**
 * Copyright (c) Siemens 2016 - 2026
 * SPDX-License-Identifier: MIT
 */
class SiFormModule {
    /**
     * Overrides the default configuration of the form module.
     *
     * @param errorMapper - The error mappers is used to resolve angular validation errors.
     * It will be merged with already existing error mappers.
     */
    // We have the errorMapper wrapped in an object, to allow the addition of more configuration fields without breaking the API.
    static withConfiguration({ validationErrorMapper }) {
        return {
            ngModule: SiFormModule,
            providers: [provideFormValidationErrorMapper(validationErrorMapper)]
        };
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiFormModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
    static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.0.8", ngImport: i0, type: SiFormModule, imports: [SiFormContainerComponent, SiFormFieldsetComponent, SiFormItemComponent], exports: [SiFormContainerComponent, SiFormFieldsetComponent, SiFormItemComponent] });
    static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiFormModule });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiFormModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [SiFormContainerComponent, SiFormFieldsetComponent, SiFormItemComponent],
                    exports: [SiFormContainerComponent, SiFormFieldsetComponent, SiFormItemComponent]
                }]
        }] });

/**
 * Copyright (c) Siemens 2016 - 2026
 * SPDX-License-Identifier: MIT
 */

/**
 * Generated bundle index. Do not edit.
 */

export { SI_FORM_ITEM_CONTROL, SiFormContainerComponent, SiFormFieldsetComponent, SiFormItemComponent, SiFormModule, SiFormValidationTooltipDirective, provideFormValidationErrorMapper };
//# sourceMappingURL=siemens-element-ng-form.mjs.map
