diff --git a/packages/form-core/src/FieldApi.ts b/packages/form-core/src/FieldApi.ts index b44dfdee..9b7dba28 100644 --- a/packages/form-core/src/FieldApi.ts +++ b/packages/form-core/src/FieldApi.ts @@ -335,6 +335,10 @@ export type FieldMeta = { * A flag indicating whether the field has been touched. */ isTouched: boolean + /** + * A flag indicating whether the field has been blurred. + */ + isBlurred: boolean /** * A flag that is `true` if the field's value has not been modified by the user. Opposite of `isDirty`. */ @@ -456,6 +460,7 @@ export class FieldApi< meta: this._getMeta() ?? { isValidating: false, isTouched: false, + isBlurred: false, isDirty: false, isPristine: true, errors: [], @@ -625,6 +630,7 @@ export class FieldApi< ({ isValidating: false, isTouched: false, + isBlurred: false, isDirty: false, isPristine: true, errors: [], @@ -1001,6 +1007,9 @@ export class FieldApi< this.setMeta((prev) => ({ ...prev, isTouched: true })) this.validate('change') } + if (!this.state.meta.isBlurred) { + this.setMeta((prev) => ({ ...prev, isBlurred: true })) + } this.validate('blur') } diff --git a/packages/form-core/src/FormApi.ts b/packages/form-core/src/FormApi.ts index 27f6159c..7e4714f3 100644 --- a/packages/form-core/src/FormApi.ts +++ b/packages/form-core/src/FormApi.ts @@ -259,6 +259,10 @@ export type FormState = { * A boolean indicating if any of the form fields have been touched. */ isTouched: boolean + /** + * A boolean indicating if any of the form fields have been blurred. + */ + isBlurred: boolean /** * A boolean indicating if any of the form's fields' values have been modified by the user. `True` if the user have modified at least one of the fields. Opposite of `isPristine`. */ @@ -305,6 +309,7 @@ function getDefaultFormState( isSubmitted: defaultState.isSubmitted ?? false, isSubmitting: defaultState.isSubmitting ?? false, isTouched: defaultState.isTouched ?? false, + isBlurred: defaultState.isBlurred ?? false, isPristine: defaultState.isPristine ?? true, isDirty: defaultState.isDirty ?? false, isValid: defaultState.isValid ?? false, @@ -388,6 +393,7 @@ export class FormApi< ) const isTouched = fieldMetaValues.some((field) => field?.isTouched) + const isBlurred = fieldMetaValues.some((field) => field?.isBlurred) const isDirty = fieldMetaValues.some((field) => field?.isDirty) const isPristine = !isDirty @@ -410,6 +416,7 @@ export class FormApi< isValid, canSubmit, isTouched, + isBlurred, isPristine, isDirty, } @@ -553,6 +560,12 @@ export class FormApi< // Mark them as touched field.instance.setMeta((prev) => ({ ...prev, isTouched: true })) } + + // If any fields are not blurred + if (!field.instance.state.meta.isBlurred) { + // Mark them as blurred + field.instance.setMeta((prev) => ({ ...prev, isBlurred: true })) + } }) }) @@ -616,6 +629,12 @@ export class FormApi< fieldInstance.setMeta((prev) => ({ ...prev, isTouched: true })) } + // If the field is not blurred (same logic as in validateAllFields) + if (!fieldInstance.state.meta.isBlurred) { + // Mark it as blurred + fieldInstance.setMeta((prev) => ({ ...prev, isBlurred: true })) + } + return fieldInstance.validate(cause) } @@ -983,6 +1002,7 @@ export class FormApi< acc[fieldKey] = { isValidating: false, isTouched: false, + isBlurred: false, isDirty: false, isPristine: true, errors: [], @@ -1009,6 +1029,7 @@ export class FormApi< this.setFieldMeta(field, (prev) => ({ ...prev, isTouched: true, + isBlurred: true, isDirty: true, })) } diff --git a/packages/form-core/tests/FieldApi.spec.ts b/packages/form-core/tests/FieldApi.spec.ts index 9887a1b4..5200b23a 100644 --- a/packages/form-core/tests/FieldApi.spec.ts +++ b/packages/form-core/tests/FieldApi.spec.ts @@ -43,6 +43,7 @@ describe('field api', () => { expect(field.getMeta()).toEqual({ isTouched: false, + isBlurred: false, isValidating: false, isPristine: true, isDirty: false, @@ -56,11 +57,17 @@ describe('field api', () => { const field = new FieldApi({ form, name: 'name', - defaultMeta: { isTouched: true, isDirty: true, isPristine: false }, + defaultMeta: { + isTouched: true, + isDirty: true, + isPristine: false, + isBlurred: true, + }, }) expect(field.getMeta()).toEqual({ isTouched: true, + isBlurred: true, isValidating: false, isDirty: true, isPristine: false, diff --git a/packages/form-core/tests/FormApi.spec.ts b/packages/form-core/tests/FormApi.spec.ts index d966df27..31c9194e 100644 --- a/packages/form-core/tests/FormApi.spec.ts +++ b/packages/form-core/tests/FormApi.spec.ts @@ -19,6 +19,7 @@ describe('form api', () => { errorMap: {}, isSubmitting: false, isTouched: false, + isBlurred: false, isPristine: true, isDirty: false, isValid: true, @@ -55,6 +56,7 @@ describe('form api', () => { isSubmitted: false, isSubmitting: false, isTouched: false, + isBlurred: false, isPristine: true, isDirty: false, isValid: true, @@ -89,6 +91,7 @@ describe('form api', () => { isSubmitted: false, isSubmitting: false, isTouched: false, + isBlurred: false, isPristine: true, isDirty: false, isValid: true, @@ -134,6 +137,7 @@ describe('form api', () => { isSubmitted: false, isSubmitting: false, isTouched: false, + isBlurred: false, isPristine: true, isDirty: false, isValid: true, @@ -178,6 +182,7 @@ describe('form api', () => { isSubmitted: false, isSubmitting: false, isTouched: false, + isBlurred: false, isPristine: true, isDirty: false, isValid: true, @@ -226,6 +231,7 @@ describe('form api', () => { name: { isValidating: false, isTouched: false, + isBlurred: false, isDirty: false, isPristine: true, errors: [], @@ -240,6 +246,7 @@ describe('form api', () => { isSubmitted: false, isSubmitting: false, isTouched: false, + isBlurred: false, isPristine: true, isDirty: false, isValid: true,