Skip to content

Commit 022aac3

Browse files
committed
feat(form): enhance form hooks with stronger types, docs, and improved field/validation logic
1 parent 72b74ec commit 022aac3

File tree

9 files changed

+762
-45
lines changed

9 files changed

+762
-45
lines changed

primitives/filed-form /src/react/hooks/FieldContext.ts

Lines changed: 170 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
'use client';
22
/* eslint-disable no-bitwise */
33

4+
/**
5+
* Form field context and type definitions
6+
* Provides comprehensive type-safe interfaces for form state management,
7+
* field operations, validation, and React context integration
8+
*/
9+
410
import { createContext, useContext } from 'react';
511
import type {
612
AllPathsKeys,
@@ -19,12 +25,21 @@ import type { FieldEntity, Meta, StoreValue } from '../../form-core/types';
1925
import type { ValidateMessages } from '../../form-core/validate';
2026
import type { Rule, ValidateOptions } from '../../form-core/validation';
2127

28+
/**
29+
* Interface for list item rendering in dynamic arrays
30+
* Provides stable keys and field names for array field management
31+
*/
2232
export type ListRenderItem = {
33+
/** Stable key for React rendering optimization */
2334
key: string;
35+
/** Field name path for this array item */
2436
name: string;
2537
};
2638

27-
// Core change: wrap each path with Meta
39+
/**
40+
* Utility type to build hierarchical metadata structure from field paths
41+
* Recursively wraps each path segment with Meta information
42+
*/
2843
type BuildMetaShape<T, P extends string> = P extends `${infer K}.${infer R}`
2944
? K extends keyof T
3045
? T[K] extends readonly (infer U)[]
@@ -37,160 +52,299 @@ type BuildMetaShape<T, P extends string> = P extends `${infer K}.${infer R}`
3752
: never
3853
: never;
3954

40-
// Convert an array of paths into a hierarchical Meta structure
55+
/**
56+
* Converts an array of field paths into a hierarchical Meta structure
57+
* Provides type-safe access to field metadata based on field paths
58+
*/
4159
export type MetaShapeFromPaths<T, Ps extends readonly string[]> = Ps extends never[] | []
4260
? { [K in keyof T]: Meta<K & string, PathToDeepType<T, K & string>> }
4361
: MergeUnion<Ps[number] extends infer P ? (P extends string ? BuildMetaShape<T, P> : never) : never>;
4462

63+
/**
64+
* Comprehensive form state interface
65+
* Contains all form-level and field-level state information
66+
*/
4567
export interface FormState<Values = any> {
68+
/** Map of fields that have been modified from initial values */
4669
dirtyFields: Record<AllPathsKeys<Values>, boolean>;
70+
/** Map of field validation errors */
4771
errors: Record<AllPathsKeys<Values>, string[]>;
72+
/** Initial form values */
4873
initialValues: DeepPartial<Values>;
49-
// === Global booleans ===
50-
isDirty: boolean;
5174

75+
// === Global state flags ===
76+
/** Whether any field has been modified */
77+
isDirty: boolean;
78+
/** Whether the last submission was successful */
5279
isSubmitSuccessful: boolean;
53-
// === Submission lifecycle ===
80+
/** Whether the form has been submitted at least once */
5481
isSubmitted: boolean;
82+
/** Whether the form is currently being submitted */
5583
isSubmitting: boolean;
84+
/** Whether all fields pass validation */
5685
isValid: boolean;
57-
86+
/** Whether any field is currently being validated */
5887
isValidating: boolean;
88+
/** Number of times the form has been submitted */
5989
submitCount: number;
60-
// === Field-level states ===
61-
touchedFields: Record<AllPathsKeys<Values>, boolean>;
6290

91+
// === Field-level state maps ===
92+
/** Map of fields that have been interacted with */
93+
touchedFields: Record<AllPathsKeys<Values>, boolean>;
94+
/** Map of fields that have completed validation */
6395
validatedFields: Record<AllPathsKeys<Values>, boolean>;
96+
/** Map of fields currently being validated */
6497
validatingFields: Record<AllPathsKeys<Values>, boolean>;
65-
// === Meta states ===
98+
99+
// === Form data ===
100+
/** Current form values */
66101
values: Values;
102+
/** Map of field validation warnings */
67103
warnings: Record<AllPathsKeys<Values>, string[]>;
68104
}
69105

106+
/**
107+
* Interface for form value operations
108+
* Provides methods to get and set form field values
109+
*/
70110
export interface ValuesOptions<Values = any> {
111+
/** Get array operations for a specific array field */
71112
arrayOp: <K extends ArrayKeys<Values>>(
72113
name: K
73114
) => {
115+
/** Insert item at specified index */
74116
insert: (index: number, item: ArrayElementValue<Values, K>) => void;
117+
/** Move item from one index to another */
75118
move: (from: number, to: number) => void;
119+
/** Remove item at specified index */
76120
remove: (index: number) => void;
121+
/** Replace item at index with new value */
77122
replace: (index: number, val: ArrayElementValue<Values, K>) => void;
123+
/** Swap positions of two items */
78124
swap: (i: number, j: number) => void;
79125
};
126+
/** Get values of specified fields or all fields */
80127
getFieldsValue: <K extends AllPathsKeys<Values>[]>(name?: K) => ShapeFromPaths<Values, K>;
128+
/** Get value of a specific field */
81129
getFieldValue: <T extends AllPathsKeys<Values>>(name: T) => PathToDeepType<Values, T>;
130+
/** Set values for multiple fields */
82131
setFieldsValue: (values: DeepPartial<Values>) => void;
132+
/** Set value for a specific field */
83133
setFieldValue: <T extends AllPathsKeys<Values>>(name: T, value: PathToDeepType<Values, T>) => void;
84134
}
85135

136+
/**
137+
* Interface for form state query operations
138+
* Provides methods to access field metadata and form state
139+
*/
86140
export interface StateOptions<Values = any> {
141+
/** Get complete metadata for a specific field */
87142
getField: <T extends AllPathsKeys<Values>>(name: T) => Meta<T, PathToDeepType<Values, T>>;
143+
/** Get validation errors for a specific field */
88144
getFieldError: (name: AllPathsKeys<Values>) => string[];
145+
/** Get metadata for specified fields or all fields */
89146
getFields: <T extends AllPathsKeys<Values>[]>(names?: T) => MetaShapeFromPaths<Values, T>;
147+
/** Get validation errors for specified fields */
90148
getFieldsError: (names?: AllPathsKeys<Values>[]) => Record<AllPathsKeys<Values>, string[]>;
149+
/** Check if any of the specified fields have been touched */
91150
getFieldsTouched: (names?: AllPathsKeys<Values>[]) => boolean;
151+
/** Check if any of the specified fields have been validated */
92152
getFieldsValidated: (names?: AllPathsKeys<Values>[]) => boolean;
153+
/** Check if any of the specified fields are currently validating */
93154
getFieldsValidating: (names?: AllPathsKeys<Values>[]) => boolean;
155+
/** Get validation warnings for specified fields */
94156
getFieldsWarning: (names?: AllPathsKeys<Values>[]) => Record<AllPathsKeys<Values>, string[]>;
157+
/** Check if a specific field has been touched */
95158
getFieldTouched: (name: AllPathsKeys<Values>) => boolean;
159+
/** Check if a specific field has been validated */
96160
getFieldValidated: (name: AllPathsKeys<Values>) => boolean;
161+
/** Check if a specific field is currently validating */
97162
getFieldValidating: (name: AllPathsKeys<Values>) => boolean;
163+
/** Get validation warnings for a specific field */
98164
getFieldWarning: (name: AllPathsKeys<Values>) => string[];
165+
/** Get complete form state snapshot */
99166
getFormState: () => FormState;
100167
}
101168

169+
/**
170+
* Extended validation options for multiple fields
171+
*/
102172
export interface ValidateFieldsOptions extends ValidateOptions {
173+
/** Only validate fields that have been modified */
103174
dirty?: boolean;
104175
}
105176

177+
/**
178+
* Interface for form operation methods
179+
* Provides methods to perform form actions like validation, reset, and submission
180+
*/
106181
export interface OperationOptions<Values = any> {
182+
/** Reset specified fields to their initial values */
107183
resetFields: (names?: AllPathsKeys<Values>[]) => void;
184+
/** Submit the form after validation */
108185
submit: () => void;
186+
/** Add middleware to the form processing pipeline */
109187
use: (mw: Middleware<Values, AllPathsKeys<Values>>) => void;
188+
/** Validate a specific field */
110189
validateField: (name: AllPathsKeys<Values>, opts?: ValidateOptions) => Promise<boolean>;
190+
/** Validate multiple fields */
111191
validateFields: (names?: AllPathsKeys<Values>[], opts?: ValidateFieldsOptions) => Promise<boolean>;
112192
}
113193

194+
/**
195+
* Interface for validation error information
196+
* Used when form submission fails validation
197+
*/
114198
export interface ValidateErrorEntity<Values = any> {
115-
// For UI to scroll directly
116-
errorCount: number; // Optional: values filtered by _pruneForSubmit (for analytics/replay)
117-
errorFields: Meta<string, any>[]; // Per-field list (used to scroll to the first error and show items)
118-
errorMap: Record<string, string[]>; // Same as above: warnings
119-
firstErrorName?: string; // Full current values (not pruned)
199+
/** Total number of fields with validation errors */
200+
errorCount: number;
201+
/** Array of field metadata with errors (for UI scrolling and display) */
202+
errorFields: Meta<string, any>[];
203+
/** Map of field names to their error messages */
204+
errorMap: Record<string, string[]>;
205+
/** Name of the first field with an error (for auto-focus) */
206+
firstErrorName?: string;
207+
/** Timestamp when validation failed */
120208
submittedAt: number;
121-
values: Values; // Fast index (header red dot, side group statistics)
209+
/** Current form values at time of validation failure */
210+
values: Values;
211+
/** Map of field names to their warning messages */
122212
warningMap: Record<string, string[]>;
123213
}
124214

215+
/**
216+
* Interface for form lifecycle callback options
217+
* Defines callbacks that can be registered to respond to form events
218+
*/
125219
export interface RegisterCallbackOptions<Values = any> {
220+
/** Called when field metadata changes (errors, validation state, etc.) */
126221
onFieldsChange?: (
127222
changedFields: Meta<AllPathsKeys<Values>, PathToDeepType<Values, AllPathsKeys<Values>>>[],
128223
allFields: Meta<AllPathsKeys<Values>, PathToDeepType<Values, AllPathsKeys<Values>>>[]
129224
) => void;
130225

226+
/** Called when form submission succeeds validation */
131227
onFinish?: (values: Values) => void;
132228

229+
/** Called when form submission fails validation */
133230
onFinishFailed?: (errorInfo: ValidateErrorEntity<Values>) => void;
134231

232+
/** Called when form values change */
135233
onValuesChange?: (changedValues: Partial<Values>, values: Values) => void;
136234
}
137235

236+
/**
237+
* Interface for internal form configuration callbacks
238+
* Used internally to configure form behavior and lifecycle
239+
*/
138240
export interface InternalCallbacks<Values = any> {
241+
/** Destroy form instance and optionally clear data */
139242
destroyForm: (clearOnDestroy?: boolean) => void;
243+
/** Set form lifecycle callbacks */
140244
setCallbacks: (callbacks: RegisterCallbackOptions<Values>) => void;
245+
/** Set initial form values */
141246
setInitialValues: (values: DeepPartial<Values>) => void;
247+
/** Set field preservation behavior */
142248
setPreserve: (preserve: boolean) => void;
249+
/** Set custom validation messages */
143250
setValidateMessages: (messages: ValidateMessages) => void;
144251
}
145252

253+
/**
254+
* Interface for internal field management hooks
255+
* Provides low-level field operations and state management
256+
*/
146257
export interface InternalFieldHooks<Values = any> {
258+
/** Perform array operations on array fields */
147259
arrayOp: (name: AllPathsKeys<Values>, args: ArrayOpArgs) => void;
260+
/** Dispatch actions to the form store */
148261
dispatch: (action: Action) => void;
262+
/** Get array field items with stable keys */
149263
getArrayFields: (name: ArrayKeys<Values>, initialValue?: StoreValue[]) => ListRenderItem[];
264+
/** Get initial value for a field */
150265
getInitialValue: <T extends AllPathsKeys<Values>>(name: T) => PathToDeepType<Values, T>;
266+
/** Register a computed field with dependencies */
151267
registerComputed: <T extends AllPathsKeys<Values>>(
152268
name: T,
153269
deps: AllPathsKeys<Values>[],
154270
compute: (get: (n: AllPathsKeys<Values>) => any, all: Values) => PathToDeepType<Values, T>
155271
) => () => void;
272+
/** Register a reactive effect with dependencies */
156273
registerEffect: (
157274
deps: AllPathsKeys<Values>[],
158275
effect: (get: (n: AllPathsKeys<Values>) => any, all: Values) => void
159276
) => () => void;
277+
/** Register a field entity for state management */
160278
registerField: (entity: FieldEntity) => () => void;
279+
/** Set validation rules for a field */
161280
setFieldRules: (name: AllPathsKeys<Values>, rules?: Rule[]) => void;
281+
/** Set values for multiple fields */
162282
setFieldsValue: (values: DeepPartial<Values>) => void;
283+
/** Set value for a specific field */
163284
setFieldValue: (name: AllPathsKeys<Values>, value: PathToDeepType<Values, AllPathsKeys<Values>>) => void;
285+
/** Set validation rules for a field (alias for setFieldRules) */
164286
setRules: (name: AllPathsKeys<Values>, rules?: Rule[]) => void;
287+
/** Subscribe to field changes with optional filtering */
165288
subscribeField: <T extends AllPathsKeys<Values>>(
166289
name: T | T[] | undefined,
167290
cb: (value: PathToDeepType<Values, T>, name: T, values: Values, mask: ChangeMask) => void,
168291
opt?: { includeChildren?: boolean; mask?: ChangeMask }
169292
) => () => void;
293+
/** Execute function within a transaction for batched updates */
170294
transaction: <T>(fn: () => T) => T;
295+
/** Execute async function within a transaction for batched updates */
171296
transactionAsync: <T>(fn: () => Promise<T>) => Promise<T>;
172297
}
173298

299+
/**
300+
* Main form instance interface
301+
* Combines all form operation interfaces for external API
302+
*/
174303
export interface FormInstance<Values = any>
175304
extends ValuesOptions<Values>,
176305
StateOptions<Values>,
177306
OperationOptions<Values> {}
178307

308+
/**
309+
* Combined interface for all internal form hooks
310+
* Used internally for form configuration and field management
311+
*/
179312
export interface InternalFormHooks<Values = any> extends InternalCallbacks<Values>, InternalFieldHooks<Values> {}
180313

314+
/**
315+
* Internal form context interface
316+
* Extends FormInstance with additional context-specific properties
317+
*/
181318
export interface InternalFormContext<Values = any> extends FormInstance<Values> {
319+
/** Default validation trigger events for fields */
182320
validateTrigger: string | string[];
183321
}
184322

323+
/**
324+
* Complete internal form instance interface
325+
* Provides access to both public API and internal hooks
326+
*/
185327
export interface InternalFormInstance<Values = any> extends InternalFormContext<Values> {
186-
/** Internal API, not recommended for external use */
328+
/** Internal API for accessing low-level form operations - not recommended for external use */
187329
getInternalHooks: () => InternalFormHooks<Values>;
188330
}
189331

332+
/**
333+
* React context for form field management
334+
* Provides form instance and configuration to child components
335+
*/
190336
export const FieldContext = createContext<InternalFormContext | null>(null);
191337

338+
/**
339+
* Context provider component for form fields
340+
* Wraps form components to provide form context
341+
*/
192342
export const FieldContextProvider = FieldContext.Provider;
193343

344+
/**
345+
* Hook to access form context from child components
346+
* Returns the form instance and configuration from the nearest Form provider
347+
*/
194348
export const useFieldContext = <Values = any>(): InternalFormContext<Values> | null => {
195349
const context = useContext(FieldContext);
196350

0 commit comments

Comments
 (0)