/**
 * @license Angular v21.2.1
 * (c) 2010-2026 Google LLC. https://angular.dev/
 * License: MIT
 */

import * as i0 from '@angular/core';
import { InjectionToken, ɵisPromise as _isPromise, resource, linkedSignal, inject, ɵRuntimeError as _RuntimeError, untracked, input, Renderer2, DestroyRef, computed, Injector, ElementRef, signal, afterRenderEffect, effect, ɵformatRuntimeError as _formatRuntimeError, Directive } from '@angular/core';
import { assertPathIsCurrent, FieldPathNode, addDefaultField, metadata, createMetadataKey, MAX, MAX_LENGTH, MIN, MIN_LENGTH, PATTERN, REQUIRED, createManagedMetadataKey, DEBOUNCER, signalErrorsToValidationErrors, submit } from './_validation_errors-chunk.mjs';
export { MetadataKey, MetadataReducer, apply, applyEach, applyWhen, applyWhenValue, form, schema } from './_validation_errors-chunk.mjs';
import { httpResource } from '@angular/common/http';
import { Validators, NG_VALUE_ACCESSOR, NgControl } from '@angular/forms';
import '@angular/core/primitives/signals';

const SIGNAL_FORMS_CONFIG = new InjectionToken(typeof ngDevMode !== 'undefined' && ngDevMode ? 'SIGNAL_FORMS_CONFIG' : '');

function provideSignalFormsConfig(config) {
  return [{
    provide: SIGNAL_FORMS_CONFIG,
    useValue: config
  }];
}

function disabled(path, logic) {
  assertPathIsCurrent(path);
  const pathNode = FieldPathNode.unwrapFieldPath(path);
  pathNode.builder.addDisabledReasonRule(ctx => {
    let result = true;
    if (typeof logic === 'string') {
      result = logic;
    } else if (logic) {
      result = logic(ctx);
    }
    if (typeof result === 'string') {
      return {
        fieldTree: ctx.fieldTree,
        message: result
      };
    }
    return result ? {
      fieldTree: ctx.fieldTree
    } : undefined;
  });
}

function hidden(path, logic) {
  assertPathIsCurrent(path);
  const pathNode = FieldPathNode.unwrapFieldPath(path);
  pathNode.builder.addHiddenRule(logic);
}

function readonly(path, logic = () => true) {
  assertPathIsCurrent(path);
  const pathNode = FieldPathNode.unwrapFieldPath(path);
  pathNode.builder.addReadonlyRule(logic);
}

function getLengthOrSize(value) {
  const v = value;
  return typeof v.length === 'number' ? v.length : v.size;
}
function getOption(opt, ctx) {
  return opt instanceof Function ? opt(ctx) : opt;
}
function isEmpty(value) {
  if (typeof value === 'number') {
    return isNaN(value);
  }
  return value === '' || value === false || value == null;
}
function normalizeErrors(error) {
  if (error === undefined) {
    return [];
  }
  if (Array.isArray(error)) {
    return error;
  }
  return [error];
}

function validate(path, logic) {
  assertPathIsCurrent(path);
  const pathNode = FieldPathNode.unwrapFieldPath(path);
  pathNode.builder.addSyncErrorRule(ctx => {
    return addDefaultField(logic(ctx), ctx.fieldTree);
  });
}

function requiredError(options) {
  return new RequiredValidationError(options);
}
function minError(min, options) {
  return new MinValidationError(min, options);
}
function maxError(max, options) {
  return new MaxValidationError(max, options);
}
function minLengthError(minLength, options) {
  return new MinLengthValidationError(minLength, options);
}
function maxLengthError(maxLength, options) {
  return new MaxLengthValidationError(maxLength, options);
}
function patternError(pattern, options) {
  return new PatternValidationError(pattern, options);
}
function emailError(options) {
  return new EmailValidationError(options);
}
class BaseNgValidationError {
  __brand = undefined;
  kind = '';
  fieldTree;
  message;
  constructor(options) {
    if (options) {
      Object.assign(this, options);
    }
  }
}
class RequiredValidationError extends BaseNgValidationError {
  kind = 'required';
}
class MinValidationError extends BaseNgValidationError {
  min;
  kind = 'min';
  constructor(min, options) {
    super(options);
    this.min = min;
  }
}
class MaxValidationError extends BaseNgValidationError {
  max;
  kind = 'max';
  constructor(max, options) {
    super(options);
    this.max = max;
  }
}
class MinLengthValidationError extends BaseNgValidationError {
  minLength;
  kind = 'minLength';
  constructor(minLength, options) {
    super(options);
    this.minLength = minLength;
  }
}
class MaxLengthValidationError extends BaseNgValidationError {
  maxLength;
  kind = 'maxLength';
  constructor(maxLength, options) {
    super(options);
    this.maxLength = maxLength;
  }
}
class PatternValidationError extends BaseNgValidationError {
  pattern;
  kind = 'pattern';
  constructor(pattern, options) {
    super(options);
    this.pattern = pattern;
  }
}
class EmailValidationError extends BaseNgValidationError {
  kind = 'email';
}
class NativeInputParseError extends BaseNgValidationError {
  kind = 'parse';
}
const NgValidationError = BaseNgValidationError;

const EMAIL_REGEXP = /^(?=.{1,254}$)(?=.{1,64}@)[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
function email(path, config) {
  validate(path, ctx => {
    if (isEmpty(ctx.value())) {
      return undefined;
    }
    if (!EMAIL_REGEXP.test(ctx.value())) {
      if (config?.error) {
        return getOption(config.error, ctx);
      } else {
        return emailError({
          message: getOption(config?.message, ctx)
        });
      }
    }
    return undefined;
  });
}

function max(path, maxValue, config) {
  const MAX_MEMO = metadata(path, createMetadataKey(), ctx => typeof maxValue === 'number' ? maxValue : maxValue(ctx));
  metadata(path, MAX, ({
    state
  }) => state.metadata(MAX_MEMO)());
  validate(path, ctx => {
    if (isEmpty(ctx.value())) {
      return undefined;
    }
    const max = ctx.state.metadata(MAX_MEMO)();
    if (max === undefined || Number.isNaN(max)) {
      return undefined;
    }
    const value = ctx.value();
    const numValue = !value && value !== 0 ? NaN : Number(value);
    if (numValue > max) {
      if (config?.error) {
        return getOption(config.error, ctx);
      } else {
        return maxError(max, {
          message: getOption(config?.message, ctx)
        });
      }
    }
    return undefined;
  });
}

function maxLength(path, maxLength, config) {
  const MAX_LENGTH_MEMO = metadata(path, createMetadataKey(), ctx => typeof maxLength === 'number' ? maxLength : maxLength(ctx));
  metadata(path, MAX_LENGTH, ({
    state
  }) => state.metadata(MAX_LENGTH_MEMO)());
  validate(path, ctx => {
    if (isEmpty(ctx.value())) {
      return undefined;
    }
    const maxLength = ctx.state.metadata(MAX_LENGTH_MEMO)();
    if (maxLength === undefined) {
      return undefined;
    }
    if (getLengthOrSize(ctx.value()) > maxLength) {
      if (config?.error) {
        return getOption(config.error, ctx);
      } else {
        return maxLengthError(maxLength, {
          message: getOption(config?.message, ctx)
        });
      }
    }
    return undefined;
  });
}

function min(path, minValue, config) {
  const MIN_MEMO = metadata(path, createMetadataKey(), ctx => typeof minValue === 'number' ? minValue : minValue(ctx));
  metadata(path, MIN, ({
    state
  }) => state.metadata(MIN_MEMO)());
  validate(path, ctx => {
    if (isEmpty(ctx.value())) {
      return undefined;
    }
    const min = ctx.state.metadata(MIN_MEMO)();
    if (min === undefined || Number.isNaN(min)) {
      return undefined;
    }
    const value = ctx.value();
    const numValue = !value && value !== 0 ? NaN : Number(value);
    if (numValue < min) {
      if (config?.error) {
        return getOption(config.error, ctx);
      } else {
        return minError(min, {
          message: getOption(config?.message, ctx)
        });
      }
    }
    return undefined;
  });
}

function minLength(path, minLength, config) {
  const MIN_LENGTH_MEMO = metadata(path, createMetadataKey(), ctx => typeof minLength === 'number' ? minLength : minLength(ctx));
  metadata(path, MIN_LENGTH, ({
    state
  }) => state.metadata(MIN_LENGTH_MEMO)());
  validate(path, ctx => {
    if (isEmpty(ctx.value())) {
      return undefined;
    }
    const minLength = ctx.state.metadata(MIN_LENGTH_MEMO)();
    if (minLength === undefined) {
      return undefined;
    }
    if (getLengthOrSize(ctx.value()) < minLength) {
      if (config?.error) {
        return getOption(config.error, ctx);
      } else {
        return minLengthError(minLength, {
          message: getOption(config?.message, ctx)
        });
      }
    }
    return undefined;
  });
}

function pattern(path, pattern, config) {
  const PATTERN_MEMO = metadata(path, createMetadataKey(), ctx => pattern instanceof RegExp ? pattern : pattern(ctx));
  metadata(path, PATTERN, ({
    state
  }) => state.metadata(PATTERN_MEMO)());
  validate(path, ctx => {
    if (isEmpty(ctx.value())) {
      return undefined;
    }
    const pattern = ctx.state.metadata(PATTERN_MEMO)();
    if (pattern === undefined) {
      return undefined;
    }
    if (!pattern.test(ctx.value())) {
      if (config?.error) {
        return getOption(config.error, ctx);
      } else {
        return patternError(pattern, {
          message: getOption(config?.message, ctx)
        });
      }
    }
    return undefined;
  });
}

function required(path, config) {
  const REQUIRED_MEMO = metadata(path, createMetadataKey(), ctx => config?.when ? config.when(ctx) : true);
  metadata(path, REQUIRED, ({
    state
  }) => state.metadata(REQUIRED_MEMO)());
  validate(path, ctx => {
    if (ctx.state.metadata(REQUIRED_MEMO)() && isEmpty(ctx.value())) {
      if (config?.error) {
        return getOption(config.error, ctx);
      } else {
        return requiredError({
          message: getOption(config?.message, ctx)
        });
      }
    }
    return undefined;
  });
}

function validateAsync(path, opts) {
  assertPathIsCurrent(path);
  const pathNode = FieldPathNode.unwrapFieldPath(path);
  const RESOURCE = createManagedMetadataKey(opts.factory);
  metadata(path, RESOURCE, ctx => {
    const node = ctx.stateOf(path);
    const validationState = node.validationState;
    if (validationState.shouldSkipValidation() || !validationState.syncValid()) {
      return undefined;
    }
    return opts.params(ctx);
  });
  pathNode.builder.addAsyncErrorRule(ctx => {
    const res = ctx.state.metadata(RESOURCE);
    let errors;
    switch (res.status()) {
      case 'idle':
        return undefined;
      case 'loading':
      case 'reloading':
        return 'pending';
      case 'resolved':
      case 'local':
        if (!res.hasValue()) {
          return undefined;
        }
        errors = opts.onSuccess(res.value(), ctx);
        return addDefaultField(errors, ctx.fieldTree);
      case 'error':
        errors = opts.onError(res.error(), ctx);
        return addDefaultField(errors, ctx.fieldTree);
    }
  });
}

function validateTree(path, logic) {
  assertPathIsCurrent(path);
  const pathNode = FieldPathNode.unwrapFieldPath(path);
  pathNode.builder.addSyncTreeErrorRule(ctx => addDefaultField(logic(ctx), ctx.fieldTree));
}

function validateStandardSchema(path, schema) {
  const VALIDATOR_MEMO = metadata(path, createMetadataKey(), ctx => {
    const resolvedSchema = typeof schema === 'function' ? schema(ctx) : schema;
    return resolvedSchema ? resolvedSchema['~standard'].validate(ctx.value()) : undefined;
  });
  validateTree(path, ({
    state,
    fieldTreeOf
  }) => {
    const result = state.metadata(VALIDATOR_MEMO)();
    if (!result || _isPromise(result)) {
      return [];
    }
    return result?.issues?.map(issue => standardIssueToFormTreeError(fieldTreeOf(path), issue)) ?? [];
  });
  validateAsync(path, {
    params: ({
      state
    }) => {
      const result = state.metadata(VALIDATOR_MEMO)();
      return result && _isPromise(result) ? result : undefined;
    },
    factory: params => {
      return resource({
        params,
        loader: async ({
          params
        }) => (await params)?.issues ?? []
      });
    },
    onSuccess: (issues, {
      fieldTreeOf
    }) => {
      return issues.map(issue => standardIssueToFormTreeError(fieldTreeOf(path), issue));
    },
    onError: () => {}
  });
}
function standardSchemaError(issue, options) {
  return new StandardSchemaValidationError(issue, options);
}
function standardIssueToFormTreeError(fieldTree, issue) {
  let target = fieldTree;
  for (const pathPart of issue.path ?? []) {
    const pathKey = typeof pathPart === 'object' ? pathPart.key : pathPart;
    target = target[pathKey];
  }
  return addDefaultField(standardSchemaError(issue, {
    message: issue.message
  }), target);
}
class StandardSchemaValidationError extends BaseNgValidationError {
  issue;
  kind = 'standardSchema';
  constructor(issue, options) {
    super(options);
    this.issue = issue;
  }
}

function validateHttp(path, opts) {
  validateAsync(path, {
    params: opts.request,
    factory: request => httpResource(request, opts.options),
    onSuccess: opts.onSuccess,
    onError: opts.onError
  });
}

function debounce(path, durationOrDebouncer) {
  assertPathIsCurrent(path);
  const pathNode = FieldPathNode.unwrapFieldPath(path);
  const debouncer = typeof durationOrDebouncer === 'function' ? durationOrDebouncer : durationOrDebouncer > 0 ? debounceForDuration(durationOrDebouncer) : immediate;
  pathNode.builder.addMetadataRule(DEBOUNCER, () => debouncer);
}
function debounceForDuration(durationInMilliseconds) {
  return (_context, abortSignal) => {
    return new Promise(resolve => {
      let timeoutId;
      const onAbort = () => {
        clearTimeout(timeoutId);
        resolve();
      };
      timeoutId = setTimeout(() => {
        abortSignal.removeEventListener('abort', onAbort);
        resolve();
      }, durationInMilliseconds);
      abortSignal.addEventListener('abort', onAbort, {
        once: true
      });
    });
  };
}
function immediate() {}

const FORM_FIELD_PARSE_ERRORS = new InjectionToken(typeof ngDevMode !== 'undefined' && ngDevMode ? 'FORM_FIELD_PARSE_ERRORS' : '');

function createParser(getValue, setValue, parse) {
  const errors = linkedSignal({
    ...(ngDevMode ? {
      debugName: "errors"
    } : {}),
    source: getValue,
    computation: () => []
  });
  const setRawValue = rawValue => {
    const result = parse(rawValue);
    errors.set(normalizeErrors(result.error));
    if (result.value !== undefined) {
      setValue(result.value);
    }
    errors.set(normalizeErrors(result.error));
  };
  return {
    errors: errors.asReadonly(),
    setRawValue
  };
}

function transformedValue(value, options) {
  const {
    parse,
    format
  } = options;
  const parser = createParser(value, value.set, parse);
  const formFieldParseErrors = inject(FORM_FIELD_PARSE_ERRORS, {
    self: true,
    optional: true
  });
  if (formFieldParseErrors) {
    formFieldParseErrors.set(parser.errors);
  }
  const rawValue = linkedSignal(() => format(value()), ...(ngDevMode ? [{
    debugName: "rawValue"
  }] : []));
  const result = rawValue;
  result.parseErrors = parser.errors;
  const originalSet = result.set.bind(result);
  result.set = newRawValue => {
    parser.setRawValue(newRawValue);
    originalSet(newRawValue);
  };
  result.update = updateFn => {
    result.set(updateFn(rawValue()));
  };
  return result;
}

class InteropNgControl {
  field;
  constructor(field) {
    this.field = field;
  }
  control = this;
  get value() {
    return this.field().value();
  }
  get valid() {
    return this.field().valid();
  }
  get invalid() {
    return this.field().invalid();
  }
  get pending() {
    return this.field().pending();
  }
  get disabled() {
    return this.field().disabled();
  }
  get enabled() {
    return !this.field().disabled();
  }
  get errors() {
    return signalErrorsToValidationErrors(this.field().errors());
  }
  get pristine() {
    return !this.field().dirty();
  }
  get dirty() {
    return this.field().dirty();
  }
  get touched() {
    return this.field().touched();
  }
  get untouched() {
    return !this.field().touched();
  }
  get status() {
    if (this.field().disabled()) {
      return 'DISABLED';
    }
    if (this.field().valid()) {
      return 'VALID';
    }
    if (this.field().invalid()) {
      return 'INVALID';
    }
    if (this.field().pending()) {
      return 'PENDING';
    }
    throw new _RuntimeError(1910, ngDevMode && 'Unknown form control status');
  }
  valueAccessor = null;
  hasValidator(validator) {
    if (validator === Validators.required) {
      return this.field().required();
    }
    return false;
  }
  updateValueAndValidity() {}
}

const FIELD_STATE_KEY_TO_CONTROL_BINDING = {
  disabled: 'disabled',
  disabledReasons: 'disabledReasons',
  dirty: 'dirty',
  errors: 'errors',
  hidden: 'hidden',
  invalid: 'invalid',
  max: 'max',
  maxLength: 'maxLength',
  min: 'min',
  minLength: 'minLength',
  name: 'name',
  pattern: 'pattern',
  pending: 'pending',
  readonly: 'readonly',
  required: 'required',
  touched: 'touched'
};
const CONTROL_BINDING_TO_FIELD_STATE_KEY = /* @__PURE__ */(() => {
  const map = {};
  for (const key of Object.keys(FIELD_STATE_KEY_TO_CONTROL_BINDING)) {
    map[FIELD_STATE_KEY_TO_CONTROL_BINDING[key]] = key;
  }
  return map;
})();
function readFieldStateBindingValue(fieldState, key) {
  const property = CONTROL_BINDING_TO_FIELD_STATE_KEY[key];
  return fieldState[property]?.();
}
const CONTROL_BINDING_NAMES = /* @__PURE__ */(() => Object.values(FIELD_STATE_KEY_TO_CONTROL_BINDING))();
function createBindings() {
  return {};
}
function bindingUpdated(bindings, key, value) {
  if (bindings[key] !== value) {
    bindings[key] = value;
    return true;
  }
  return false;
}

function isNativeFormElement(element) {
  return element.tagName === 'INPUT' || element.tagName === 'SELECT' || element.tagName === 'TEXTAREA';
}
function isNumericFormElement(element) {
  if (element.tagName !== 'INPUT') {
    return false;
  }
  const type = element.type;
  return type === 'date' || type === 'datetime-local' || type === 'month' || type === 'number' || type === 'range' || type === 'time' || type === 'week';
}
function isTextualFormElement(element) {
  return element.tagName === 'INPUT' || element.tagName === 'TEXTAREA';
}
function getNativeControlValue(element, currentValue) {
  let modelValue;
  if (element.validity.badInput) {
    return {
      error: new NativeInputParseError()
    };
  }
  switch (element.type) {
    case 'checkbox':
      return {
        value: element.checked
      };
    case 'number':
    case 'range':
    case 'datetime-local':
      modelValue = untracked(currentValue);
      if (typeof modelValue === 'number' || modelValue === null) {
        return {
          value: element.value === '' ? null : element.valueAsNumber
        };
      }
      break;
    case 'date':
    case 'month':
    case 'time':
    case 'week':
      modelValue = untracked(currentValue);
      if (modelValue === null || modelValue instanceof Date) {
        return {
          value: element.valueAsDate
        };
      } else if (typeof modelValue === 'number') {
        return {
          value: element.valueAsNumber
        };
      }
      break;
  }
  return {
    value: element.value
  };
}
function setNativeControlValue(element, value) {
  switch (element.type) {
    case 'checkbox':
      element.checked = value;
      return;
    case 'radio':
      element.checked = value === element.value;
      return;
    case 'number':
    case 'range':
    case 'datetime-local':
      if (typeof value === 'number') {
        setNativeNumberControlValue(element, value);
        return;
      } else if (value === null) {
        element.value = '';
        return;
      }
      break;
    case 'date':
    case 'month':
    case 'time':
    case 'week':
      if (value === null || value instanceof Date) {
        element.valueAsDate = value;
        return;
      } else if (typeof value === 'number') {
        setNativeNumberControlValue(element, value);
        return;
      }
  }
  element.value = value;
}
function setNativeNumberControlValue(element, value) {
  if (isNaN(value)) {
    element.value = '';
  } else {
    element.valueAsNumber = value;
  }
}
function setNativeDomProperty(renderer, element, name, value) {
  switch (name) {
    case 'name':
      renderer.setAttribute(element, name, value);
      break;
    case 'disabled':
    case 'readonly':
    case 'required':
      if (value) {
        renderer.setAttribute(element, name, '');
      } else {
        renderer.removeAttribute(element, name);
      }
      break;
    case 'max':
    case 'min':
    case 'minLength':
    case 'maxLength':
      if (value !== undefined) {
        renderer.setAttribute(element, name, value.toString());
      } else {
        renderer.removeAttribute(element, name);
      }
      break;
  }
}

function customControlCreate(host, parent) {
  host.listenToCustomControlModel(value => parent.state().controlValue.set(value));
  host.listenToCustomControlOutput('touchedChange', () => parent.state().markAsTouched());
  parent.registerAsBinding(host.customControl);
  const bindings = createBindings();
  return () => {
    const state = parent.state();
    const controlValue = state.controlValue();
    if (bindingUpdated(bindings, 'controlValue', controlValue)) {
      host.setCustomControlModelInput(controlValue);
    }
    for (const name of CONTROL_BINDING_NAMES) {
      let value;
      if (name === 'errors') {
        value = parent.errors();
      } else {
        value = readFieldStateBindingValue(state, name);
      }
      if (bindingUpdated(bindings, name, value)) {
        host.setInputOnDirectives(name, value);
        if (parent.elementAcceptsNativeProperty(name) && !host.customControlHasInput(name)) {
          setNativeDomProperty(parent.renderer, parent.nativeFormElement, name, value);
        }
      }
    }
  };
}

function cvaControlCreate(host, parent) {
  parent.controlValueAccessor.registerOnChange(value => parent.state().controlValue.set(value));
  parent.controlValueAccessor.registerOnTouched(() => parent.state().markAsTouched());
  parent.registerAsBinding();
  const bindings = createBindings();
  return () => {
    const fieldState = parent.state();
    const value = fieldState.value();
    if (bindingUpdated(bindings, 'controlValue', value)) {
      untracked(() => parent.controlValueAccessor.writeValue(value));
    }
    for (const name of CONTROL_BINDING_NAMES) {
      const value = readFieldStateBindingValue(fieldState, name);
      if (bindingUpdated(bindings, name, value)) {
        const propertyWasSet = host.setInputOnDirectives(name, value);
        if (name === 'disabled' && parent.controlValueAccessor.setDisabledState) {
          untracked(() => parent.controlValueAccessor.setDisabledState(value));
        } else if (!propertyWasSet && parent.elementAcceptsNativeProperty(name)) {
          setNativeDomProperty(parent.renderer, parent.nativeFormElement, name, value);
        }
      }
    }
  };
}

function observeSelectMutations(select, onMutation, destroyRef) {
  if (typeof MutationObserver !== 'function') {
    return;
  }
  const observer = new MutationObserver(mutations => {
    if (mutations.some(m => isRelevantSelectMutation(m))) {
      onMutation();
    }
  });
  observer.observe(select, {
    attributes: true,
    attributeFilter: ['value'],
    characterData: true,
    childList: true,
    subtree: true
  });
  destroyRef.onDestroy(() => observer.disconnect());
}
function isRelevantSelectMutation(mutation) {
  if (mutation.type === 'childList' || mutation.type === 'characterData') {
    if (mutation.target instanceof Comment) {
      return false;
    }
    for (const node of mutation.addedNodes) {
      if (!(node instanceof Comment)) {
        return true;
      }
    }
    for (const node of mutation.removedNodes) {
      if (!(node instanceof Comment)) {
        return true;
      }
    }
    return false;
  }
  if (mutation.type === 'attributes' && mutation.target instanceof HTMLOptionElement) {
    return true;
  }
  return false;
}

function nativeControlCreate(host, parent, parseErrorsSource) {
  let updateMode = false;
  const input = parent.nativeFormElement;
  const parser = createParser(() => parent.state().value(), rawValue => parent.state().controlValue.set(rawValue), () => getNativeControlValue(input, parent.state().value));
  parseErrorsSource.set(parser.errors);
  host.listenToDom('input', () => parser.setRawValue(undefined));
  host.listenToDom('blur', () => parent.state().markAsTouched());
  parent.registerAsBinding();
  if (input.tagName === 'SELECT') {
    observeSelectMutations(input, () => {
      if (!updateMode) {
        return;
      }
      input.value = parent.state().controlValue();
    }, parent.destroyRef);
  }
  const bindings = createBindings();
  return () => {
    const state = parent.state();
    const controlValue = state.controlValue();
    if (bindingUpdated(bindings, 'controlValue', controlValue)) {
      setNativeControlValue(input, controlValue);
    }
    for (const name of CONTROL_BINDING_NAMES) {
      const value = readFieldStateBindingValue(state, name);
      if (bindingUpdated(bindings, name, value)) {
        host.setInputOnDirectives(name, value);
        if (parent.elementAcceptsNativeProperty(name)) {
          setNativeDomProperty(parent.renderer, input, name, value);
        }
      }
    }
    updateMode = true;
  };
}

const ɵNgFieldDirective = Symbol();
const FORM_FIELD = new InjectionToken(typeof ngDevMode !== 'undefined' && ngDevMode ? 'FORM_FIELD' : '');
class FormField {
  field = input.required({
    ...(ngDevMode ? {
      debugName: "field"
    } : {}),
    alias: 'formField'
  });
  renderer = inject(Renderer2);
  destroyRef = inject(DestroyRef);
  state = computed(() => this.field()(), ...(ngDevMode ? [{
    debugName: "state"
  }] : []));
  injector = inject(Injector);
  element = inject(ElementRef).nativeElement;
  elementIsNativeFormElement = isNativeFormElement(this.element);
  elementAcceptsNumericValues = isNumericFormElement(this.element);
  elementAcceptsTextualValues = isTextualFormElement(this.element);
  nativeFormElement = this.elementIsNativeFormElement ? this.element : undefined;
  focuser = options => this.element.focus(options);
  controlValueAccessors = inject(NG_VALUE_ACCESSOR, {
    optional: true,
    self: true
  });
  config = inject(SIGNAL_FORMS_CONFIG, {
    optional: true
  });
  parseErrorsSource = signal(undefined, ...(ngDevMode ? [{
    debugName: "parseErrorsSource"
  }] : []));
  _interopNgControl;
  get interopNgControl() {
    return this._interopNgControl ??= new InteropNgControl(this.state);
  }
  parseErrors = computed(() => this.parseErrorsSource()?.().map(err => ({
    ...err,
    fieldTree: untracked(this.state).fieldTree,
    formField: this
  })) ?? [], ...(ngDevMode ? [{
    debugName: "parseErrors"
  }] : []));
  errors = computed(() => this.state().errors().filter(err => !err.formField || err.formField === this), ...(ngDevMode ? [{
    debugName: "errors"
  }] : []));
  isFieldBinding = false;
  get controlValueAccessor() {
    return this.controlValueAccessors?.[0] ?? this.interopNgControl?.valueAccessor ?? undefined;
  }
  installClassBindingEffect() {
    const classes = Object.entries(this.config?.classes ?? {}).map(([className, computation]) => [className, computed(() => computation(this))]);
    if (classes.length === 0) {
      return;
    }
    const bindings = createBindings();
    afterRenderEffect({
      write: () => {
        for (const [className, computation] of classes) {
          const active = computation();
          if (bindingUpdated(bindings, className, active)) {
            if (active) {
              this.renderer.addClass(this.element, className);
            } else {
              this.renderer.removeClass(this.element, className);
            }
          }
        }
      }
    }, {
      injector: this.injector
    });
  }
  focus(options) {
    this.focuser(options);
  }
  registerAsBinding(bindingOptions) {
    if (this.isFieldBinding) {
      throw new _RuntimeError(1913, typeof ngDevMode !== 'undefined' && ngDevMode && 'FormField already registered as a binding');
    }
    this.isFieldBinding = true;
    this.installClassBindingEffect();
    if (bindingOptions?.focus) {
      this.focuser = focusOptions => bindingOptions.focus(focusOptions);
    }
    effect(onCleanup => {
      const fieldNode = this.state();
      fieldNode.nodeState.formFieldBindings.update(controls => [...controls, this]);
      onCleanup(() => {
        fieldNode.nodeState.formFieldBindings.update(controls => controls.filter(c => c !== this));
      });
    }, {
      injector: this.injector
    });
    if (typeof ngDevMode !== 'undefined' && ngDevMode) {
      effect(() => {
        const fieldNode = this.state();
        if (fieldNode.hidden()) {
          const path = fieldNode.structure.pathKeys().join('.') || '<root>';
          console.warn(_formatRuntimeError(1916, `Field '${path}' is hidden but is being rendered. ` + `Hidden fields should be removed from the DOM using @if.`));
        }
      }, {
        injector: this.injector
      });
    }
  }
  [ɵNgFieldDirective];
  ɵngControlCreate(host) {
    if (host.hasPassThrough) {
      return;
    }
    if (this.controlValueAccessor) {
      this.ɵngControlUpdate = cvaControlCreate(host, this);
    } else if (host.customControl) {
      this.ɵngControlUpdate = customControlCreate(host, this);
    } else if (this.elementIsNativeFormElement) {
      this.ɵngControlUpdate = nativeControlCreate(host, this, this.parseErrorsSource);
    } else {
      throw new _RuntimeError(1914, typeof ngDevMode !== 'undefined' && ngDevMode && `${host.descriptor} is an invalid [formField] directive host. The host must be a native form control ` + `(such as <input>', '<select>', or '<textarea>') or a custom form control with a 'value' or ` + `'checked' model.`);
    }
  }
  ɵngControlUpdate;
  elementAcceptsNativeProperty(key) {
    if (!this.elementIsNativeFormElement) {
      return false;
    }
    switch (key) {
      case 'min':
      case 'max':
        return this.elementAcceptsNumericValues;
      case 'minLength':
      case 'maxLength':
        return this.elementAcceptsTextualValues;
      case 'disabled':
      case 'required':
      case 'readonly':
      case 'name':
        return true;
      default:
        return false;
    }
  }
  static ɵfac = i0.ɵɵngDeclareFactory({
    minVersion: "12.0.0",
    version: "21.2.1",
    ngImport: i0,
    type: FormField,
    deps: [],
    target: i0.ɵɵFactoryTarget.Directive
  });
  static ɵdir = i0.ɵɵngDeclareDirective({
    minVersion: "17.1.0",
    version: "21.2.1",
    type: FormField,
    isStandalone: true,
    selector: "[formField]",
    inputs: {
      field: {
        classPropertyName: "field",
        publicName: "formField",
        isSignal: true,
        isRequired: true,
        transformFunction: null
      }
    },
    providers: [{
      provide: FORM_FIELD,
      useExisting: FormField
    }, {
      provide: NgControl,
      useFactory: () => inject(FormField).interopNgControl
    }, {
      provide: FORM_FIELD_PARSE_ERRORS,
      useFactory: () => inject(FormField).parseErrorsSource
    }],
    exportAs: ["formField"],
    controlCreate: {
      passThroughInput: "formField"
    },
    ngImport: i0
  });
}
i0.ɵɵngDeclareClassMetadata({
  minVersion: "12.0.0",
  version: "21.2.1",
  ngImport: i0,
  type: FormField,
  decorators: [{
    type: Directive,
    args: [{
      selector: '[formField]',
      exportAs: 'formField',
      providers: [{
        provide: FORM_FIELD,
        useExisting: FormField
      }, {
        provide: NgControl,
        useFactory: () => inject(FormField).interopNgControl
      }, {
        provide: FORM_FIELD_PARSE_ERRORS,
        useFactory: () => inject(FormField).parseErrorsSource
      }]
    }]
  }],
  propDecorators: {
    field: [{
      type: i0.Input,
      args: [{
        isSignal: true,
        alias: "formField",
        required: true
      }]
    }]
  }
});

class FormRoot {
  fieldTree = input.required({
    ...(ngDevMode ? {
      debugName: "fieldTree"
    } : {}),
    alias: 'formRoot'
  });
  onSubmit(event) {
    event.preventDefault();
    submit(this.fieldTree());
  }
  static ɵfac = i0.ɵɵngDeclareFactory({
    minVersion: "12.0.0",
    version: "21.2.1",
    ngImport: i0,
    type: FormRoot,
    deps: [],
    target: i0.ɵɵFactoryTarget.Directive
  });
  static ɵdir = i0.ɵɵngDeclareDirective({
    minVersion: "17.1.0",
    version: "21.2.1",
    type: FormRoot,
    isStandalone: true,
    selector: "form[formRoot]",
    inputs: {
      fieldTree: {
        classPropertyName: "fieldTree",
        publicName: "formRoot",
        isSignal: true,
        isRequired: true,
        transformFunction: null
      }
    },
    host: {
      attributes: {
        "novalidate": ""
      },
      listeners: {
        "submit": "onSubmit($event)"
      }
    },
    ngImport: i0
  });
}
i0.ɵɵngDeclareClassMetadata({
  minVersion: "12.0.0",
  version: "21.2.1",
  ngImport: i0,
  type: FormRoot,
  decorators: [{
    type: Directive,
    args: [{
      selector: 'form[formRoot]',
      host: {
        'novalidate': '',
        '(submit)': 'onSubmit($event)'
      }
    }]
  }],
  propDecorators: {
    fieldTree: [{
      type: i0.Input,
      args: [{
        isSignal: true,
        alias: "formRoot",
        required: true
      }]
    }]
  }
});

export { BaseNgValidationError, EmailValidationError, FORM_FIELD, FormField, FormRoot, MAX, MAX_LENGTH, MIN, MIN_LENGTH, MaxLengthValidationError, MaxValidationError, MinLengthValidationError, MinValidationError, NativeInputParseError, NgValidationError, PATTERN, PatternValidationError, REQUIRED, RequiredValidationError, StandardSchemaValidationError, createManagedMetadataKey, createMetadataKey, debounce, disabled, email, emailError, hidden, max, maxError, maxLength, maxLengthError, metadata, min, minError, minLength, minLengthError, pattern, patternError, provideSignalFormsConfig, readonly, required, requiredError, standardSchemaError, submit, transformedValue, validate, validateAsync, validateHttp, validateStandardSchema, validateTree, ɵNgFieldDirective };
//# sourceMappingURL=signals.mjs.map
