{"version":3,"file":"FieldGroupApi.cjs","sources":["../../src/FieldGroupApi.ts"],"sourcesContent":["import { createStore } from '@tanstack/store'\nimport { concatenatePaths, getBy, makePathArray } from './utils'\nimport type { ReadonlyStore } from '@tanstack/store'\nimport type { Updater } from './utils'\nimport type {\n  FormApi,\n  FormAsyncValidateOrFn,\n  FormValidateOrFn,\n} from './FormApi'\nimport type { AnyFieldMetaBase, FieldOptions } from './FieldApi'\nimport type {\n  DeepKeys,\n  DeepKeysOfType,\n  DeepValue,\n  FieldsMap,\n} from './util-types'\nimport type {\n  FieldManipulator,\n  UpdateMetaOptions,\n  ValidationCause,\n} from './types'\n\nexport type AnyFieldGroupApi = FieldGroupApi<\n  any,\n  any,\n  any,\n  any,\n  any,\n  any,\n  any,\n  any,\n  any,\n  any,\n  any,\n  any,\n  any,\n  any\n>\n\nexport interface FieldGroupState<in out TFieldGroupData> {\n  /**\n   * The current values of the field group\n   */\n  values: TFieldGroupData\n}\n\n/**\n * An object representing the options for a field group.\n */\nexport interface FieldGroupOptions<\n  in out TFormData,\n  in out TFieldGroupData,\n  in out TFields extends\n    | DeepKeysOfType<TFormData, TFieldGroupData | null | undefined>\n    | FieldsMap<TFormData, TFieldGroupData>,\n  in out TOnMount extends undefined | FormValidateOrFn<TFormData>,\n  in out TOnChange extends undefined | FormValidateOrFn<TFormData>,\n  in out TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n  in out TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n  in out TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n  in out TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n  in out TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n  in out TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n  in out TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n  in out TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n  in out TSubmitMeta = never,\n> {\n  form:\n    | FormApi<\n        TFormData,\n        TOnMount,\n        TOnChange,\n        TOnChangeAsync,\n        TOnBlur,\n        TOnBlurAsync,\n        TOnSubmit,\n        TOnSubmitAsync,\n        TOnDynamic,\n        TOnDynamicAsync,\n        TOnServer,\n        TSubmitMeta\n      >\n    | FieldGroupApi<\n        any,\n        TFormData,\n        any,\n        any,\n        any,\n        any,\n        any,\n        any,\n        any,\n        any,\n        any,\n        any,\n        any,\n        TSubmitMeta\n      >\n  /**\n   * The path to the field group data.\n   */\n  fields: TFields\n  /**\n   * The expected subsetValues that the form must provide.\n   */\n  defaultValues?: TFieldGroupData\n  /**\n   * onSubmitMeta, the data passed from the handleSubmit handler, to the onSubmit function props\n   */\n  onSubmitMeta?: TSubmitMeta\n}\n\nexport class FieldGroupApi<\n  in out TFormData,\n  in out TFieldGroupData,\n  in out TFields extends\n    | DeepKeysOfType<TFormData, TFieldGroupData | null | undefined>\n    | FieldsMap<TFormData, TFieldGroupData>,\n  in out TOnMount extends undefined | FormValidateOrFn<TFormData>,\n  in out TOnChange extends undefined | FormValidateOrFn<TFormData>,\n  in out TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n  in out TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n  in out TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n  in out TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n  in out TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n  in out TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n  in out TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n  in out TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n  in out TSubmitMeta = never,\n> implements FieldManipulator<TFieldGroupData, TSubmitMeta> {\n  /**\n   * The form that called this field group.\n   */\n  readonly form: FormApi<\n    TFormData,\n    TOnMount,\n    TOnChange,\n    TOnChangeAsync,\n    TOnBlur,\n    TOnBlurAsync,\n    TOnSubmit,\n    TOnSubmitAsync,\n    TOnDynamic,\n    TOnDynamicAsync,\n    TOnServer,\n    TSubmitMeta\n  >\n\n  readonly fieldsMap: TFields\n\n  /**\n   * Get the true name of the field. Not required within `Field` or `AppField`.\n   * @private\n   */\n  getFormFieldName = <TField extends DeepKeys<TFieldGroupData>>(\n    subfield: TField,\n  ): DeepKeys<TFormData> => {\n    if (typeof this.fieldsMap === 'string') {\n      return concatenatePaths(this.fieldsMap, subfield)\n    }\n\n    const firstAccessor = makePathArray(subfield)[0]\n    if (typeof firstAccessor !== 'string') {\n      // top-level arrays cannot be mapped\n      return ''\n    }\n\n    const restOfPath = subfield.slice(firstAccessor.length)\n    const formMappedPath =\n      // TFields is either a string or this. See guard above.\n      (this.fieldsMap as FieldsMap<TFormData, TFieldGroupData>)[\n        firstAccessor as keyof TFieldGroupData\n      ]\n\n    return concatenatePaths(formMappedPath, restOfPath)\n  }\n\n  /**\n   * Get the field options with the true form DeepKeys for validators\n   * @private\n   */\n  getFormFieldOptions = <\n    TOptions extends FieldOptions<\n      any,\n      any,\n      any,\n      any,\n      any,\n      any,\n      any,\n      any,\n      any,\n      any,\n      any,\n      any\n    >,\n  >(\n    props: TOptions,\n  ): TOptions => {\n    const newProps = { ...props }\n    const validators = newProps.validators\n\n    newProps.name = this.getFormFieldName(props.name)\n\n    if (\n      validators &&\n      (validators.onChangeListenTo || validators.onBlurListenTo)\n    ) {\n      const newValidators = { ...validators }\n\n      const remapListenTo = (listenTo: DeepKeys<any>[] | undefined) => {\n        if (!listenTo) return undefined\n        return listenTo.map((localFieldName) =>\n          this.getFormFieldName(localFieldName),\n        )\n      }\n\n      newValidators.onChangeListenTo = remapListenTo(\n        validators.onChangeListenTo,\n      )\n      newValidators.onBlurListenTo = remapListenTo(validators.onBlurListenTo)\n\n      newProps.validators = newValidators\n    }\n\n    return newProps\n  }\n\n  store: ReadonlyStore<FieldGroupState<TFieldGroupData>>\n\n  get state() {\n    return this.store.state\n  }\n\n  /**\n   * Constructs a new `FieldGroupApi` instance with the given form options.\n   */\n  constructor(\n    opts: FieldGroupOptions<\n      TFormData,\n      TFieldGroupData,\n      TFields,\n      TOnMount,\n      TOnChange,\n      TOnChangeAsync,\n      TOnBlur,\n      TOnBlurAsync,\n      TOnSubmit,\n      TOnSubmitAsync,\n      TOnDynamic,\n      TOnDynamicAsync,\n      TOnServer,\n      TSubmitMeta\n    >,\n  ) {\n    if (opts.form instanceof FieldGroupApi) {\n      const group = opts.form\n      this.form = group.form as never\n\n      // the DeepKey is already namespaced, so we need to ensure that we reference\n      // the form and not the group\n      if (typeof opts.fields === 'string') {\n        this.fieldsMap = group.getFormFieldName(opts.fields) as TFields\n      } else {\n        // TypeScript has a tough time with generics being a union for some reason\n        const fields = {\n          ...(opts.fields as FieldsMap<TFormData, TFieldGroupData>),\n        }\n        for (const key in fields) {\n          fields[key] = group.getFormFieldName(fields[key]) as never\n        }\n        this.fieldsMap = fields as never\n      }\n    } else {\n      this.form = opts.form\n      this.fieldsMap = opts.fields\n    }\n\n    this.store = createStore(() => {\n      const currFormStore = this.form.store.get()\n      let values: TFieldGroupData\n      if (typeof this.fieldsMap === 'string') {\n        // all values live at that name, so we can directly fetch it\n        values = getBy(currFormStore.values, this.fieldsMap)\n      } else {\n        // we need to fetch the values from all places where they were mapped from\n        values = {} as never\n        const fields: Record<keyof TFieldGroupData, string> = this\n          .fieldsMap as never\n        for (const key in fields) {\n          values[key] = getBy(currFormStore.values, fields[key])\n        }\n      }\n\n      return {\n        values,\n      }\n    })\n  }\n\n  /**\n   * Mounts the field group instance to listen to value changes.\n   *\n   * TODO: Remove\n   */\n  mount = () => {\n    return () => {}\n  }\n\n  /**\n   * Validates the children of a specified array in the form starting from a given index until the end using the correct handlers for a given validation type.\n   */\n  validateArrayFieldsStartingFrom = async <\n    TField extends DeepKeysOfType<TFieldGroupData, any[]>,\n  >(\n    field: TField,\n    index: number,\n    cause: ValidationCause,\n  ) => {\n    return this.form.validateArrayFieldsStartingFrom(\n      this.getFormFieldName(field),\n      index,\n      cause,\n    )\n  }\n\n  /**\n   * Validates a specified field in the form using the correct handlers for a given validation type.\n   */\n  validateField = <TField extends DeepKeys<TFieldGroupData>>(\n    field: TField,\n    cause: ValidationCause,\n  ) => {\n    return this.form.validateField(this.getFormFieldName(field), cause)\n  }\n\n  /**\n   * Handles the form submission, performs validation, and calls the appropriate onSubmit or onSubmitInvalid callbacks.\n   */\n  handleSubmit(): Promise<void>\n  handleSubmit(submitMeta: TSubmitMeta): Promise<void>\n  async handleSubmit(submitMeta?: TSubmitMeta): Promise<void> {\n    // cast is required since the implementation isn't one of the two overloads\n    return this.form.handleSubmit(submitMeta as any)\n  }\n\n  /**\n   * Gets the value of the specified field.\n   */\n  getFieldValue = <TField extends DeepKeys<TFieldGroupData>>(\n    field: TField,\n  ): DeepValue<TFieldGroupData, TField> => {\n    return this.form.getFieldValue(this.getFormFieldName(field)) as DeepValue<\n      TFieldGroupData,\n      TField\n    >\n  }\n\n  /**\n   * Gets the metadata of the specified field.\n   */\n  getFieldMeta = <TField extends DeepKeys<TFieldGroupData>>(field: TField) => {\n    return this.form.getFieldMeta(this.getFormFieldName(field))\n  }\n\n  /**\n   * Updates the metadata of the specified field.\n   */\n  setFieldMeta = <TField extends DeepKeys<TFieldGroupData>>(\n    field: TField,\n    updater: Updater<AnyFieldMetaBase>,\n  ) => {\n    return this.form.setFieldMeta(this.getFormFieldName(field), updater)\n  }\n\n  /**\n   * Sets the value of the specified field and optionally updates the touched state.\n   */\n  setFieldValue = <TField extends DeepKeys<TFieldGroupData>>(\n    field: TField,\n    updater: Updater<DeepValue<TFieldGroupData, TField>>,\n    opts?: UpdateMetaOptions,\n  ) => {\n    return this.form.setFieldValue(\n      this.getFormFieldName(field) as never,\n      updater as never,\n      opts,\n    )\n  }\n\n  /**\n   * Delete a field and its subfields.\n   */\n  deleteField = <TField extends DeepKeys<TFieldGroupData>>(field: TField) => {\n    return this.form.deleteField(this.getFormFieldName(field))\n  }\n\n  /**\n   * Pushes a value into an array field.\n   */\n  pushFieldValue = <TField extends DeepKeysOfType<TFieldGroupData, any[]>>(\n    field: TField,\n    value: DeepValue<TFieldGroupData, TField> extends any[]\n      ? DeepValue<TFieldGroupData, TField>[number]\n      : never,\n    opts?: UpdateMetaOptions,\n  ) => {\n    return this.form.pushFieldValue(\n      this.getFormFieldName(field),\n      // since unknown doesn't extend an array, it types `value` as never.\n      value as never,\n      opts,\n    )\n  }\n\n  /**\n   * Insert a value into an array field at the specified index.\n   */\n  insertFieldValue = async <\n    TField extends DeepKeysOfType<TFieldGroupData, any[]>,\n  >(\n    field: TField,\n    index: number,\n    value: DeepValue<TFieldGroupData, TField> extends any[]\n      ? DeepValue<TFieldGroupData, TField>[number]\n      : never,\n    opts?: UpdateMetaOptions,\n  ) => {\n    return this.form.insertFieldValue(\n      this.getFormFieldName(field),\n      index,\n      // since unknown doesn't extend an array, it types `value` as never.\n      value as never,\n      opts,\n    )\n  }\n\n  /**\n   * Replaces a value into an array field at the specified index.\n   */\n  replaceFieldValue = async <\n    TField extends DeepKeysOfType<TFieldGroupData, any[]>,\n  >(\n    field: TField,\n    index: number,\n    value: DeepValue<TFieldGroupData, TField> extends any[]\n      ? DeepValue<TFieldGroupData, TField>[number]\n      : never,\n    opts?: UpdateMetaOptions,\n  ) => {\n    return this.form.replaceFieldValue(\n      this.getFormFieldName(field),\n      index,\n      // since unknown doesn't extend an array, it types `value` as never.\n      value as never,\n      opts,\n    )\n  }\n\n  /**\n   * Removes a value from an array field at the specified index.\n   */\n  removeFieldValue = async <\n    TField extends DeepKeysOfType<TFieldGroupData, any[]>,\n  >(\n    field: TField,\n    index: number,\n    opts?: UpdateMetaOptions,\n  ) => {\n    return this.form.removeFieldValue(this.getFormFieldName(field), index, opts)\n  }\n\n  /**\n   * Swaps the values at the specified indices within an array field.\n   */\n  swapFieldValues = <TField extends DeepKeysOfType<TFieldGroupData, any[]>>(\n    field: TField,\n    index1: number,\n    index2: number,\n    opts?: UpdateMetaOptions,\n  ) => {\n    return this.form.swapFieldValues(\n      this.getFormFieldName(field),\n      index1,\n      index2,\n      opts,\n    )\n  }\n\n  /**\n   * Moves the value at the first specified index to the second specified index within an array field.\n   */\n  moveFieldValues = <TField extends DeepKeysOfType<TFieldGroupData, any[]>>(\n    field: TField,\n    index1: number,\n    index2: number,\n    opts?: UpdateMetaOptions,\n  ) => {\n    return this.form.moveFieldValues(\n      this.getFormFieldName(field),\n      index1,\n      index2,\n      opts,\n    )\n  }\n\n  clearFieldValues = <TField extends DeepKeysOfType<TFieldGroupData, any[]>>(\n    field: TField,\n    opts?: UpdateMetaOptions,\n  ) => {\n    return this.form.clearFieldValues(this.getFormFieldName(field), opts)\n  }\n\n  /**\n   * Resets the field value and meta to default state\n   */\n  resetField = <TField extends DeepKeys<TFieldGroupData>>(field: TField) => {\n    return this.form.resetField(this.getFormFieldName(field))\n  }\n\n  validateAllFields = (cause: ValidationCause) =>\n    this.form.validateAllFields(cause)\n}\n"],"names":["concatenatePaths","makePathArray","opts","createStore","getBy"],"mappings":"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"}