import * as i0 from '@angular/core';
import { InjectionToken, inject, Injectable, input, computed, viewChild, ViewContainerRef, effect, Component, DestroyRef, output, ChangeDetectionStrategy } from '@angular/core';
import * as i3 from '@angular/common';
import { CommonModule } from '@angular/common';
import * as i1 from '@angular/forms';
import { Validators, FormControl, ReactiveFormsModule } from '@angular/forms';
import { startWith } from 'rxjs/operators';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';

const FORM_RENDERER_FIELD_REGISTRY = new InjectionToken('FORM_RENDERER_FIELDS_CONFIG');

class ComponentRegistryService {
    globalRegistry = inject(FORM_RENDERER_FIELD_REGISTRY, { optional: true });
    componentMap = new Map();
    constructor() {
        this.registerDefaultComponents();
    }
    registerDefaultComponents() {
        this.componentMap.set('input', () => import('./ngstarter-ui-components-form-renderer-input-field-RYxi-Mpw.mjs').then(c => c.InputField));
        this.componentMap.set('textarea', () => import('./ngstarter-ui-components-form-renderer-textarea-field-4zH7FTQ1.mjs')
            .then(c => c.TextareaField));
        this.componentMap.set('select', () => import('./ngstarter-ui-components-form-renderer-select-field-eLcwI-BY.mjs')
            .then(c => c.SelectField));
        this.componentMap.set('checkbox', () => import('./ngstarter-ui-components-form-renderer-checkbox-field-CoyKdvhV.mjs')
            .then(c => c.CheckboxField));
        this.componentMap.set('datepicker', () => import('./ngstarter-ui-components-form-renderer-datepicker-field-Bzc0TPO9.mjs')
            .then(c => c.DatepickerField));
        this.componentMap.set('toggle', () => import('./ngstarter-ui-components-form-renderer-toggle-field-iyqUrWxt.mjs')
            .then(c => c.ToggleField));
        this.componentMap.set('radioGroup', () => import('./ngstarter-ui-components-form-renderer-radio-group-field-DUCSU3iT.mjs')
            .then(c => c.RadioGroupField));
        this.componentMap.set('timezone', () => import('./ngstarter-ui-components-form-renderer-timezone-field-BpH65Hd-.mjs')
            .then(c => c.TimezoneField));
        this.componentMap.set('image', () => import('./ngstarter-ui-components-form-renderer-image-content-ICTwkZPa.mjs')
            .then(c => c.ImageContent));
        this.componentMap.set('text', () => import('./ngstarter-ui-components-form-renderer-text-content-BjzH_M3-.mjs')
            .then(c => c.TextContent));
        this.componentMap.set('divider', () => import('./ngstarter-ui-components-form-renderer-divider-content-D8Sffofu.mjs')
            .then(c => c.DividerContent));
        this.componentMap.set('autocompleteMany', () => import('./ngstarter-ui-components-form-renderer-autocomplete-many-field-BKQVlZHV.mjs')
            .then(c => c.AutocompleteManyField));
        if (this.globalRegistry) {
            const globalRegistry = this.globalRegistry;
            Object.keys(globalRegistry).forEach(typeName => {
                this.componentMap.set(typeName, globalRegistry[typeName]);
            });
        }
    }
    getImporter(typeName) {
        return this.componentMap.get(typeName);
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ComponentRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ComponentRegistryService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ComponentRegistryService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }], ctorParameters: () => [] });

class ComponentLoader {
    registry;
    componentConfig = input.required(...(ngDevMode ? [{ debugName: "componentConfig" }] : /* istanbul ignore next */ []));
    control = input(null, ...(ngDevMode ? [{ debugName: "control" }] : /* istanbul ignore next */ []));
    colspan = input(...(ngDevMode ? [undefined, { debugName: "colspan" }] : /* istanbul ignore next */ []));
    gridColumnStyle = computed(() => {
        const span = this.colspan();
        return span ? `span ${span}` : null;
    }, ...(ngDevMode ? [{ debugName: "gridColumnStyle" }] : /* istanbul ignore next */ []));
    anchor = viewChild.required('anchor', { read: ViewContainerRef });
    constructor(registry) {
        this.registry = registry;
        effect(async () => {
            const config = this.componentConfig();
            const control = this.control();
            const vcr = this.anchor();
            vcr.clear();
            const importer = this.registry.getImporter(config.type);
            if (importer) {
                try {
                    const componentType = await importer();
                    const componentRef = vcr.createComponent(componentType);
                    componentRef.setInput('config', config);
                    if (control) {
                        componentRef.setInput('control', control);
                    }
                }
                catch (error) {
                    console.error(`Error loading component for type "${config.type}":`, error);
                }
            }
        });
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ComponentLoader, deps: [{ token: ComponentRegistryService }], target: i0.ɵɵFactoryTarget.Component });
    static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.4", type: ComponentLoader, isStandalone: true, selector: "ngs-component-loader", inputs: { componentConfig: { classPropertyName: "componentConfig", publicName: "componentConfig", isSignal: true, isRequired: true, transformFunction: null }, control: { classPropertyName: "control", publicName: "control", isSignal: true, isRequired: false, transformFunction: null }, colspan: { classPropertyName: "colspan", publicName: "colspan", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style.grid-column": "gridColumnStyle()" } }, viewQueries: [{ propertyName: "anchor", first: true, predicate: ["anchor"], descendants: true, read: ViewContainerRef, isSignal: true }], exportAs: ["ngsComponentLoader"], ngImport: i0, template: "<ng-container #anchor/>\n" });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ComponentLoader, decorators: [{
            type: Component,
            args: [{ selector: 'ngs-component-loader', exportAs: 'ngsComponentLoader', standalone: true, host: {
                        '[style.grid-column]': 'gridColumnStyle()',
                    }, template: "<ng-container #anchor/>\n" }]
        }], ctorParameters: () => [{ type: ComponentRegistryService }], propDecorators: { componentConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "componentConfig", required: true }] }], control: [{ type: i0.Input, args: [{ isSignal: true, alias: "control", required: false }] }], colspan: [{ type: i0.Input, args: [{ isSignal: true, alias: "colspan", required: false }] }], anchor: [{ type: i0.ViewChild, args: ['anchor', { ...{ read: ViewContainerRef }, isSignal: true }] }] } });

class ValidatorRegistryService {
    validatorMap = new Map();
    constructor() {
        this.registerDefaultValidators();
    }
    registerDefaultValidators() {
        this.registerValidator('required', () => Validators.required);
        this.registerValidator('email', () => Validators.email);
        this.registerValidator('minLength', (config) => Validators.minLength(config.value));
        this.registerValidator('maxLength', (config) => Validators.maxLength(config.value));
        this.registerValidator('pattern', (config) => Validators.pattern(config.value));
    }
    registerValidator(name, factory) {
        this.validatorMap.set(name, factory);
    }
    getValidator(config) {
        const factory = this.validatorMap.get(config.type);
        if (!factory) {
            return Validators.nullValidator;
        }
        return factory(config);
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ValidatorRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ValidatorRegistryService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ValidatorRegistryService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }], ctorParameters: () => [] });

class FormGeneratorService {
    fb;
    validatorRegistry;
    constructor(fb, validatorRegistry) {
        this.fb = fb;
        this.validatorRegistry = validatorRegistry;
    }
    createFormGroup(config, initialValue) {
        const group = this.fb.group({}, { validators: config.crossValidators });
        for (const elementConfig of config.elements) {
            if (elementConfig.kind === 'field') {
                let finalValue = initialValue?.[elementConfig.name] ?? elementConfig.value ?? null;
                if (finalValue === null && 'defaultValue' in elementConfig) {
                    finalValue = elementConfig.defaultValue;
                }
                const validators = this.mapValidators(elementConfig.validators);
                const formState = {
                    value: finalValue,
                    disabled: elementConfig.disabled ?? false
                };
                const control = new FormControl(formState, validators);
                group.addControl(elementConfig.name, control);
            }
        }
        return group;
    }
    mapValidators(validators) {
        if (!validators) {
            return [];
        }
        return validators.map(config => this.validatorRegistry.getValidator(config));
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FormGeneratorService, deps: [{ token: i1.FormBuilder }, { token: ValidatorRegistryService }], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FormGeneratorService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FormGeneratorService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }], ctorParameters: () => [{ type: i1.FormBuilder }, { type: ValidatorRegistryService }] });

class FormRenderer {
    formGenerator;
    validatorRegistry;
    destroyRef = inject(DestroyRef);
    config = input.required(...(ngDevMode ? [{ debugName: "config" }] : /* istanbul ignore next */ []));
    initialValue = input(...(ngDevMode ? [undefined, { debugName: "initialValue" }] : /* istanbul ignore next */ []));
    formSubmit = output();
    valueChanges = output();
    initialized = output();
    elementsMap = computed(() => {
        const map = new Map();
        for (const element of this.config().elements) {
            map.set(element.name, element);
        }
        return map;
    }, ...(ngDevMode ? [{ debugName: "elementsMap" }] : /* istanbul ignore next */ []));
    formGroup = computed(() => this.formGenerator.createFormGroup(this.config(), this.initialValue()), ...(ngDevMode ? [{ debugName: "formGroup" }] : /* istanbul ignore next */ []));
    #initialized = false;
    valueChangesSub;
    constructor(formGenerator, validatorRegistry) {
        this.formGenerator = formGenerator;
        this.validatorRegistry = validatorRegistry;
        effect(() => {
            this.valueChangesSub?.unsubscribe();
            const form = this.formGroup();
            this.valueChangesSub = form.valueChanges
                .pipe(startWith(form.value), takeUntilDestroyed(this.destroyRef))
                .subscribe(() => {
                for (const elementConfig of this.config().elements) {
                    if (elementConfig.kind === 'field') {
                        const control = form.get(elementConfig.name);
                        if (!control) {
                            continue;
                        }
                        const isVisible = elementConfig.visibleWhen ? elementConfig.visibleWhen(form) : true;
                        const shouldBeEnabled = !(elementConfig.disabled ?? false) && isVisible;
                        if (shouldBeEnabled && control.disabled) {
                            control.enable({ emitEvent: false });
                            const validators = this.mapValidators(elementConfig.validators);
                            control.setValidators(validators);
                        }
                        else if (!shouldBeEnabled && control.enabled) {
                            control.disable({ emitEvent: false });
                            control.clearValidators();
                        }
                        control.updateValueAndValidity({ emitEvent: false });
                    }
                }
                if (!this.#initialized) {
                    this.#initialized = true;
                    this.initialized.emit();
                }
            });
        });
    }
    ngOnInit() {
        const form = this.formGroup();
        form
            .valueChanges
            .pipe(takeUntilDestroyed(this.destroyRef))
            .subscribe(() => {
            if (!this.#initialized) {
                return;
            }
            this.valueChanges.emit(form.value);
        });
    }
    ngOnDestroy() {
        this.valueChangesSub?.unsubscribe();
    }
    submit() {
        const form = this.formGroup();
        if (form.valid) {
            this.formSubmit.emit(form.getRawValue());
        }
        else {
            form.markAllAsTouched();
        }
    }
    get form() {
        return this.formGroup();
    }
    get value() {
        return this.formGroup().value;
    }
    get isValid() {
        return this.form.valid;
    }
    get isInvalid() {
        return this.form.invalid;
    }
    isFieldVisible(name) {
        const componentConfig = this.elementsMap().get(name);
        if (!componentConfig) {
            return true;
        }
        if (componentConfig.kind === 'field' && componentConfig.visibleWhen) {
            return componentConfig.visibleWhen(this.formGroup());
        }
        return true;
    }
    getComponentConfig(name) {
        return this.elementsMap().get(name);
    }
    getControl(name) {
        return this.formGroup().get(name);
    }
    isGridNode(node) {
        return 'children' in node;
    }
    onSubmit() {
        this.submit();
    }
    mapValidators(validators) {
        if (!validators) {
            return [];
        }
        return validators.map(config => this.validatorRegistry.getValidator(config));
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FormRenderer, deps: [{ token: FormGeneratorService }, { token: ValidatorRegistryService }], target: i0.ɵɵFactoryTarget.Component });
    static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.4", type: FormRenderer, isStandalone: true, selector: "ngs-form-renderer", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null }, initialValue: { classPropertyName: "initialValue", publicName: "initialValue", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { formSubmit: "formSubmit", valueChanges: "valueChanges", initialized: "initialized" }, host: { classAttribute: "ngs-form-renderer" }, exportAs: ["ngsFormRenderer"], ngImport: i0, template: "@if (config(); as cfg) {\n  <form [formGroup]=\"formGroup()\" (ngSubmit)=\"onSubmit()\">\n    <ng-container *ngTemplateOutlet=\"renderNode; context: {$implicit: cfg.layout}\" />\n    <ng-content/>\n  </form>\n}\n\n<ng-template #renderNode let-node>\n  @if (isGridNode(node)) {\n    <div\n      class=\"grid-layout\"\n      [style.grid-template-columns]=\"'repeat(' + node.columns + ', 1fr)'\"\n      [style.gap]=\"node.gap\">\n      @for (childNode of node.children; track childNode) {\n        <ng-container *ngTemplateOutlet=\"renderNode; context: {$implicit: childNode}\" />\n      }\n    </div>\n  } @else {\n    @if(isFieldVisible(node.name)) {\n      <ngs-component-loader [componentConfig]=\"getComponentConfig(node.name)!\"\n        [control]=\"getControl(node.name)\"\n        [colspan]=\"node.colspan\" />\n    }\n  }\n</ng-template>\n", styles: [":host{display:block}:host .grid-layout{display:grid;width:100%;gap:calc(var(--spacing, .25rem) * 4) calc(var(--spacing, .25rem) * 7)}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: ComponentLoader, selector: "ngs-component-loader", inputs: ["componentConfig", "control", "colspan"], exportAs: ["ngsComponentLoader"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FormRenderer, decorators: [{
            type: Component,
            args: [{ selector: 'ngs-form-renderer', exportAs: 'ngsFormRenderer', standalone: true, imports: [
                        CommonModule,
                        ReactiveFormsModule,
                        ComponentLoader,
                    ], changeDetection: ChangeDetectionStrategy.OnPush, host: {
                        'class': 'ngs-form-renderer',
                    }, template: "@if (config(); as cfg) {\n  <form [formGroup]=\"formGroup()\" (ngSubmit)=\"onSubmit()\">\n    <ng-container *ngTemplateOutlet=\"renderNode; context: {$implicit: cfg.layout}\" />\n    <ng-content/>\n  </form>\n}\n\n<ng-template #renderNode let-node>\n  @if (isGridNode(node)) {\n    <div\n      class=\"grid-layout\"\n      [style.grid-template-columns]=\"'repeat(' + node.columns + ', 1fr)'\"\n      [style.gap]=\"node.gap\">\n      @for (childNode of node.children; track childNode) {\n        <ng-container *ngTemplateOutlet=\"renderNode; context: {$implicit: childNode}\" />\n      }\n    </div>\n  } @else {\n    @if(isFieldVisible(node.name)) {\n      <ngs-component-loader [componentConfig]=\"getComponentConfig(node.name)!\"\n        [control]=\"getControl(node.name)\"\n        [colspan]=\"node.colspan\" />\n    }\n  }\n</ng-template>\n", styles: [":host{display:block}:host .grid-layout{display:grid;width:100%;gap:calc(var(--spacing, .25rem) * 4) calc(var(--spacing, .25rem) * 7)}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"] }]
        }], ctorParameters: () => [{ type: FormGeneratorService }, { type: ValidatorRegistryService }], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: true }] }], initialValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialValue", required: false }] }], formSubmit: [{ type: i0.Output, args: ["formSubmit"] }], valueChanges: [{ type: i0.Output, args: ["valueChanges"] }], initialized: [{ type: i0.Output, args: ["initialized"] }] } });

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

export { ComponentRegistryService, FORM_RENDERER_FIELD_REGISTRY, FormRenderer, ValidatorRegistryService };
//# sourceMappingURL=ngstarter-ui-components-form-renderer.mjs.map
