import { dynamic } from "@solidjs/web";
import { createEffect, createMemo, omit, untrack } from "solid-js";
import { useFieldRootContext } from "../../internals/field-context.jsx";
import { fieldStateAttrs } from "../../internals/field-state-attrs.jsx";
import { useAriaLabelledBy } from "../../internals/labelable-provider/use-aria-labelled-by.jsx";
import { useLabelableId } from "../../internals/labelable-provider/use-labelable-id.jsx";
import { mergeRootProps } from "../../internals/merge-root-props.jsx";
import { resolveClassStyle } from "../../internals/resolve-class-style.jsx";
import { useFieldControlRegistration } from "../../internals/use-field-control-registration/use-field-control-registration.jsx";
import { useFieldMessagesContext } from "../field-messages-context.jsx";
/**
 * The form control to label and validate. Renders an `<input>` element.
 *
 * Wires:
 *   - id registration into the surrounding `<LabelableProvider>` (so
 *     `<Field.Label>` can point `htmlFor` at us).
 *   - value registration into the surrounding `<Field.Root>` (so form-level
 *     validation / submit focus can find us).
 *   - `aria-labelledby` composed from any labels registered with the provider.
 *   - `aria-describedby` composed from descriptions + errors registered with
 *     the field-messages registry.
 *   - `onInput` / `onBlur` / `onFocus` into the root's touched/dirty/filled/
 *     focused setters and the validation pipeline (`validation.commit` on
 *     blur when `validationMode === 'onBlur'`; `validation.getInputValidationProps()`
 *     merges an `onInput`-driven commit for `onChange`/`onSubmit` modes).
 */
export function FieldControl(props) {
  const field = useFieldRootContext();
  const messages = useFieldMessagesContext();

  // Register control id with the labelable-provider so `<Field.Label>`'s
  // `htmlFor` wires up. `useLabelableId` handles the one-shot registration
  // + cleanup. We pass `props.id` (read once, belt-and-braces via
  // `untrack` in the hook itself) to honour a consumer override.
  const {
    id
  } = useLabelableId({
    id: untrack(() => typeof props.id === "string" ? props.id : undefined)
  });
  const disabled = createMemo(() => field.disabled() || props.disabled === true);
  const state = createMemo(() => ({
    ...field.state(),
    disabled: disabled()
  }));

  // Register our live value with the surrounding `<Field.Root>`. The root
  // uses this to key the control into `<Form>`'s `fields` map and to
  // refresh the registration on value changes (via `useValueChanged`).
  // `getValue` reads directly from the input element — matches React's
  // "validation.inputRef.current?.value" fallback.
  useFieldControlRegistration({
    value: () => props.value,
    id: () => id,
    controlRef: field.validation.inputRef,
    getValue: () => field.validation.inputRef.current?.value
  });

  // Keep `filled` in sync with either an external `value` (if controlled)
  // or the input element's `value` attribute on mount. Mirrors React's
  // `useIsoLayoutEffect` branch in FieldControl.
  createEffect(() => {
    const externalValue = props.value;
    return externalValue;
  }, externalValue => {
    const hasExternal = externalValue != null;
    const input = field.validation.inputRef.current;
    if (input?.value || hasExternal && externalValue !== "") {
      field.setFilled(true);
    } else if (hasExternal && externalValue === "") {
      field.setFilled(false);
    }
  });
  const ariaLabelledBy = useAriaLabelledBy();
  const ariaDescribedBy = createMemo(() => {
    const ids = messages.getMessageIds();
    if (ids.length === 0) return undefined;
    return ids.join(" ");
  });
  const userStyling = resolveClassStyle(props, () => state());
  const userHtmlProps = omit(props, "class", "style", "attrs", "as", "id");
  const internalProps = mergeRootProps(fieldStateAttrs(state), {
    id,
    ref: el => {
      field.validation.inputRef.current = el;
    },
    get disabled() {
      return disabled();
    },
    get name() {
      return field.name() ?? props.name;
    },
    get "data-disabled"() {
      return disabled() ? "" : undefined;
    },
    get "aria-labelledby"() {
      return ariaLabelledBy() ?? "";
    },
    get "aria-describedby"() {
      return ariaDescribedBy() ?? "";
    },
    onFocus: () => {
      field.setFocused(true);
    },
    onBlur: event => {
      field.setTouched(true);
      field.setFocused(false);
      if (field.validationMode() === "onBlur") {
        void field.validation.commit(event.currentTarget.value);
      }
    },
    onInput: event => {
      const value = event.currentTarget.value;
      field.setDirty(value !== field.validityData().initialValue);
      field.setFilled(value !== "");
    },
    onKeyDown: event => {
      if (event.currentTarget.tagName === "INPUT" && event.key === "Enter") {
        field.setTouched(true);
        void field.validation.commit(event.currentTarget.value);
      }
    }
  });
  const styledUserProps = mergeRootProps(userStyling, userHtmlProps);
  const merged = mergeRootProps(mergeRootProps(internalProps, field.validation.getInputValidationProps()), styledUserProps);
  const Tag = dynamic(() => props.as ?? "input");
  return <Tag {...merged} />;
}
//# sourceMappingURL=field-control.jsx.map
