import * as i0 from '@angular/core';
import { inject, ChangeDetectorRef, Input, Directive, Injectable, InjectionToken, isDevMode, ElementRef, DestroyRef, booleanAttribute, ViewChild, Pipe, NgModule } from '@angular/core';
import { Subscription, BehaviorSubject, Subject, isObservable, of, filter } from 'rxjs';
import { takeUntil, map } from 'rxjs/operators';
import { coerceArray } from '@angular/cdk/coercion';
import { NgControl, ControlContainer, NgForm } from '@angular/forms';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { isValidControlState, FD_FORM_FIELD, FD_FORM_FIELD_CONTROL, FormField } from '@fundamental-ngx/cdk/forms';
import * as i1 from '@angular/cdk/layout';

let randomId$1 = 0;
/**
 * This class contains common properties used across components.
 * this can be extended to reduce the code duplication across components.
 * @hidden for form related Base , see BaseInput.
 */
class BaseComponent {
    constructor() {
        /** id for the Element */
        this.id = `fdp-id-${randomId$1++}`;
        /** @hidden */
        this._disabled = false;
        /** @hidden */
        this._subscriptions = new Subscription();
        /**
         * @hidden
         * Change detector ref.
         */
        this._cdr = inject(ChangeDetectorRef);
    }
    /**
     * @deprecated
     * Use `ariaLabelledBy` instead.
     */
    set ariaLabelledby(value) {
        this.ariaDescribedBy = value;
    }
    /** disabled status of the element */
    set disabled(disabled) {
        this._disabled = disabled;
    }
    get disabled() {
        return this._disabled;
    }
    /** @hidden */
    ngOnDestroy() {
        this._subscriptions.unsubscribe();
    }
    /**
     * @hidden
     * For internal usage only
     *
     * Since all components use OnPush strategy in the fundamental lib
     * it's tricky to update a child input directly from a parent component class
     *
     */
    markForCheck() {
        this._cdr.markForCheck();
    }
    /**
     * @hidden
     * For internal usage only
     *
     * Since all components use OnPush strategy in the fundamental lib
     * it's tricky to update a child input directly from a parent component class
     *
     */
    detectChanges() {
        this._cdr.detectChanges();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: BaseComponent, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.0", type: BaseComponent, isStandalone: true, inputs: { ariaLabel: "ariaLabel", ariaLabelledBy: "ariaLabelledBy", ariaLabelledby: "ariaLabelledby", ariaDescribedBy: "ariaDescribedBy", id: "id", name: "name", width: "width", disabled: "disabled" }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: BaseComponent, decorators: [{
            type: Directive
        }], propDecorators: { ariaLabel: [{
                type: Input
            }], ariaLabelledBy: [{
                type: Input
            }], ariaLabelledby: [{
                type: Input
            }], ariaDescribedBy: [{
                type: Input
            }], id: [{
                type: Input
            }], name: [{
                type: Input
            }], width: [{
                type: Input
            }], disabled: [{
                type: Input
            }] } });

/**
 * Platform lib Config
 */
class PlatformConfig {
    /** @hidden */
    static { this.injector = null; }
    /** @hidden */
    static setInjector(injector) {
        PlatformConfig.injector = injector;
    }
    /** @hidden */
    static getInjector() {
        return PlatformConfig.injector;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: PlatformConfig, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: PlatformConfig, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: PlatformConfig, decorators: [{
            type: Injectable,
            args: [{ providedIn: 'root' }]
        }] });

/** @hidden */
function objectValues(obj) {
    return Object.keys(obj).map((key) => obj[key]);
}
/** @hidden */
function objectToName(target) {
    if (isBlank(target) || (!isStringMap(target) && !isType(target))) {
        throw new Error(' Cannot convert. Uknown object');
    }
    return isType(target) ? target.prototype.constructor.name : target.constructor.name;
}
/** @hidden */
function isJsObject(o) {
    return o !== null && (typeof o === 'function' || typeof o === 'object');
}
/** @hidden */
function isPresent(obj) {
    return obj !== undefined && obj !== null;
}
/** @hidden */
function isBlank(obj) {
    return obj === undefined || obj === null;
}
/** @hidden */
function isBoolean(obj) {
    return typeof obj === 'boolean';
}
/** @hidden */
function isNumber(obj) {
    return typeof obj === 'number';
}
/** @hidden */
function isString(obj) {
    return typeof obj === 'string';
}
/** @hidden */
function isFunction(obj) {
    return typeof obj === 'function';
}
/** @hidden */
function isType(obj) {
    return isFunction(obj);
}
/** @hidden */
function isStringMap(obj) {
    return typeof obj === 'object' && obj !== null;
}
/** @hidden */
function isObject(item) {
    return typeof item === 'object' && !Array.isArray(item) && item !== null;
}
/** @hidden */
function isPromise(obj) {
    // allow any Promise/A+ compliant thenable.
    // It's up to the caller to ensure that obj.then conforms to the spec
    return !!obj && isFunction(obj.then);
}
/** @hidden */
function isSubscribable(obj) {
    return !!obj && isFunction(obj.subscribe);
}

/* eslint-disable @typescript-eslint/no-unused-vars */
/**
 *
 * When working with large set of data using components such as:
 *  - Datatables
 *  - InputSearch
 *  - ComboBox (Autocomplete)
 *  - Tree
 *  - List
 *  - etc..
 *  and for these we need to unify the way we access a data. Currently we leave most of the complex
 *  data access or data manipulation on application developer which leads to different
 *  implementation approaches and allot of code duplication.
 *
 *  It is common practice when working with different data input sources we try to apply some
 *  Design patterns e.g. Adapter Pattern that provides us with guidelines how different interfaces
 *  can talk to each other and from here we can derive another pattern so called Datasource pattern
 *  which is high-level architectural pattern to abstract data and internal resources
 *  access (DB calls, Rest calls, domain objects) with a mininal amount a code.
 *
 * What we want to achieve is to define abstract layer where each specific resource type needs to
 * implement and move all the related logic into reusable DataSources. (TableDataSource,
 * ComboBoxDataSource, ... )
 *
 * Observable<T[]> is a reactive stream, that changes its content based on what it pushed
 * into it using .next().
 *
 * Each type of data component such Autocomplete, DataTable, List,.. needs to provide its own
 * specific implementation to handle specific use-case.
 *
 * ```
 *  export abstract class DataTableDataSource<T> implements DataSource<T> {
 *    data: T[];
 *    filterTerm: string;
 *    paginator: Paginator | null
 *
 *    filterPredicate: ((data: T, filter: string) => boolean;
 *
 *    sortData: ((data: T[], sort: Sort) => T[]);
 *
 *    //
 *    create (t: T): T;
 *    update (t: T): T;
 *    remove (t: T): T;
 *
 *  }
 * ```
 *
 * After we define a format we can start creating concrete implementations based on the usage or
 * resource .e.g:
 *
 *
 * ```
 * export abstract class RestAutoCompleteDataSource<T> extend DataChooserDataSource <T> {
 *   // We can use a registry that is able to map a Entity Type = Endpoint
 *   // have identical way how to fetch , search ,...
 * }
 * ```
 *
 * Probably when working with data only locally, it can be:
 *
 * ```
 * export abstract class LocalArrayAutoCompleteDataSource<T> extend DataChooserDataSource <T> {
 * }
 * ```
 *
 *
 */
var MatchingStrategy;
(function (MatchingStrategy) {
    MatchingStrategy["STARTS_WITH_PER_TERM"] = "starts with per term";
    MatchingStrategy["STARTS_WITH"] = "starts with";
    MatchingStrategy["CONTAINS"] = "contains";
})(MatchingStrategy || (MatchingStrategy = {}));
const DATA_PROVIDERS = new InjectionToken('DataProviderRegistry');
/** Matching Strategy: StartsWithPerTerm - Reqexp */
function getMatchingStrategyStartsWithPerTermReqexp(value) {
    // We need to escape all special characters in order not to break the regular expression.
    return new RegExp(`(\\s|^)(${value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
}
/** @hidden */
function isDataSource(value) {
    return value && typeof value.open === 'function';
}
/**
 * Provider is a data driver that can access data and retrieve them. It knows how to get 1
 * or more records, maybe do paging and some other things.
 *
 */
class DataProvider {
    constructor() {
        /** @hidden */
        this._matchingStrategy = MatchingStrategy.STARTS_WITH;
        /** @hidden */
        this._matchingBy = null;
    }
    /**
     * Tells if this DataProvider supports INSERT, REMOVE
     *
     */
    canCRUD() {
        return false;
    }
    // Implement to support CRUD operations.
    /** @hidden */
    getOne(params) {
        throw new Error('Not supported');
    }
    /** @hidden */
    insert(payload, params) {
        throw new Error('Not supported');
    }
    /** @hidden */
    remove(params) {
        throw new Error('Not supported');
    }
    /** @hidden */
    update(payload, params) {
        throw new Error('Not supported');
    }
    /** @hidden */
    setLookupKey(key) {
        this._keyPath = key;
    }
    /** @hidden */
    setMatchingBy(matchingBy) {
        this._matchingBy = matchingBy;
    }
    /** @hidden */
    setMatchingStrategy(strategy) {
        this._matchingStrategy = strategy;
    }
}
class ComboBoxDataSource {
    /** @hidden */
    static { this.MaxLimit = 5; }
    /** @hidden */
    get isDataLoading() {
        return this._dataLoading;
    }
    /** @hidden */
    constructor(dataProvider) {
        this.dataProvider = dataProvider;
        /** @hidden */
        this.limitless = false;
        /** @hidden */
        this._dataChanges = new BehaviorSubject([]);
        /** @hidden */
        this._onDataRequested$ = new Subject();
        /** @hidden */
        this._onDataReceived$ = new Subject();
        /** @hidden */
        this._onDestroy$ = new Subject();
        /** @hidden */
        this._dataLoading = false;
    }
    /** @hidden */
    match(predicate = new Map(), start = 0, end = Infinity) {
        this._onDataRequested$.next();
        this._dataLoading = true;
        const searchParam = new Map();
        if (typeof predicate === 'string') {
            searchParam.set('query', predicate);
        }
        else if (predicate instanceof Map) {
            predicate.forEach((v, k) => searchParam.set(k, v));
        }
        else {
            throw new Error('DataSource.match() predicate can only accepts string and Map');
        }
        if (!searchParam.has('limit') && !this.limitless) {
            searchParam.set('limit', ComboBoxDataSource.MaxLimit);
        }
        this.dataProvider
            .fetch(searchParam, start, end)
            .pipe(takeUntil(this._onDestroy$))
            .subscribe({
            next: (result) => {
                this._onDataReceived$.next();
                this._dataLoading = false;
                this._dataChanges.next(result);
            },
            error: () => {
                this._onDataReceived$.next();
                this._dataLoading = false;
            }
        });
    }
    /** @hidden */
    open() {
        return this._dataChanges.pipe(takeUntil(this._onDestroy$));
    }
    /** @hidden */
    close() {
        this._onDestroy$.next();
    }
    /** @hidden */
    onDataRequested() {
        return this._onDataRequested$.asObservable();
    }
    /** @hidden */
    onDataReceived() {
        return this._onDataReceived$.asObservable();
    }
}
class MultiComboBoxDataSource extends ComboBoxDataSource {
    /** @hidden */
    constructor(dataProvider) {
        super(dataProvider);
        this.dataProvider = dataProvider;
    }
}
class SearchFieldDataSource extends ComboBoxDataSource {
    /** @hidden */
    constructor(dataProvider) {
        super(dataProvider);
        this.dataProvider = dataProvider;
    }
}
class ListDataSource extends ComboBoxDataSource {
    /** @hidden */
    constructor(dataProvider) {
        super(dataProvider);
        this.dataProvider = dataProvider;
        /** @hidden */
        this.limitless = true;
    }
}
class MultiInputDataSource extends ComboBoxDataSource {
    /** @hidden */
    constructor(dataProvider) {
        super(dataProvider);
        this.dataProvider = dataProvider;
    }
}
class ApprovalFlowUserDataSource extends ComboBoxDataSource {
    /** @hidden */
    constructor(dataProvider) {
        super(dataProvider);
        this.dataProvider = dataProvider;
    }
}
class ApprovalFlowTeamDataSource extends ComboBoxDataSource {
    /** @hidden */
    constructor(dataProvider) {
        super(dataProvider);
        this.dataProvider = dataProvider;
    }
}

/**
 * Default implementation for Observable Arrays and Arrays.
 */
/**
 * In Memory implementation of DataProvider that supports fulltext search
 */
class BaseDataProvider extends DataProvider {
    /** @hidden */
    constructor(values) {
        super();
        this.values = values;
    }
    /** @hidden */
    fetch(params) {
        const observable = isObservable(this.values) ? this.values : of(this.values);
        const queryString = params.get('query');
        const limit = params.get('limit') || 50;
        if (!queryString || queryString === '*') {
            return observable.pipe(map((items) => this.withLimit(items, limit)));
        }
        const toLowerPattern = queryString.toLowerCase();
        return observable.pipe(map((items) => {
            const result = [];
            for (let i = 0; i < items.length; i++) {
                const item = items[i];
                if (this.matches(item, toLowerPattern)) {
                    result.push(item);
                    if (result.length >= limit) {
                        break;
                    }
                }
            }
            return result;
        }));
    }
    /**
     * Warning: If you dont supply search Key and you want fulltext search and you use this
     * default implementation be aware that it can  perform poorly as it is naive implementation
     * that does not do deep compare.
     */
    matches(item, pattern) {
        if (!this._matchingBy) {
            return false;
        }
        const { firstBy, secondaryBy } = this._matchingBy;
        let matched = this.matchesBy(item, pattern, firstBy);
        if (!matched && secondaryBy) {
            matched = this.matchesBy(item, pattern, secondaryBy);
        }
        return matched;
    }
    /** @hidden */
    matchesBy(item, pattern, matchingBy) {
        const value = isJsObject(item) && matchingBy ? matchingBy(item) : item;
        if (isFunction(value)) {
            return value.call(item);
        }
        else if (isJsObject(value)) {
            return this.hasObjectValue(item, pattern);
        }
        else if (this._matchingStrategy === MatchingStrategy.STARTS_WITH_PER_TERM) {
            const reqexp = getMatchingStrategyStartsWithPerTermReqexp(pattern);
            return !!pattern && !!value && !!value.match(reqexp);
        }
        else if (this._matchingStrategy === MatchingStrategy.STARTS_WITH) {
            return !!pattern && !!value && value.toString().toLowerCase().startsWith(pattern.toLowerCase());
        }
        else if (this._matchingStrategy === MatchingStrategy.CONTAINS) {
            return !!pattern && !!value && value.toString().toLowerCase().indexOf(pattern) > -1;
        }
        else {
            return !!pattern && !!value && value.toString().toLowerCase() === pattern;
        }
    }
    /** @hidden */
    hasObjectValue(obj, pattern) {
        const values = objectValues(obj);
        const parentObj = objectToName(obj);
        const length2 = values.filter((value) => {
            if (!value || Array.isArray(value)) {
                return false;
            }
            else if (!isJsObject(value) && !isFunction(value)) {
                return value.toString().toLowerCase().indexOf(pattern) !== -1;
            }
            else if (isJsObject(value) && objectToName(value) !== parentObj) {
                return this.hasObjectValue(value, pattern);
            }
            return false;
        }).length;
        return length2 > 0;
    }
    /** @hidden */
    withLimit(data, limit) {
        if (limit && data.length > limit) {
            return data.slice(0, limit);
        }
        return data;
    }
}

/**
 * Default implementation for Arrays.
 */
class ArrayComboBoxDataSource extends ComboBoxDataSource {
    /** @hidden */
    constructor(data) {
        super(new BaseDataProvider(data));
        this.data = data;
    }
}
class ArrayMultiComboBoxDataSource extends MultiComboBoxDataSource {
    /** @hidden */
    constructor(data) {
        super(new BaseDataProvider(data));
        this.data = data;
    }
}
class ArrayListDataSource extends ListDataSource {
    /** @hidden */
    constructor(data) {
        super(new BaseDataProvider(data));
        this.data = data;
    }
}
class ArrayMultiInputDataSource extends MultiInputDataSource {
    /** @hidden */
    constructor(data) {
        super(new BaseDataProvider(data));
        this.data = data;
    }
}

/** @hidden */
function isSelectableItem(item) {
    return (item &&
        item.label !== undefined &&
        item.value !== undefined &&
        Object.prototype.hasOwnProperty.call(item, 'selected'));
}
/** @hidden */
function isSelectItem(item) {
    return item && item.label !== undefined && item.value !== undefined;
}
const isOptionItem = isSelectItem;
const isSelectableOptionItem = isSelectableItem;
/**
 * Wraps the provided value in an array, unless it is an array already.
 * If `null` or `undefined` is received, will return an empty array.
 */
function coerceArraySafe(value) {
    return isBlank(value) ? [] : coerceArray(value);
}

class ObservableComboBoxDataSource extends ComboBoxDataSource {
    /** @hidden */
    constructor(data) {
        super(new BaseDataProvider(data));
        this.data = data;
    }
}
class ObservableMultiComboBoxDataSource extends MultiComboBoxDataSource {
    /** @hidden */
    constructor(data) {
        super(new BaseDataProvider(data));
        this.data = data;
    }
}
class ObservableListDataSource extends ListDataSource {
    /** @hidden */
    constructor(data) {
        super(new BaseDataProvider(data));
        this.data = data;
    }
}
class ObservableMultiInputDataSource extends MultiInputDataSource {
    /** @hidden */
    constructor(data) {
        super(new BaseDataProvider(data));
        this.data = data;
    }
}

const FDP_DO_CHECK = new InjectionToken('FdpInputDoCheckTrigger');
const FDP_FORM_SUBMIT = new InjectionToken('FdpFormSubmitTrigger');
let randomId = 0;
/**
 * All form components share the same information (value, name, placeholder,.. ) as well as
 * the same behavior given by ControlValueAccessor.
 *
 * Even this is not ideal solution there is no other way then use inheritance to reuse some of the
 * common logic. It should be possible to use some kind of compositions with Proxies but something
 * similar that exists in Aspect Oriented Programing.
 *
 * Usually try to fire stateChange only for things that can change dynamically in runtime. We don't expect
 * that e.g. placeholder will change after component is created
 */
class BaseInput extends BaseComponent {
    /**
     *  The state of the form control - applies css classes.
     *  Can be 'success', 'error', 'warning', 'default', 'information'.
     *
     * @default 'default'
     */
    set state(state) {
        if (!state || isValidControlState(state)) {
            this._state = state;
        }
        else if (isDevMode()) {
            console.warn(`Provided value "${state}" is not a valid option for FormStates type`);
        }
    }
    get state() {
        if (this._state) {
            return this._state;
        }
        if (!this.controlInvalid) {
            return 'default';
        }
        return this.formField?.getPriorityState() || 'error';
    }
    /** Whether the input is disabled */
    set disabled(value) {
        this.setDisabledState(value);
    }
    get disabled() {
        if (this.ngControl && this.ngControl.disabled !== null) {
            return this.ngControl.disabled;
        }
        return this._disabled;
    }
    /**
     * Tell the component if we are in editing mode.
     */
    set editable(value) {
        if (this._editable === value) {
            return;
        }
        this._editable = value;
        this.markForCheck();
        this.stateChanges.next('editable');
    }
    get editable() {
        return this._editable;
    }
    /** Whether control has errors */
    get controlInvalid() {
        return this._controlInvalid;
    }
    /** @hidden */
    constructor() {
        /**
         * We do not use Injector.get() approach here because there is a bug
         * with this signature https://github.com/angular/angular/issues/31776
         * where "get()" method doesn't take into account "flag" option"
         *
         */
        super();
        /**
         * To allow user to determine what event he wants to trigger the messages to show
         * Accepts any [HTML DOM Events](https://www.w3schools.com/jsref/dom_obj_event.asp).
         */
        this.triggers = [
            {
                trigger: 'focusin',
                openAction: true,
                closeAction: false
            },
            {
                trigger: 'focusout',
                openAction: false,
                closeAction: true
            }
        ];
        /** Form control validation event strategy. */
        this.validationStrategy = ['touched', 'dirty', 'submitted'];
        /**
         * See @FormFieldControl
         */
        this.focused = false;
        /**
         * See @FormFieldControl
         */
        this.stateChanges = new Subject();
        /** @hidden */
        this.formField = null;
        /** @hidden */
        this._doCheck$ = inject(FDP_DO_CHECK, { optional: true });
        /** Element reference. */
        this.elementRef = inject(ElementRef);
        /** NgControl reference. */
        this.ngControl = inject(NgControl, {
            optional: true,
            self: true
        });
        /** Control container reference. */
        this.controlContainer = inject(ControlContainer, {
            optional: true,
            skipSelf: true
        });
        /** Form reference. */
        this.ngForm = inject(NgForm, {
            optional: true,
            skipSelf: true
        });
        /** @hidden */
        this.defaultId = `fdp-input-id-${randomId++}`;
        /** @hidden */
        this._editable = true;
        /** @hidden */
        this._destroyed = inject(DestroyRef);
        /** @hidden */
        this._externalSubmit = inject(FDP_FORM_SUBMIT, { optional: true });
        /** @hidden */
        this._externalFormSubmitted = false;
        /**
         * @hidden
         */
        this._controlInvalid = false;
        /** @hidden */
        this.onChange = () => { };
        /** @hidden */
        this.onTouched = () => { };
        if (this.ngControl) {
            this.ngControl.valueAccessor = this;
        }
        const formField = inject(FD_FORM_FIELD, {
            optional: true,
            skipSelf: true,
            host: true
        });
        const formControl = inject(FD_FORM_FIELD_CONTROL, {
            optional: true,
            skipSelf: true,
            host: true
        });
        // We have to ignore "formField" if there is "formControl" wrapper
        this.formField = formField && !formControl ? formField : null;
        this._doCheck$?.pipe(takeUntilDestroyed(this._destroyed)).subscribe(() => {
            this.ngDoCheck();
        });
        this._externalSubmit?.pipe(takeUntilDestroyed(this._destroyed)).subscribe(() => {
            this._externalFormSubmitted = true;
        });
    }
    /** @hidden */
    ngOnInit() {
        if (this.formField) {
            this.formField.registerFormFieldControl(this);
        }
    }
    /**
     * Re-validate and emit event to parent container on every CD cycle as they are some errors
     * that we can't subscribe to.
     */
    ngDoCheck() {
        if (this.ngControl) {
            this.updateErrorState();
        }
    }
    /** @hidden */
    ngAfterViewInit() {
        if (this.ngControl) {
            this._subscriptions.add(this.ngControl.statusChanges?.subscribe(() => {
                this.markForCheck();
            }));
        }
        const labelAndHelpId = `fdp-form-label-content-${this.id}`;
        // if not specified, associate label and inline help ids with the input,
        // else add these ids to the specified ones
        if (this.formField) {
            if (!this.ariaLabelledBy) {
                this.ariaLabelledBy = labelAndHelpId;
            }
            else {
                this.ariaLabelledBy += ' ' + labelAndHelpId;
            }
        }
        this.innerErrorsTemplate = this.formField?.innerErrorRenderers;
        this.stateChanges
            .pipe(filter(() => !!this.formField), takeUntilDestroyed(this._destroyed))
            .subscribe(() => {
            this.advancedStateMessage = {
                template: this.formField.innerErrorRenderers,
                hasErrors: this.formField.hasErrors()
            };
            this.detectChanges();
        });
        this.detectChanges();
    }
    /** @hidden */
    registerOnChange(fn) {
        this.onChange = fn;
    }
    /** @hidden */
    registerOnTouched(fn) {
        this.onTouched = fn;
    }
    /** @hidden */
    ngOnDestroy() {
        super.ngOnDestroy();
        this.stateChanges.complete();
        if (this.formField) {
            this.formField.unregisterFormFieldControl(this);
        }
    }
    /** @hidden */
    setDisabledState(isDisabled) {
        const newState = booleanAttribute(isDisabled);
        this.markForCheck();
        if (newState !== this._disabled) {
            this._disabled = newState;
            this.stateChanges.next('setDisabledState');
        }
    }
    /**
     * Each sub class must override this method as inheritance does not work
     */
    writeValue(value) {
        this._value = value;
        this.stateChanges.next('writeValue');
        this.markForCheck();
    }
    /**
     *
     * Keeps track of element focus
     */
    _onFocusChanged(isFocused) {
        if (isFocused !== this.focused && (!this.disabled || !isFocused)) {
            this.focused = isFocused;
            this.stateChanges.next('_onFocusChanged');
        }
        if (!isFocused) {
            this.onTouched();
        }
    }
    /**
     * Handles even when we click on parent container which is the FormField Wrapping this
     * control
     */
    onContainerClick(event) {
        this.focus(event);
    }
    /**
     * In most of the cases when working with input element directly you should be just find to assing
     * variable to this element
     *
     * ```
     * <input #inputElementRef fd-form-control ...>
     * ```
     *
     * and this default behavior used. For other cases implement focus.
     *
     */
    focus(event) {
        if (this._elementRef && !this.focused) {
            this._elementRef.nativeElement.focus(event);
        }
    }
    /**
     *  Need re-validates errors on every CD iteration to make sure we are also
     *  covering non-control errors, errors that happens outside of this control
     */
    updateErrorState() {
        const parent = this.ngForm;
        const parentControlContainer = this.controlContainer;
        const control = this.ngControl ? this.ngControl.control : null;
        const newStatusIsError = !!(control?.invalid &&
            ((this.validationStrategy.includes('dirty') && control.dirty) ||
                (this.validationStrategy.includes('touched') && control.touched) ||
                (this.validationStrategy.includes('submitted') && parent?.submitted) ||
                parentControlContainer?.submitted ||
                this._externalFormSubmitted));
        if (newStatusIsError !== this.controlInvalid) {
            this._controlInvalid = newStatusIsError;
            this.stateChanges.next('updateErrorState');
            this.markForCheck();
        }
    }
    /**
     * Used to change the value of a control.
     * @param value the value to be applied
     * @param emitOnChange whether to emit "onChange" event.
     * Should be "false", if the change is made programmatically (internally) by the control, "true" otherwise
     */
    setValue(value, emitOnChange = true) {
        if (value !== this._value) {
            this.writeValue(value);
            if (emitOnChange) {
                this.onChange(value);
            }
            this.markForCheck();
        }
    }
    /** @hidden */
    getValue() {
        return this._value;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: BaseInput, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "21.1.0", type: BaseInput, isStandalone: true, inputs: { placeholder: "placeholder", triggers: "triggers", state: "state", stateMessage: "stateMessage", stateMessageConfig: "stateMessageConfig", disabled: "disabled", readonly: "readonly", editable: ["editable", "editable", booleanAttribute], validationStrategy: "validationStrategy" }, viewQueries: [{ propertyName: "_elementRef", first: true, predicate: ["inputElementRef"], descendants: true, read: ElementRef, static: true }], usesInheritance: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: BaseInput, decorators: [{
            type: Directive
        }], ctorParameters: () => [], propDecorators: { placeholder: [{
                type: Input
            }], triggers: [{
                type: Input
            }], state: [{
                type: Input
            }], stateMessage: [{
                type: Input
            }], stateMessageConfig: [{
                type: Input
            }], disabled: [{
                type: Input
            }], readonly: [{
                type: Input
            }], editable: [{
                type: Input,
                args: [{ transform: booleanAttribute }]
            }], validationStrategy: [{
                type: Input
            }], _elementRef: [{
                type: ViewChild,
                args: ['inputElementRef', { static: true, read: ElementRef }]
            }] } });

/**
 * Defines specific behavior for Input controls which deals with list of values including:
 *  - Select
 *  - RadioGroup
 *  - CheckboxGroup
 *  - ComboBox
 *  ...
 *
 */
class CollectionBaseInput extends BaseInput {
    /**
     * List of values, it can be of type SelectItem, string or any object.
     * Generic object type is among the list of types,
     * because we allow to get labels and values using `displayKey` and `lookupKey` inputs accordingly.
     */
    set list(value) {
        this._list = value;
    }
    get list() {
        return this._list;
    }
    /** @hidden */
    lookupValue(item) {
        if (isSelectItem(item)) {
            return this.lookupKey && item && item.value ? item.value[this.lookupKey] : item.value;
        }
        else {
            return this.lookupKey && item ? item[this.lookupKey] : item;
        }
    }
    /** @hidden */
    displayValue(item) {
        if (isSelectItem(item)) {
            return item.label;
        }
        else if (isJsObject(item) && this.displayKey) {
            const currentItem = this.objectGet(item, this.displayKey);
            return isFunction(currentItem) ? currentItem() : currentItem;
        }
        else {
            return item;
        }
    }
    /** @hidden */
    objectGet(obj, is) {
        if (!isJsObject(obj)) {
            return obj;
        }
        else if (isString(is)) {
            return this.objectGet(obj, is.split('.'));
        }
        else if (!is?.length) {
            return obj;
        }
        else {
            return this.objectGet(obj[is[0]], is.slice(1));
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: CollectionBaseInput, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.0", type: CollectionBaseInput, isStandalone: true, inputs: { list: "list", lookupKey: "lookupKey", displayKey: "displayKey" }, usesInheritance: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: CollectionBaseInput, decorators: [{
            type: Directive
        }], propDecorators: { list: [{
                type: Input
            }], lookupKey: [{
                type: Input
            }], displayKey: [{
                type: Input
            }] } });

/**
 * FormField base class.
 *
 * This class is used to create form field components.
 *
 */
class PlatformFormField extends FormField {
}
class FormFieldGroup {
}
class PlatformFormFieldControl {
}
/**
 * FormGroup base class.
 *
 * This class is used to create form group components.
 *
 */
class FormGroupContainer {
}

var ColumnLayoutGridClass;
(function (ColumnLayoutGridClass) {
    ColumnLayoutGridClass["XL"] = "xl";
    ColumnLayoutGridClass["L"] = "lg";
    ColumnLayoutGridClass["M"] = "md";
    ColumnLayoutGridClass["S"] = "sm";
})(ColumnLayoutGridClass || (ColumnLayoutGridClass = {}));

var RESPONSIVE_BREAKPOINTS;
(function (RESPONSIVE_BREAKPOINTS) {
    RESPONSIVE_BREAKPOINTS[RESPONSIVE_BREAKPOINTS["S"] = 600] = "S";
    RESPONSIVE_BREAKPOINTS[RESPONSIVE_BREAKPOINTS["M"] = 1024] = "M";
    RESPONSIVE_BREAKPOINTS[RESPONSIVE_BREAKPOINTS["L"] = 1440] = "L";
})(RESPONSIVE_BREAKPOINTS || (RESPONSIVE_BREAKPOINTS = {}));
const RESPONSIVE_BREAKPOINTS_CONFIG = new InjectionToken('Default Responsive breakpoint config');
class ResponsiveBreakPointConfig {
    constructor() {
        /** Large screen */
        this.L = RESPONSIVE_BREAKPOINTS['L'];
        /** Medium screen */
        this.M = RESPONSIVE_BREAKPOINTS['M'];
        /** Small screen */
        this.S = RESPONSIVE_BREAKPOINTS['S'];
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ResponsiveBreakPointConfig, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ResponsiveBreakPointConfig }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ResponsiveBreakPointConfig, decorators: [{
            type: Injectable
        }] });
class ResponsiveBreakpointsService {
    /** @hidden */
    constructor(_breakpointObserver) {
        this._breakpointObserver = _breakpointObserver;
        /** @hidden */
        this.breakpoints = {};
        /** @hidden */
        this.minWidth = 'min-width';
        /** @hidden */
        this.maxWidth = 'max-width';
        /** @hidden */
        this.unit = 'px';
    }
    /** subscribe to get current screen size based on config provided */
    observeBreakpointByConfig(config) {
        const breakPointName = new BehaviorSubject('S');
        this._breakpointObserver.observe(this._getBreakpoints(config)).subscribe((matchValues) => {
            const breakPoint = this._breakPointMeet(matchValues);
            breakPointName.next(breakPoint);
        });
        return breakPointName;
    }
    /** @hidden when screen size changes from one breakpoint to another */
    _breakPointMeet(breakPointMatches) {
        let breakPointName;
        if (breakPointMatches.matches) {
            for (const breakpoint in breakPointMatches.breakpoints) {
                if (breakPointMatches.breakpoints[breakpoint]) {
                    breakPointName = this._getBreakpointName(breakpoint);
                }
            }
        }
        return breakPointName ?? 'S';
    }
    /** @hidden */
    _getBreakpoints(config) {
        let breakPointStr;
        this.activeBreakpoints = [];
        for (const screenSize of Object.keys(config)) {
            switch (screenSize) {
                case 'S':
                    breakPointStr = `(${this.maxWidth}: ${config[screenSize]}${this.unit})`;
                    this.activeBreakpoints.push(breakPointStr);
                    this.breakpoints[breakPointStr] = screenSize;
                    break;
                case 'M':
                    breakPointStr = `(${this.minWidth}: ${config['S']}${this.unit}) and (${this.maxWidth}: ${config[screenSize]}${this.unit})`;
                    this.activeBreakpoints.push(breakPointStr);
                    this.breakpoints[breakPointStr] = screenSize;
                    break;
                case 'L':
                    breakPointStr = `(${this.minWidth}: ${config['M']}${this.unit}) and (${this.maxWidth}: ${config[screenSize]}${this.unit})`;
                    this.activeBreakpoints.push(breakPointStr);
                    this.breakpoints[breakPointStr] = screenSize;
                    // create entry for XL screen
                    breakPointStr = `(${this.minWidth}: ${config[screenSize]}${this.unit})`;
                    this.activeBreakpoints.push(breakPointStr);
                    this.breakpoints[breakPointStr] = 'XL';
                    break;
            }
        }
        return this.activeBreakpoints;
    }
    /** @hidden */
    _getBreakpointName(breakpointValue) {
        return this.breakpoints[breakpointValue];
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ResponsiveBreakpointsService, deps: [{ token: i1.BreakpointObserver }], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ResponsiveBreakpointsService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ResponsiveBreakpointsService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root'
                }]
        }], ctorParameters: () => [{ type: i1.BreakpointObserver }] });
class InLineLayoutCollectionBaseInput extends CollectionBaseInput {
    constructor() {
        super(...arguments);
        /** @hidden */
        this._inlineCurrentValue$ = new BehaviorSubject(false);
        /** @hidden */
        this._responsiveBreakpointsService = inject(ResponsiveBreakpointsService);
        /** @hidden */
        this._responsiveBreakPointConfig = inject(RESPONSIVE_BREAKPOINTS_CONFIG, {
            optional: true
        }) || new ResponsiveBreakPointConfig();
        /** @hidden */
        this._isInLineLayoutEnabled = false;
    }
    /** object to change isInline property based on screen size */
    set inlineLayout(layout) {
        if (layout) {
            this._inlineLayout = layout;
            this._isInLineLayoutEnabled = true;
        }
        this._setFieldLayout(layout);
    }
    get inlineLayout() {
        return this._inlineLayout;
    }
    /** @hidden */
    ngOnInit() {
        super.ngOnInit();
        if (this._isInLineLayoutEnabled) {
            this._responsiveBreakpointsService
                .observeBreakpointByConfig(this._responsiveBreakPointConfig)
                .subscribe((breakPointName) => {
                this._updateLayout(breakPointName);
            });
        }
    }
    /** @hidden set values of inline for each screen layout */
    _setFieldLayout(inlineLayout) {
        if (!inlineLayout) {
            this._isInLineLayoutEnabled = false;
            return;
        }
        try {
            this._sIsInline = !!inlineLayout['S'];
            this._mdIsInline = !!inlineLayout['M'];
            this._lgIsInline = !!inlineLayout['L'];
            this._xlIsInline = !!inlineLayout['XL'];
        }
        catch {
            this._isInLineLayoutEnabled = false;
        }
    }
    /** @hidden */
    _updateLayout(currentBreakingPointName) {
        if (this._isInLineLayoutEnabled) {
            switch (currentBreakingPointName) {
                case 'S':
                    this._inlineCurrentValue$.next(this._sIsInline);
                    break;
                case 'M':
                    this._inlineCurrentValue$.next(this._mdIsInline);
                    break;
                case 'L':
                    this._inlineCurrentValue$.next(this._lgIsInline);
                    break;
                case 'XL':
                    this._inlineCurrentValue$.next(this._xlIsInline);
                    break;
                default:
                    this._inlineCurrentValue$.next(this._xlIsInline);
            }
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: InLineLayoutCollectionBaseInput, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.0", type: InLineLayoutCollectionBaseInput, isStandalone: true, inputs: { inlineLayout: "inlineLayout" }, usesInheritance: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: InLineLayoutCollectionBaseInput, decorators: [{
            type: Directive
        }], propDecorators: { inlineLayout: [{
                type: Input
            }] } });

class ConvertBytesPipe {
    constructor() {
        /** @hidden */
        this._sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
    }
    /** @hidden */
    transform(bytes) {
        return this._convertBytes(bytes);
    }
    /**
     * Convert bytes to KB, MB, GB, ...
     * @param bytes number
     * @param decimals number
     */
    _convertBytes(bytes = 0, decimals = 2) {
        if (bytes <= 0) {
            return '0 Bytes';
        }
        const k = 1024;
        const dm = decimals < 0 ? 0 : decimals;
        const i = Math.floor(Math.log(bytes) / Math.log(k));
        return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + this._sizes[i];
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ConvertBytesPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
    static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.0", ngImport: i0, type: ConvertBytesPipe, isStandalone: true, name: "convertBytes" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ConvertBytesPipe, decorators: [{
            type: Pipe,
            args: [{
                    name: 'convertBytes',
                    standalone: true
                }]
        }] });

/**
 * @deprecated
 * Use direct imports of components and directives.
 */
class PlatformPipeModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: PlatformPipeModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.1.0", ngImport: i0, type: PlatformPipeModule, imports: [ConvertBytesPipe], exports: [ConvertBytesPipe] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: PlatformPipeModule }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: PlatformPipeModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [ConvertBytesPipe],
                    exports: [ConvertBytesPipe]
                }]
        }] });

/**
 * @license
 * Copyright Google LLC All Rights Reserved.
 *
 * Use of this source code is governed by an MIT-style license that can be
 * found in the LICENSE file at https://angular.io/license
 */
/**
 * Creates a browser MouseEvent with the specified options.
 * @docs-private
 */
function createMouseEvent(type, x = 0, y = 0, button = 0) {
    const event = document.createEvent('MouseEvent');
    const originalPreventDefault = event.preventDefault.bind(event);
    event.initMouseEvent(type, true /* canBubble */, true /* cancelable */, window /* view */, 0 /* detail */, x /* screenX */, y /* screenY */, x /* clientX */, y /* clientY */, false /* ctrlKey */, false /* altKey */, false /* shiftKey */, false /* metaKey */, button /* button */, null /* relatedTarget */);
    // `initMouseEvent` doesn't allow us to pass the `buttons` and
    // defaults it to 0 which looks like a fake event.
    Object.defineProperty(event, 'buttons', { get: () => 1 });
    // IE won't set `defaultPrevented` on synthetic events so we need to do it manually.
    event.preventDefault = function () {
        Object.defineProperty(event, 'defaultPrevented', { get: () => true, configurable: true });
        return originalPreventDefault();
    };
    return event;
}
/**
 * Creates a browser TouchEvent with the specified pointer coordinates.
 * @docs-private
 */
function createTouchEvent(type, pageX = 0, pageY = 0) {
    // In favor of creating events that work for most of the browsers, the event is created
    // as a basic UI Event. The necessary details for the event will be set manually.
    const event = document.createEvent('UIEvent');
    const touchDetails = { pageX, pageY };
    // TS3.6 removes the initUIEvent method and suggests porting to "new UIEvent()".
    event.initUIEvent(type, true, true, window, 0);
    // Most of the browsers don't have a "initTouchEvent" method that can be used to define
    // the touch details.
    Object.defineProperties(event, {
        touches: { value: [touchDetails] },
        targetTouches: { value: [touchDetails] },
        changedTouches: { value: [touchDetails] }
    });
    return event;
}
/**
 * Dispatches a keydown event from an element.
 * @docs-private
 */
function createKeyboardEvent(type, keyCode = 0, key = '', target, modifiers = {}) {
    const event = document.createEvent('KeyboardEvent');
    const originalPreventDefault = event.preventDefault;
    // Firefox does not support `initKeyboardEvent`, but supports `initKeyEvent`.
    if (event.initKeyEvent) {
        event.initKeyEvent(type, true, true, window, modifiers.control, modifiers.alt, modifiers.shift, modifiers.meta, keyCode);
    }
    else {
        // `initKeyboardEvent` expects to receive modifiers as a whitespace-delimited string
        // See https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/initKeyboardEvent
        let modifiersList = '';
        if (modifiers.control) {
            modifiersList += 'Control ';
        }
        if (modifiers.alt) {
            modifiersList += 'Alt ';
        }
        if (modifiers.shift) {
            modifiersList += 'Shift ';
        }
        if (modifiers.meta) {
            modifiersList += 'Meta ';
        }
        event.initKeyboardEvent(type, true /* canBubble */, true /* cancelable */, window /* view */, 0 /* char */, key /* key */, 0 /* location */, modifiersList.trim() /* modifiersList */, false /* repeat */);
    }
    // Webkit Browsers don't set the keyCode when calling the init function.
    // See related bug https://bugs.webkit.org/show_bug.cgi?id=16735
    Object.defineProperties(event, {
        keyCode: { get: () => keyCode },
        key: { get: () => key },
        target: { get: () => target },
        ctrlKey: { get: () => !!modifiers.control },
        altKey: { get: () => !!modifiers.alt },
        shiftKey: { get: () => !!modifiers.shift },
        metaKey: { get: () => !!modifiers.meta }
    });
    // IE won't set `defaultPrevented` on synthetic events so we need to do it manually.
    event.preventDefault = function () {
        Object.defineProperty(event, 'defaultPrevented', { get: () => true, configurable: true });
        // eslint-disable-next-line prefer-rest-params
        return originalPreventDefault.apply(this, arguments);
    };
    return event;
}
/**
 * Creates a fake event object with any desired event type.
 * @docs-private
 */
function createFakeEvent(type, canBubble = false, cancelable = true) {
    const event = document.createEvent('Event');
    event.initEvent(type, canBubble, cancelable);
    return event;
}

const FDP_PRESET_MANAGED_COMPONENT = new InjectionToken('FdpPresetManagedComponent');

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

export { ApprovalFlowTeamDataSource, ApprovalFlowUserDataSource, ArrayComboBoxDataSource, ArrayListDataSource, ArrayMultiComboBoxDataSource, ArrayMultiInputDataSource, BaseComponent, BaseDataProvider, BaseInput, CollectionBaseInput, ColumnLayoutGridClass, ComboBoxDataSource, ConvertBytesPipe, DATA_PROVIDERS, DataProvider, FDP_DO_CHECK, FDP_FORM_SUBMIT, FDP_PRESET_MANAGED_COMPONENT, FormFieldGroup, FormGroupContainer, InLineLayoutCollectionBaseInput, ListDataSource, MatchingStrategy, MultiComboBoxDataSource, MultiInputDataSource, ObservableComboBoxDataSource, ObservableListDataSource, ObservableMultiComboBoxDataSource, ObservableMultiInputDataSource, PlatformConfig, PlatformFormField, PlatformFormFieldControl, PlatformPipeModule, RESPONSIVE_BREAKPOINTS, RESPONSIVE_BREAKPOINTS_CONFIG, ResponsiveBreakPointConfig, ResponsiveBreakpointsService, SearchFieldDataSource, coerceArraySafe, createFakeEvent, createKeyboardEvent, createMouseEvent, createTouchEvent, getMatchingStrategyStartsWithPerTermReqexp, isBlank, isBoolean, isDataSource, isFunction, isJsObject, isNumber, isObject, isOptionItem, isPresent, isPromise, isSelectItem, isSelectableItem, isSelectableOptionItem, isString, isStringMap, isSubscribable, isType, objectToName, objectValues };
//# sourceMappingURL=fundamental-ngx-platform-shared.mjs.map
