diff --git a/packages/form-core/src/FieldApi.ts b/packages/form-core/src/FieldApi.ts index dac3c2571..31c508c8c 100644 --- a/packages/form-core/src/FieldApi.ts +++ b/packages/form-core/src/FieldApi.ts @@ -20,6 +20,7 @@ import type { FieldInfo, FormApi, FormAsyncValidateOrFn, + FormState, FormValidateAsyncFn, FormValidateFn, FormValidateOrFn, @@ -100,6 +101,7 @@ export type FieldValidateFn< any, any, any, + any, any > }) => unknown @@ -183,6 +185,7 @@ export type FieldValidateAsyncFn< any, any, any, + any, any > signal: AbortSignal @@ -265,10 +268,39 @@ export type FieldListenerFn< any, any, any, + any, any > }) => void +/** + * @private + */ +export type FieldMetaFn< + TParentData, + TFormOnMount extends undefined | FormValidateOrFn, + TFormOnChange extends undefined | FormValidateOrFn, + TFormOnChangeAsync extends undefined | FormAsyncValidateOrFn, + TFormOnBlur extends undefined | FormValidateOrFn, + TFormOnBlurAsync extends undefined | FormAsyncValidateOrFn, + TFormOnSubmit extends undefined | FormValidateOrFn, + TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn, + TFormOnServer extends undefined | FormAsyncValidateOrFn, + TFieldMetaExtension extends object, +> = ( + props: FormState< + TParentData, + TFormOnMount, + TFormOnChange, + TFormOnChangeAsync, + TFormOnBlur, + TFormOnBlurAsync, + TFormOnSubmit, + TFormOnSubmitAsync, + TFormOnServer + >, +) => TFieldMetaExtension + export interface FieldValidators< TParentData, TName extends DeepKeys, @@ -383,6 +415,15 @@ export interface FieldOptions< TOnSubmitAsync extends | undefined | FieldAsyncValidateOrFn, + TFormOnMount extends undefined | FormValidateOrFn, + TFormOnChange extends undefined | FormValidateOrFn, + TFormOnChangeAsync extends undefined | FormAsyncValidateOrFn, + TFormOnBlur extends undefined | FormValidateOrFn, + TFormOnBlurAsync extends undefined | FormAsyncValidateOrFn, + TFormOnSubmit extends undefined | FormValidateOrFn, + TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn, + TFormOnServer extends undefined | FormAsyncValidateOrFn, + TFieldMetaExtension extends object, > { /** * The field name. The type will be `DeepKeys` to ensure your name is a deep key of the parent dataset. @@ -436,13 +477,30 @@ export interface FieldOptions< any, any, any, - any + any, + TFieldMetaExtension > > /** * A list of listeners which attach to the corresponding events */ listeners?: FieldListeners + + /** + * A list of listeners which attach to the corresponding events + */ + meta?: FieldMetaFn< + TParentData, + TFormOnMount, + TFormOnChange, + TFormOnChangeAsync, + TFormOnBlur, + TFormOnBlurAsync, + TFormOnSubmit, + TFormOnSubmitAsync, + TFormOnServer, + TFieldMetaExtension + > /** * Disable the `flat(1)` operation on `field.errors`. This is useful if you want to keep the error structure as is. Not suggested for most use-cases. */ @@ -492,6 +550,7 @@ export interface FieldApiOptions< | FormAsyncValidateOrFn, in out TFormOnServer extends undefined | FormAsyncValidateOrFn, in out TParentSubmitMeta, + in out TFieldMetaExtension extends object, > extends FieldOptions< TParentData, TName, @@ -502,7 +561,16 @@ export interface FieldApiOptions< TOnBlur, TOnBlurAsync, TOnSubmit, - TOnSubmitAsync + TOnSubmitAsync, + TFormOnMount, + TFormOnChange, + TFormOnChangeAsync, + TFormOnBlur, + TFormOnBlurAsync, + TFormOnSubmit, + TFormOnSubmitAsync, + TFormOnServer, + TFieldMetaExtension > { form: FormApi< TParentData, @@ -542,6 +610,7 @@ export type FieldMetaBase< TFormOnBlurAsync extends undefined | FormAsyncValidateOrFn, TFormOnSubmit extends undefined | FormValidateOrFn, TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn, + TFieldMetaExtension extends object = {}, > = { /** * A flag indicating whether the field has been touched. @@ -575,7 +644,7 @@ export type FieldMetaBase< * A flag indicating whether the field is currently being validated. */ isValidating: boolean -} +} & TFieldMetaExtension export type AnyFieldMetaBase = FieldMetaBase< any, @@ -594,6 +663,7 @@ export type AnyFieldMetaBase = FieldMetaBase< any, any, any, + any, any > @@ -621,6 +691,7 @@ export type FieldMetaDerived< TFormOnBlurAsync extends undefined | FormAsyncValidateOrFn, TFormOnSubmit extends undefined | FormValidateOrFn, TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn, + TFieldMetaExtension extends object = {}, > = { /** * An array of errors related to the field value. @@ -660,7 +731,7 @@ export type FieldMetaDerived< * A flag indicating whether the field's current value is the default value */ isDefaultValue: boolean -} +} & TFieldMetaExtension export type AnyFieldMetaDerived = FieldMetaDerived< any, @@ -679,6 +750,7 @@ export type AnyFieldMetaDerived = FieldMetaDerived< any, any, any, + any, any > @@ -709,6 +781,7 @@ export type FieldMeta< TFormOnBlurAsync extends undefined | FormAsyncValidateOrFn, TFormOnSubmit extends undefined | FormValidateOrFn, TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn, + TFieldMetaExtension extends object = {}, > = FieldMetaBase< TParentData, TName, @@ -726,7 +799,8 @@ export type FieldMeta< TFormOnBlur, TFormOnBlurAsync, TFormOnSubmit, - TFormOnSubmitAsync + TFormOnSubmitAsync, + TFieldMetaExtension > & FieldMetaDerived< TParentData, @@ -745,7 +819,8 @@ export type FieldMeta< TFormOnBlur, TFormOnBlurAsync, TFormOnSubmit, - TFormOnSubmitAsync + TFormOnSubmitAsync, + TFieldMetaExtension > export type AnyFieldMeta = FieldMeta< @@ -765,6 +840,7 @@ export type AnyFieldMeta = FieldMeta< any, any, any, + any, any > @@ -795,6 +871,7 @@ export type FieldState< TFormOnBlurAsync extends undefined | FormAsyncValidateOrFn, TFormOnSubmit extends undefined | FormValidateOrFn, TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn, + TFieldMetaExtension extends object, > = { /** * The current value of the field. @@ -820,7 +897,8 @@ export type FieldState< TFormOnBlur, TFormOnBlurAsync, TFormOnSubmit, - TFormOnSubmitAsync + TFormOnSubmitAsync, + TFieldMetaExtension > } @@ -848,6 +926,7 @@ export type AnyFieldApi = FieldApi< any, any, any, + any, any > @@ -900,6 +979,7 @@ export class FieldApi< | FormAsyncValidateOrFn, in out TFormOnServer extends undefined | FormAsyncValidateOrFn, in out TParentSubmitMeta, + in out TFieldMetaExtension extends object, > { /** * A reference to the form API instance. @@ -923,7 +1003,8 @@ export class FieldApi< TFormOnSubmit, TFormOnSubmitAsync, TFormOnServer, - TParentSubmitMeta + TParentSubmitMeta, + TFieldMetaExtension >['form'] /** * The field name. @@ -951,7 +1032,8 @@ export class FieldApi< TFormOnSubmit, TFormOnSubmitAsync, TFormOnServer, - TParentSubmitMeta + TParentSubmitMeta, + TFieldMetaExtension > = {} as any /** * The field state store. @@ -974,7 +1056,8 @@ export class FieldApi< TFormOnBlur, TFormOnBlurAsync, TFormOnSubmit, - TFormOnSubmitAsync + TFormOnSubmitAsync, + TFieldMetaExtension > > /** @@ -1012,7 +1095,8 @@ export class FieldApi< TFormOnSubmit, TFormOnSubmitAsync, TFormOnServer, - TParentSubmitMeta + TParentSubmitMeta, + TFieldMetaExtension >, ) { this.form = opts.form as never @@ -1052,7 +1136,8 @@ export class FieldApi< TFormOnBlur, TFormOnBlurAsync, TFormOnSubmit, - TFormOnSubmitAsync + TFormOnSubmitAsync, + TFieldMetaExtension > }, }) @@ -1136,6 +1221,11 @@ export class FieldApi< fieldApi: this, }) + this.setMeta((prev) => ({ + ...prev, + ...this.options.meta?.(this.form.state), + })) + return cleanup } @@ -1162,7 +1252,8 @@ export class FieldApi< TFormOnSubmit, TFormOnSubmitAsync, TFormOnServer, - TParentSubmitMeta + TParentSubmitMeta, + TFieldMetaExtension >, ) => { this.options = opts as never @@ -1211,6 +1302,11 @@ export class FieldApi< this.triggerOnChangeListener() + this.setMeta((prev) => ({ + ...prev, + ...this.options.meta?.(this.form.state), + })) + this.validate('change') } @@ -1238,7 +1334,8 @@ export class FieldApi< TFormOnBlur, TFormOnBlurAsync, TFormOnSubmit, - TFormOnSubmitAsync + TFormOnSubmitAsync, + TFieldMetaExtension > >, ) => this.form.setFieldMeta(this.name, updater) diff --git a/packages/form-core/src/FormApi.ts b/packages/form-core/src/FormApi.ts index 77174199c..6ea791e10 100644 --- a/packages/form-core/src/FormApi.ts +++ b/packages/form-core/src/FormApi.ts @@ -484,6 +484,7 @@ export type FieldInfo = { any, any, any, + any, any > | null /** diff --git a/packages/form-core/tests/FieldApi.spec.ts b/packages/form-core/tests/FieldApi.spec.ts index c10c3fa9c..19695c712 100644 --- a/packages/form-core/tests/FieldApi.spec.ts +++ b/packages/form-core/tests/FieldApi.spec.ts @@ -2457,4 +2457,27 @@ describe('field api', () => { expect(field.getMeta().errorSourceMap.onChange).toEqual('field') }) + + it('should have user defined meta and react to value change', () => { + const form = new FormApi({ + defaultValues: { + name: 'Stegosaurus', + }, + }) + form.mount() + + const nameField = new FieldApi({ + form, + name: 'name', + meta: ({ values }) => ({ + dinosaur: values.name === 'Stegosaurus' ? 'dino' : 'notDino', + }), + }) + + nameField.mount() + expect(nameField.getMeta().dinosaur).toEqual('dino') + + nameField.handleChange('Cat') + expect(nameField.getMeta().dinosaur).toEqual('notDino') + }) })