@page-speed/forms 0.5.2 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-232KNGJI.js +207 -0
- package/dist/chunk-232KNGJI.js.map +1 -0
- package/dist/chunk-24RPM43T.js +373 -0
- package/dist/chunk-24RPM43T.js.map +1 -0
- package/dist/chunk-27JUYRDE.cjs +173 -0
- package/dist/chunk-27JUYRDE.cjs.map +1 -0
- package/dist/chunk-5NT5T5XY.js +4136 -0
- package/dist/chunk-5NT5T5XY.js.map +1 -0
- package/dist/chunk-AVAKC6R7.cjs +236 -0
- package/dist/chunk-AVAKC6R7.cjs.map +1 -0
- package/dist/chunk-DKLLPKZN.cjs +238 -0
- package/dist/chunk-DKLLPKZN.cjs.map +1 -0
- package/dist/chunk-EX6CRLKG.cjs +397 -0
- package/dist/chunk-EX6CRLKG.cjs.map +1 -0
- package/dist/chunk-H6NNFV64.js +127 -0
- package/dist/chunk-H6NNFV64.js.map +1 -0
- package/dist/chunk-JBEWTBFH.js +217 -0
- package/dist/chunk-JBEWTBFH.js.map +1 -0
- package/dist/chunk-JBEZLX3H.cjs +138 -0
- package/dist/chunk-JBEZLX3H.cjs.map +1 -0
- package/dist/chunk-VLGZG2VP.js +150 -0
- package/dist/chunk-VLGZG2VP.js.map +1 -0
- package/dist/chunk-ZYFTT6DB.cjs +4169 -0
- package/dist/chunk-ZYFTT6DB.cjs.map +1 -0
- package/dist/core.cjs +23 -733
- package/dist/core.cjs.map +1 -1
- package/dist/core.js +3 -716
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +43 -738
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +3 -716
- package/dist/index.js.map +1 -1
- package/dist/inputs.cjs +44 -4359
- package/dist/inputs.cjs.map +1 -1
- package/dist/inputs.js +2 -4337
- package/dist/inputs.js.map +1 -1
- package/dist/integration.cjs +51 -4645
- package/dist/integration.cjs.map +1 -1
- package/dist/integration.js +37 -4631
- package/dist/integration.js.map +1 -1
- package/dist/validation-rules.cjs +75 -231
- package/dist/validation-rules.cjs.map +1 -1
- package/dist/validation-rules.js +1 -215
- package/dist/validation-rules.js.map +1 -1
- package/dist/validation-utils.cjs +43 -133
- package/dist/validation-utils.cjs.map +1 -1
- package/dist/validation-utils.js +1 -125
- package/dist/validation-utils.js.map +1 -1
- package/dist/validation.cjs +115 -364
- package/dist/validation.cjs.map +1 -1
- package/dist/validation.js +2 -339
- package/dist/validation.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkDKLLPKZN_cjs = require('./chunk-DKLLPKZN.cjs');
|
|
4
|
+
var React3 = require('react');
|
|
5
|
+
var react = require('@legendapp/state/react');
|
|
6
|
+
var useMap = require('@opensite/hooks/useMap');
|
|
7
|
+
|
|
8
|
+
function _interopNamespace(e) {
|
|
9
|
+
if (e && e.__esModule) return e;
|
|
10
|
+
var n = Object.create(null);
|
|
11
|
+
if (e) {
|
|
12
|
+
Object.keys(e).forEach(function (k) {
|
|
13
|
+
if (k !== 'default') {
|
|
14
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
15
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
get: function () { return e[k]; }
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
n.default = e;
|
|
23
|
+
return Object.freeze(n);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
var React3__namespace = /*#__PURE__*/_interopNamespace(React3);
|
|
27
|
+
|
|
28
|
+
function useForm(options) {
|
|
29
|
+
const {
|
|
30
|
+
initialValues,
|
|
31
|
+
validationSchema,
|
|
32
|
+
validateOn = "onBlur",
|
|
33
|
+
revalidateOn = "onChange",
|
|
34
|
+
onSubmit,
|
|
35
|
+
onError,
|
|
36
|
+
debug = false
|
|
37
|
+
} = options;
|
|
38
|
+
const state$ = react.useObservable({
|
|
39
|
+
values: initialValues,
|
|
40
|
+
errors: {},
|
|
41
|
+
touched: {},
|
|
42
|
+
isSubmitting: false,
|
|
43
|
+
status: "idle",
|
|
44
|
+
initialValues: { ...initialValues },
|
|
45
|
+
// Create a copy to prevent reference sharing
|
|
46
|
+
hasValidated: {}
|
|
47
|
+
});
|
|
48
|
+
const validationInProgress = React3.useRef(/* @__PURE__ */ new Set());
|
|
49
|
+
const [, fieldMetadataActions] = useMap.useMap();
|
|
50
|
+
const validateField = React3.useCallback(
|
|
51
|
+
async (field) => {
|
|
52
|
+
const validators = validationSchema?.[field];
|
|
53
|
+
if (!validators) return void 0;
|
|
54
|
+
const fieldKey = String(field);
|
|
55
|
+
validationInProgress.current.add(fieldKey);
|
|
56
|
+
const currentMeta = fieldMetadataActions.get(fieldKey) || {
|
|
57
|
+
validationCount: 0
|
|
58
|
+
};
|
|
59
|
+
fieldMetadataActions.set(fieldKey, {
|
|
60
|
+
lastValidated: Date.now(),
|
|
61
|
+
validationCount: currentMeta.validationCount + 1
|
|
62
|
+
});
|
|
63
|
+
try {
|
|
64
|
+
const value = state$.values[field].get();
|
|
65
|
+
const allValues = state$.values.get();
|
|
66
|
+
const validatorArray = Array.isArray(validators) ? validators : [validators];
|
|
67
|
+
for (const validator of validatorArray) {
|
|
68
|
+
const error = await validator(value, allValues);
|
|
69
|
+
if (error) {
|
|
70
|
+
state$.errors[field].set(error);
|
|
71
|
+
validationInProgress.current.delete(fieldKey);
|
|
72
|
+
return error;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
state$.errors[field].set(void 0);
|
|
76
|
+
validationInProgress.current.delete(fieldKey);
|
|
77
|
+
return void 0;
|
|
78
|
+
} catch (error) {
|
|
79
|
+
validationInProgress.current.delete(fieldKey);
|
|
80
|
+
const errorMessage = error instanceof Error ? error.message : "Validation error";
|
|
81
|
+
state$.errors[field].set(errorMessage);
|
|
82
|
+
return errorMessage;
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
[validationSchema, state$, fieldMetadataActions]
|
|
86
|
+
);
|
|
87
|
+
const validateForm = React3.useCallback(async () => {
|
|
88
|
+
if (!validationSchema) return {};
|
|
89
|
+
const fields = Object.keys(validationSchema);
|
|
90
|
+
const errors2 = {};
|
|
91
|
+
await Promise.all(
|
|
92
|
+
fields.map(async (field) => {
|
|
93
|
+
const error = await validateField(field);
|
|
94
|
+
if (error) {
|
|
95
|
+
errors2[field] = error;
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
);
|
|
99
|
+
state$.errors.set(errors2);
|
|
100
|
+
return errors2;
|
|
101
|
+
}, [validationSchema, validateField, state$]);
|
|
102
|
+
const setFieldValue = React3.useCallback(
|
|
103
|
+
(field, value) => {
|
|
104
|
+
state$.values[field].set(value);
|
|
105
|
+
const shouldRevalidate = revalidateOn === "onChange" && state$.hasValidated[String(field)].get();
|
|
106
|
+
if (shouldRevalidate && validationSchema?.[field]) {
|
|
107
|
+
validateField(field);
|
|
108
|
+
}
|
|
109
|
+
if (debug) {
|
|
110
|
+
console.log("[useForm] setFieldValue:", { field, value });
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
[state$, revalidateOn, validationSchema, validateField, debug]
|
|
114
|
+
);
|
|
115
|
+
const setFieldTouched = React3.useCallback(
|
|
116
|
+
(field, touched2) => {
|
|
117
|
+
state$.touched[field].set(touched2);
|
|
118
|
+
if (touched2 && validateOn === "onBlur" && validationSchema?.[field]) {
|
|
119
|
+
state$.hasValidated[String(field)].set(true);
|
|
120
|
+
validateField(field);
|
|
121
|
+
}
|
|
122
|
+
if (debug) {
|
|
123
|
+
console.log("[useForm] setFieldTouched:", { field, touched: touched2 });
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
[state$, validateOn, validationSchema, validateField, debug]
|
|
127
|
+
);
|
|
128
|
+
const resetForm = React3.useCallback(() => {
|
|
129
|
+
state$.values.set(state$.initialValues.get());
|
|
130
|
+
state$.errors.set({});
|
|
131
|
+
state$.touched.set({});
|
|
132
|
+
state$.isSubmitting.set(false);
|
|
133
|
+
state$.status.set("idle");
|
|
134
|
+
state$.hasValidated.set({});
|
|
135
|
+
fieldMetadataActions.clear();
|
|
136
|
+
if (debug) {
|
|
137
|
+
console.log("[useForm] Form reset");
|
|
138
|
+
}
|
|
139
|
+
}, [state$, fieldMetadataActions, debug]);
|
|
140
|
+
const handleSubmit = React3.useCallback(
|
|
141
|
+
async (e) => {
|
|
142
|
+
e?.preventDefault();
|
|
143
|
+
if (debug) {
|
|
144
|
+
console.log("[useForm] handleSubmit started");
|
|
145
|
+
}
|
|
146
|
+
state$.isSubmitting.set(true);
|
|
147
|
+
state$.status.set("submitting");
|
|
148
|
+
try {
|
|
149
|
+
const errors2 = await validateForm();
|
|
150
|
+
const hasErrors = Object.keys(errors2).length > 0;
|
|
151
|
+
if (hasErrors) {
|
|
152
|
+
state$.status.set("error");
|
|
153
|
+
onError?.(errors2);
|
|
154
|
+
if (debug) {
|
|
155
|
+
console.log("[useForm] Validation errors:", errors2);
|
|
156
|
+
}
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const helpers = {
|
|
160
|
+
setValues: (values2) => {
|
|
161
|
+
if (typeof values2 === "function") {
|
|
162
|
+
state$.values.set(values2(state$.values.get()));
|
|
163
|
+
} else {
|
|
164
|
+
state$.values.set(values2);
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
setFieldValue,
|
|
168
|
+
setErrors: (errors3) => state$.errors.set(errors3),
|
|
169
|
+
setFieldError: (field, error) => state$.errors[field].set(error),
|
|
170
|
+
setTouched: (touched2) => state$.touched.set(touched2),
|
|
171
|
+
setFieldTouched,
|
|
172
|
+
setSubmitting: (submitting) => state$.isSubmitting.set(submitting),
|
|
173
|
+
resetForm
|
|
174
|
+
};
|
|
175
|
+
await onSubmit(state$.values.get(), helpers);
|
|
176
|
+
state$.status.set("success");
|
|
177
|
+
if (debug) {
|
|
178
|
+
console.log("[useForm] Submit successful");
|
|
179
|
+
}
|
|
180
|
+
} catch (error) {
|
|
181
|
+
state$.status.set("error");
|
|
182
|
+
if (debug) {
|
|
183
|
+
console.error("[useForm] Submit error:", error);
|
|
184
|
+
}
|
|
185
|
+
throw error;
|
|
186
|
+
} finally {
|
|
187
|
+
state$.isSubmitting.set(false);
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
[
|
|
191
|
+
state$,
|
|
192
|
+
validateForm,
|
|
193
|
+
onSubmit,
|
|
194
|
+
onError,
|
|
195
|
+
setFieldValue,
|
|
196
|
+
setFieldTouched,
|
|
197
|
+
resetForm,
|
|
198
|
+
debug
|
|
199
|
+
]
|
|
200
|
+
);
|
|
201
|
+
const getFieldProps = React3.useCallback(
|
|
202
|
+
(field) => {
|
|
203
|
+
return {
|
|
204
|
+
name: String(field),
|
|
205
|
+
value: state$.values[field].get(),
|
|
206
|
+
onChange: (value) => setFieldValue(field, value),
|
|
207
|
+
onBlur: () => setFieldTouched(field, true)
|
|
208
|
+
};
|
|
209
|
+
},
|
|
210
|
+
[state$, setFieldValue, setFieldTouched]
|
|
211
|
+
);
|
|
212
|
+
const getFieldMeta = React3.useCallback(
|
|
213
|
+
(field) => {
|
|
214
|
+
const fieldKey = String(field);
|
|
215
|
+
const metadata = fieldMetadataActions.get(fieldKey);
|
|
216
|
+
return {
|
|
217
|
+
error: state$.errors[field].get(),
|
|
218
|
+
touched: state$.touched[field].get() ?? false,
|
|
219
|
+
isDirty: state$.values[field].get() !== state$.initialValues[field].get(),
|
|
220
|
+
isValidating: validationInProgress.current.has(fieldKey),
|
|
221
|
+
// Additional metadata from @opensite/hooks
|
|
222
|
+
validationCount: metadata?.validationCount,
|
|
223
|
+
lastValidated: metadata?.lastValidated
|
|
224
|
+
};
|
|
225
|
+
},
|
|
226
|
+
[state$, fieldMetadataActions]
|
|
227
|
+
);
|
|
228
|
+
const values = react.useSelector(() => state$.values.get());
|
|
229
|
+
const errors = react.useSelector(() => state$.errors.get());
|
|
230
|
+
const touched = react.useSelector(() => state$.touched.get());
|
|
231
|
+
const isSubmitting = react.useSelector(() => state$.isSubmitting.get());
|
|
232
|
+
const status = react.useSelector(() => state$.status.get());
|
|
233
|
+
const isValid = react.useSelector(() => Object.keys(state$.errors.get()).length === 0);
|
|
234
|
+
const isDirty = react.useSelector(() => {
|
|
235
|
+
const currentValues = state$.values.get();
|
|
236
|
+
const initialValues2 = state$.initialValues.get();
|
|
237
|
+
return Object.keys(currentValues).some(
|
|
238
|
+
(key) => currentValues[key] !== initialValues2[key]
|
|
239
|
+
);
|
|
240
|
+
});
|
|
241
|
+
return {
|
|
242
|
+
// State
|
|
243
|
+
values,
|
|
244
|
+
errors,
|
|
245
|
+
touched,
|
|
246
|
+
isSubmitting,
|
|
247
|
+
isValid,
|
|
248
|
+
isDirty,
|
|
249
|
+
status,
|
|
250
|
+
// Actions
|
|
251
|
+
handleSubmit,
|
|
252
|
+
setValues: (values2) => {
|
|
253
|
+
if (typeof values2 === "function") {
|
|
254
|
+
state$.values.set(values2(state$.values.get()));
|
|
255
|
+
} else {
|
|
256
|
+
state$.values.set(values2);
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
setFieldValue,
|
|
260
|
+
setErrors: (errors2) => state$.errors.set(errors2),
|
|
261
|
+
setFieldError: (field, error) => state$.errors[field].set(error),
|
|
262
|
+
setTouched: (touched2) => state$.touched.set(touched2),
|
|
263
|
+
setFieldTouched,
|
|
264
|
+
validateForm,
|
|
265
|
+
validateField,
|
|
266
|
+
resetForm,
|
|
267
|
+
getFieldProps,
|
|
268
|
+
getFieldMeta
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
var FormContext = React3__namespace.createContext(null);
|
|
272
|
+
FormContext.displayName = "FormContext";
|
|
273
|
+
function useField(options) {
|
|
274
|
+
const { name, validate, transform } = options;
|
|
275
|
+
const form = React3.useContext(FormContext);
|
|
276
|
+
if (!form) {
|
|
277
|
+
throw new Error(
|
|
278
|
+
"useField must be used within a FormContext. Wrap your component with <Form> or use useForm's getFieldProps instead."
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
const baseFieldProps = form.getFieldProps(name);
|
|
282
|
+
const field = {
|
|
283
|
+
...baseFieldProps,
|
|
284
|
+
value: baseFieldProps.value,
|
|
285
|
+
onChange: (value) => {
|
|
286
|
+
const transformedValue = transform ? transform(value) : value;
|
|
287
|
+
baseFieldProps.onChange(transformedValue);
|
|
288
|
+
if (validate) {
|
|
289
|
+
const result = validate(transformedValue, form.values);
|
|
290
|
+
if (result instanceof Promise) {
|
|
291
|
+
result.then((error) => {
|
|
292
|
+
if (error !== void 0) {
|
|
293
|
+
form.setFieldError(name, error);
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
} else if (result !== void 0) {
|
|
297
|
+
form.setFieldError(name, result);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
const meta = form.getFieldMeta(name);
|
|
303
|
+
const helpers = {
|
|
304
|
+
setValue: React3.useCallback(
|
|
305
|
+
(value) => {
|
|
306
|
+
const transformedValue = transform ? transform(value) : value;
|
|
307
|
+
form.setFieldValue(name, transformedValue);
|
|
308
|
+
},
|
|
309
|
+
[name, transform, form]
|
|
310
|
+
),
|
|
311
|
+
setTouched: React3.useCallback(
|
|
312
|
+
(touched) => {
|
|
313
|
+
form.setFieldTouched(name, touched);
|
|
314
|
+
},
|
|
315
|
+
[name, form]
|
|
316
|
+
),
|
|
317
|
+
setError: React3.useCallback(
|
|
318
|
+
(error) => {
|
|
319
|
+
form.setFieldError(name, error);
|
|
320
|
+
},
|
|
321
|
+
[name, form]
|
|
322
|
+
)
|
|
323
|
+
};
|
|
324
|
+
return {
|
|
325
|
+
field,
|
|
326
|
+
meta,
|
|
327
|
+
helpers
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
var FieldFeedback = ({
|
|
331
|
+
errorId,
|
|
332
|
+
errorClassName,
|
|
333
|
+
error,
|
|
334
|
+
shouldRenderError
|
|
335
|
+
}) => {
|
|
336
|
+
const errorText = Array.isArray(error) ? error.join(", ") : error;
|
|
337
|
+
if (!errorText || !shouldRenderError) return null;
|
|
338
|
+
return /* @__PURE__ */ React3__namespace.createElement(chunkDKLLPKZN_cjs.FieldError, { id: errorId, className: errorClassName }, errorText);
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
// src/core/Field.tsx
|
|
342
|
+
function Field2({
|
|
343
|
+
name,
|
|
344
|
+
label,
|
|
345
|
+
description,
|
|
346
|
+
children,
|
|
347
|
+
showError = true,
|
|
348
|
+
className,
|
|
349
|
+
errorClassName,
|
|
350
|
+
required = false,
|
|
351
|
+
validate
|
|
352
|
+
}) {
|
|
353
|
+
const fieldState = useField({ name, validate });
|
|
354
|
+
const { meta } = fieldState;
|
|
355
|
+
const hasError = React3__namespace.useMemo(() => {
|
|
356
|
+
return showError && meta.touched && meta.error ? true : false;
|
|
357
|
+
}, [meta?.touched, meta?.error, showError]);
|
|
358
|
+
const errorId = `${name}-error`;
|
|
359
|
+
const descriptionId = `${name}-description`;
|
|
360
|
+
return /* @__PURE__ */ React3__namespace.createElement(
|
|
361
|
+
chunkDKLLPKZN_cjs.Field,
|
|
362
|
+
{
|
|
363
|
+
className,
|
|
364
|
+
"data-field": name,
|
|
365
|
+
invalid: hasError
|
|
366
|
+
},
|
|
367
|
+
/* @__PURE__ */ React3__namespace.createElement(
|
|
368
|
+
chunkDKLLPKZN_cjs.LabelGroup,
|
|
369
|
+
{
|
|
370
|
+
labelHtmlFor: name,
|
|
371
|
+
required,
|
|
372
|
+
variant: "label",
|
|
373
|
+
secondaryId: descriptionId,
|
|
374
|
+
secondary: description,
|
|
375
|
+
primary: label
|
|
376
|
+
}
|
|
377
|
+
),
|
|
378
|
+
/* @__PURE__ */ React3__namespace.createElement("div", { "data-slot": "field-control" }, typeof children === "function" ? children(fieldState) : children),
|
|
379
|
+
/* @__PURE__ */ React3__namespace.createElement(
|
|
380
|
+
FieldFeedback,
|
|
381
|
+
{
|
|
382
|
+
errorId,
|
|
383
|
+
errorClassName,
|
|
384
|
+
shouldRenderError: hasError,
|
|
385
|
+
error: meta.error
|
|
386
|
+
}
|
|
387
|
+
)
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
Field2.displayName = "Field";
|
|
391
|
+
|
|
392
|
+
exports.Field = Field2;
|
|
393
|
+
exports.FormContext = FormContext;
|
|
394
|
+
exports.useField = useField;
|
|
395
|
+
exports.useForm = useForm;
|
|
396
|
+
//# sourceMappingURL=chunk-EX6CRLKG.cjs.map
|
|
397
|
+
//# sourceMappingURL=chunk-EX6CRLKG.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/useForm.ts","../src/core/FormContext.tsx","../src/core/useField.ts","../src/core/field-feedback.tsx","../src/core/Field.tsx"],"names":["useObservable","useRef","useMap","useCallback","errors","touched","values","useSelector","initialValues","React","useContext","FieldError","Field","React3","LabelGroup"],"mappings":"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","file":"chunk-EX6CRLKG.cjs","sourcesContent":["\"use client\";\n\nimport { useCallback, useRef } from \"react\";\nimport { useObservable, useSelector } from \"@legendapp/state/react\";\n// Tree-shakable imports from @opensite/hooks following ECOSYSTEM_GUIDELINES\nimport { useMap } from \"@opensite/hooks/useMap\";\nimport type {\n FormValues,\n FormErrors,\n TouchedFields,\n UseFormOptions,\n UseFormReturn,\n SubmissionStatus,\n FieldInputProps,\n FieldMeta,\n FormHelpers,\n} from \"./types\";\n\n/**\n * useForm - High-performance form state management with field-level reactivity\n *\n * Built on @legendapp/state for optimal performance:\n * - Field-level reactivity: Only re-render the specific field that changed\n * - Observable-based state: ~1 re-render per change vs ~10 for traditional hooks\n * - Tree-shakable: Only bundle what you use\n *\n * @example\n * ```tsx\n * const form = useForm({\n * initialValues: { email: '', password: '' },\n * onSubmit: async (values) => {\n * await login(values);\n * },\n * validationSchema: {\n * email: (value) => !value ? 'Required' : undefined,\n * password: (value) => value.length < 8 ? 'Too short' : undefined,\n * },\n * });\n *\n * return (\n * <form onSubmit={form.handleSubmit}>\n * <input {...form.getFieldProps('email')} />\n * {form.errors.email && <span>{form.errors.email}</span>}\n * </form>\n * );\n * ```\n *\n * @see https://opensite.ai/developers/page-speed/forms/use-form\n */\nexport function useForm<T extends FormValues = FormValues>(\n options: UseFormOptions<T>\n): UseFormReturn<T> {\n const {\n initialValues,\n validationSchema,\n validateOn = \"onBlur\",\n revalidateOn = \"onChange\",\n onSubmit,\n onError,\n debug = false,\n } = options;\n\n // Create observable form state for field-level reactivity\n // Note: Type assertion needed for @legendapp/state beta compatibility\n // The beta version's TypeScript types don't properly expose nested Observable properties\n // This will be removed once stable v3.0.0 is released with proper type definitions\n const state$ = useObservable({\n values: initialValues,\n errors: {} as FormErrors<T>,\n touched: {} as TouchedFields<T>,\n isSubmitting: false,\n status: \"idle\" as SubmissionStatus,\n initialValues: { ...initialValues }, // Create a copy to prevent reference sharing\n hasValidated: {} as Record<string, boolean>,\n }) as any;\n\n // Track validation in progress to prevent race conditions\n const validationInProgress = useRef<Set<string>>(new Set());\n\n // Enhanced state management with @opensite/hooks\n // useMap: Manage complex nested field metadata immutably\n const [, fieldMetadataActions] = useMap<\n string,\n { lastValidated?: number; validationCount: number }\n >();\n\n /**\n * Validate a single field\n * Enhanced with @opensite/hooks useMap for metadata tracking\n */\n const validateField = useCallback(\n async <K extends keyof T>(field: K): Promise<string | undefined> => {\n const validators = validationSchema?.[field];\n if (!validators) return undefined;\n\n const fieldKey = String(field);\n validationInProgress.current.add(fieldKey);\n\n // Track validation metadata using useMap\n const currentMeta = fieldMetadataActions.get(fieldKey) || {\n validationCount: 0,\n };\n fieldMetadataActions.set(fieldKey, {\n lastValidated: Date.now(),\n validationCount: currentMeta.validationCount + 1,\n });\n\n try {\n const value = state$.values[field].get();\n const allValues = state$.values.get();\n\n const validatorArray = Array.isArray(validators)\n ? validators\n : [validators];\n\n for (const validator of validatorArray) {\n const error = await validator(value, allValues);\n if (error) {\n state$.errors[field].set(error);\n validationInProgress.current.delete(fieldKey);\n return error;\n }\n }\n\n // Clear error if validation passed\n state$.errors[field].set(undefined);\n validationInProgress.current.delete(fieldKey);\n return undefined;\n } catch (error) {\n validationInProgress.current.delete(fieldKey);\n const errorMessage =\n error instanceof Error ? error.message : \"Validation error\";\n state$.errors[field].set(errorMessage);\n return errorMessage;\n }\n },\n [validationSchema, state$, fieldMetadataActions]\n );\n\n /**\n * Validate entire form\n */\n const validateForm = useCallback(async (): Promise<FormErrors<T>> => {\n if (!validationSchema) return {};\n\n const fields = Object.keys(validationSchema) as Array<keyof T>;\n const errors: FormErrors<T> = {};\n\n await Promise.all(\n fields.map(async (field) => {\n const error = await validateField(field);\n if (error) {\n errors[field] = error;\n }\n })\n );\n\n state$.errors.set(errors);\n return errors;\n }, [validationSchema, validateField, state$]);\n\n /**\n * Set field value with optional validation\n */\n const setFieldValue = useCallback(\n <K extends keyof T>(field: K, value: T[K]) => {\n state$.values[field].set(value);\n\n // Revalidate if field has been validated before\n const shouldRevalidate =\n revalidateOn === \"onChange\" &&\n state$.hasValidated[String(field)].get();\n\n if (shouldRevalidate && validationSchema?.[field]) {\n validateField(field);\n }\n\n if (debug) {\n console.log(\"[useForm] setFieldValue:\", { field, value });\n }\n },\n [state$, revalidateOn, validationSchema, validateField, debug]\n );\n\n /**\n * Set field as touched with optional validation\n */\n const setFieldTouched = useCallback(\n <K extends keyof T>(field: K, touched: boolean) => {\n state$.touched[field].set(touched);\n\n // Validate on blur if configured\n if (touched && validateOn === \"onBlur\" && validationSchema?.[field]) {\n state$.hasValidated[String(field)].set(true);\n validateField(field);\n }\n\n if (debug) {\n console.log(\"[useForm] setFieldTouched:\", { field, touched });\n }\n },\n [state$, validateOn, validationSchema, validateField, debug]\n );\n\n /**\n * Reset form to initial values\n * Enhanced with @opensite/hooks useMap to clear field metadata\n */\n const resetForm = useCallback(() => {\n state$.values.set(state$.initialValues.get());\n state$.errors.set({});\n state$.touched.set({});\n state$.isSubmitting.set(false);\n state$.status.set(\"idle\");\n state$.hasValidated.set({});\n\n // Clear field metadata tracked by useMap\n fieldMetadataActions.clear();\n\n if (debug) {\n console.log(\"[useForm] Form reset\");\n }\n }, [state$, fieldMetadataActions, debug]);\n\n /**\n * Handle form submission\n */\n const handleSubmit = useCallback(\n async (e?: React.FormEvent) => {\n e?.preventDefault();\n\n if (debug) {\n console.log(\"[useForm] handleSubmit started\");\n }\n\n state$.isSubmitting.set(true);\n state$.status.set(\"submitting\");\n\n try {\n // Validate form\n const errors = await validateForm();\n const hasErrors = Object.keys(errors).length > 0;\n\n if (hasErrors) {\n state$.status.set(\"error\");\n onError?.(errors);\n\n if (debug) {\n console.log(\"[useForm] Validation errors:\", errors);\n }\n\n return;\n }\n\n // Create form helpers\n const helpers: FormHelpers<T> = {\n setValues: (values) => {\n if (typeof values === \"function\") {\n state$.values.set(values(state$.values.get()));\n } else {\n state$.values.set(values);\n }\n },\n setFieldValue,\n setErrors: (errors) => state$.errors.set(errors),\n setFieldError: (field, error) => state$.errors[field].set(error),\n setTouched: (touched) => state$.touched.set(touched),\n setFieldTouched,\n setSubmitting: (submitting) => state$.isSubmitting.set(submitting),\n resetForm,\n };\n\n // Call submission handler\n await onSubmit(state$.values.get(), helpers);\n\n state$.status.set(\"success\");\n\n if (debug) {\n console.log(\"[useForm] Submit successful\");\n }\n } catch (error) {\n state$.status.set(\"error\");\n\n if (debug) {\n console.error(\"[useForm] Submit error:\", error);\n }\n\n throw error;\n } finally {\n state$.isSubmitting.set(false);\n }\n },\n [\n state$,\n validateForm,\n onSubmit,\n onError,\n setFieldValue,\n setFieldTouched,\n resetForm,\n debug,\n ]\n );\n\n /**\n * Get field props for binding to inputs\n */\n const getFieldProps = useCallback(\n <K extends keyof T>(field: K): FieldInputProps<T[K]> => {\n return {\n name: String(field),\n value: state$.values[field].get(),\n onChange: (value: T[K]) => setFieldValue(field, value),\n onBlur: () => setFieldTouched(field, true),\n };\n },\n [state$, setFieldValue, setFieldTouched]\n );\n\n /**\n * Get field meta information\n * Enhanced with @opensite/hooks useMap for validation metadata\n * and usePrevious for change detection\n */\n const getFieldMeta = useCallback(\n <K extends keyof T>(field: K): FieldMeta => {\n const fieldKey = String(field);\n const metadata = fieldMetadataActions.get(fieldKey);\n\n return {\n error: state$.errors[field].get(),\n touched: state$.touched[field].get() ?? false,\n isDirty:\n state$.values[field].get() !== state$.initialValues[field].get(),\n isValidating: validationInProgress.current.has(fieldKey),\n // Additional metadata from @opensite/hooks\n validationCount: metadata?.validationCount,\n lastValidated: metadata?.lastValidated,\n };\n },\n [state$, fieldMetadataActions]\n );\n\n // Use selectors for reactive properties\n const values = useSelector(() => state$.values.get());\n const errors = useSelector(() => state$.errors.get());\n const touched = useSelector(() => state$.touched.get());\n const isSubmitting = useSelector(() => state$.isSubmitting.get());\n const status = useSelector(() => state$.status.get());\n\n // Use selectors for derived state to ensure reactivity\n const isValid = useSelector(() => Object.keys(state$.errors.get()).length === 0);\n const isDirty = useSelector(() => {\n const currentValues = state$.values.get();\n const initialValues = state$.initialValues.get();\n return Object.keys(currentValues).some(\n (key) => currentValues[key] !== initialValues[key]\n );\n });\n\n return {\n // State\n values,\n errors,\n touched,\n isSubmitting,\n isValid,\n isDirty,\n status,\n\n // Actions\n handleSubmit,\n setValues: (values) => {\n if (typeof values === \"function\") {\n state$.values.set(values(state$.values.get()));\n } else {\n state$.values.set(values);\n }\n },\n setFieldValue,\n setErrors: (errors) => state$.errors.set(errors),\n setFieldError: (field, error) => state$.errors[field].set(error),\n setTouched: (touched) => state$.touched.set(touched),\n setFieldTouched,\n validateForm,\n validateField,\n resetForm,\n getFieldProps,\n getFieldMeta,\n };\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport type { UseFormReturn } from \"./types\";\n\n/**\n * FormContext - React context for providing form state to child components\n *\n * Allows useField hook to access form state without prop drilling.\n * Automatically provided by the <Form> component.\n *\n * @internal\n */\nexport const FormContext = React.createContext<UseFormReturn<any> | null>(null);\n\nFormContext.displayName = \"FormContext\";\n","\"use client\";\n\nimport { useCallback, useContext } from \"react\";\nimport { FormContext } from \"./FormContext\";\nimport type { UseFieldOptions, UseFieldReturn, FieldInputProps, FieldMeta } from \"./types\";\n\n/**\n * useField - Field-level reactive hook for form inputs\n *\n * Provides isolated reactivity for individual form fields.\n * Only re-renders when the specific field changes, not when other fields update.\n *\n * Must be used within a FormContext (inside <Form> component).\n *\n * @example\n * ```tsx\n * function EmailInput() {\n * const { field, meta, helpers } = useField({ name: 'email' });\n *\n * return (\n * <div>\n * <input {...field} type=\"email\" />\n * {meta.touched && meta.error && <span>{meta.error}</span>}\n * </div>\n * );\n * }\n * ```\n *\n * @see https://opensite.ai/developers/page-speed/forms/use-field\n */\nexport function useField<T = any>(\n options: UseFieldOptions<T>\n): UseFieldReturn<T> {\n const { name, validate, transform } = options;\n\n const form = useContext(FormContext);\n\n if (!form) {\n throw new Error(\n \"useField must be used within a FormContext. \" +\n \"Wrap your component with <Form> or use useForm's getFieldProps instead.\"\n );\n }\n\n // Get field props with automatic change/blur handling\n const baseFieldProps = form.getFieldProps(name);\n\n // Apply transform if provided\n const field: FieldInputProps<T> = {\n ...baseFieldProps,\n value: baseFieldProps.value as T,\n onChange: (value: T) => {\n const transformedValue = transform ? transform(value) : value;\n baseFieldProps.onChange(transformedValue);\n\n // Run field-level validation if provided\n if (validate) {\n const result = validate(transformedValue, form.values);\n if (result instanceof Promise) {\n result.then((error) => {\n if (error !== undefined) {\n form.setFieldError(name, error);\n }\n });\n } else if (result !== undefined) {\n form.setFieldError(name, result);\n }\n }\n },\n };\n\n // Get field meta information\n const meta: FieldMeta = form.getFieldMeta(name);\n\n // Field helpers\n const helpers = {\n setValue: useCallback(\n (value: T) => {\n const transformedValue = transform ? transform(value) : value;\n form.setFieldValue(name, transformedValue);\n },\n [name, transform, form]\n ),\n setTouched: useCallback(\n (touched: boolean) => {\n form.setFieldTouched(name, touched);\n },\n [name, form]\n ),\n setError: useCallback(\n (error: string | undefined) => {\n form.setFieldError(name, error);\n },\n [name, form]\n ),\n };\n\n return {\n field,\n meta,\n helpers,\n };\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { FieldMeta } from \"./types\";\nimport { FieldError } from \"../components/ui/field\";\n\ntype Props = {\n errorId?: string;\n errorClassName?: string;\n shouldRenderError?: boolean;\n error?: FieldMeta[\"error\"];\n};\nconst FieldFeedback = ({\n errorId,\n errorClassName,\n error,\n shouldRenderError,\n}: Props) => {\n const errorText = Array.isArray(error) ? error.join(\", \") : error;\n if (!errorText || !shouldRenderError) return null;\n\n return (\n <FieldError id={errorId} className={errorClassName}>\n {errorText}\n </FieldError>\n );\n};\n\nexport { FieldFeedback };\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useField } from \"./useField\";\nimport type { FieldProps } from \"./types\";\nimport { FieldFeedback } from \"./field-feedback\";\nimport { LabelGroup } from \"./label-group\";\nimport { Field as FieldWrapper } from \"../components/ui/field\";\n\n/**\n * Field - Field wrapper component with label, description, and error display\n *\n * Provides a complete field UI with automatic error handling and accessibility.\n * Uses useField hook internally for field-level reactivity.\n *\n * Features:\n * - Automatic label association\n * - Error display with accessibility\n * - Optional description text\n * - Render prop pattern for flexibility\n * - Full accessibility support\n *\n * @example\n * ```tsx\n * <Field name=\"email\" label=\"Email Address\" description=\"We'll never share your email\">\n * {({ field, meta }) => (\n * <input\n * {...field}\n * type=\"email\"\n * className={meta.error && meta.touched ? 'error' : ''}\n * />\n * )}\n * </Field>\n * ```\n *\n * @see https://opensite.ai/developers/page-speed/forms/field\n */\nexport function Field({\n name,\n label,\n description,\n children,\n showError = true,\n className,\n errorClassName,\n required = false,\n validate,\n}: FieldProps) {\n const fieldState = useField({ name, validate });\n const { meta } = fieldState;\n\n const hasError = React.useMemo(() => {\n return showError && meta.touched && meta.error ? true : false;\n }, [meta?.touched, meta?.error, showError]);\n\n const errorId = `${name}-error`;\n const descriptionId = `${name}-description`;\n\n return (\n <FieldWrapper\n className={className}\n data-field={name}\n invalid={hasError}\n >\n <LabelGroup\n labelHtmlFor={name}\n required={required}\n variant=\"label\"\n secondaryId={descriptionId}\n secondary={description}\n primary={label}\n />\n\n {/* Field control slot keeps legacy DOM shape for compatibility */}\n <div data-slot=\"field-control\">\n {typeof children === \"function\" ? children(fieldState) : children}\n </div>\n\n {/* Error message */}\n <FieldFeedback\n errorId={errorId}\n errorClassName={errorClassName}\n shouldRenderError={hasError}\n error={meta.error}\n />\n </FieldWrapper>\n );\n}\n\nField.displayName = \"Field\";\n"]}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// src/validation/utils.ts
|
|
2
|
+
function debounce(validator, options = {}) {
|
|
3
|
+
const { delay = 300, leading = false, trailing = true } = options;
|
|
4
|
+
let timeoutId = null;
|
|
5
|
+
let lastCallTime = 0;
|
|
6
|
+
let lastResult;
|
|
7
|
+
return async (value, allValues) => {
|
|
8
|
+
const now = Date.now();
|
|
9
|
+
const timeSinceLastCall = now - lastCallTime;
|
|
10
|
+
if (timeoutId) {
|
|
11
|
+
clearTimeout(timeoutId);
|
|
12
|
+
timeoutId = null;
|
|
13
|
+
}
|
|
14
|
+
if (leading && timeSinceLastCall >= delay) {
|
|
15
|
+
lastCallTime = now;
|
|
16
|
+
lastResult = await validator(value, allValues);
|
|
17
|
+
return lastResult;
|
|
18
|
+
}
|
|
19
|
+
if (trailing) {
|
|
20
|
+
return new Promise((resolve) => {
|
|
21
|
+
timeoutId = setTimeout(async () => {
|
|
22
|
+
lastCallTime = Date.now();
|
|
23
|
+
lastResult = await validator(value, allValues);
|
|
24
|
+
resolve(lastResult);
|
|
25
|
+
}, delay);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
return lastResult;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
var defaultMessages = {
|
|
32
|
+
required: "This field is required",
|
|
33
|
+
email: "Please enter a valid email address",
|
|
34
|
+
url: "Please enter a valid URL",
|
|
35
|
+
phone: "Please enter a valid phone number",
|
|
36
|
+
minLength: ({ min }) => `Must be at least ${min} characters`,
|
|
37
|
+
maxLength: ({ max }) => `Must be no more than ${max} characters`,
|
|
38
|
+
min: ({ min }) => `Must be at least ${min}`,
|
|
39
|
+
max: ({ max }) => `Must be no more than ${max}`,
|
|
40
|
+
pattern: "Invalid format",
|
|
41
|
+
matches: ({ field }) => `Must match ${field}`,
|
|
42
|
+
oneOf: "Invalid value",
|
|
43
|
+
creditCard: "Please enter a valid credit card number",
|
|
44
|
+
postalCode: "Please enter a valid ZIP code",
|
|
45
|
+
alpha: "Must contain only letters",
|
|
46
|
+
alphanumeric: "Must contain only letters and numbers",
|
|
47
|
+
numeric: "Must be a valid number",
|
|
48
|
+
integer: "Must be a whole number"
|
|
49
|
+
};
|
|
50
|
+
var MessageRegistry = class {
|
|
51
|
+
constructor() {
|
|
52
|
+
this.messages = { ...defaultMessages };
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Set custom messages (for i18n or customization)
|
|
56
|
+
*/
|
|
57
|
+
setMessages(messages) {
|
|
58
|
+
this.messages = { ...this.messages, ...messages };
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Get message by key
|
|
62
|
+
*/
|
|
63
|
+
getMessage(key, params) {
|
|
64
|
+
const message = this.messages[key];
|
|
65
|
+
if (!message) {
|
|
66
|
+
return `Validation error: ${key}`;
|
|
67
|
+
}
|
|
68
|
+
if (typeof message === "function") {
|
|
69
|
+
return message(params || {});
|
|
70
|
+
}
|
|
71
|
+
return message;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Reset to default messages
|
|
75
|
+
*/
|
|
76
|
+
reset() {
|
|
77
|
+
this.messages = { ...defaultMessages };
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
var messageRegistry = new MessageRegistry();
|
|
81
|
+
function setErrorMessages(messages) {
|
|
82
|
+
messageRegistry.setMessages(messages);
|
|
83
|
+
}
|
|
84
|
+
function getErrorMessage(key, params) {
|
|
85
|
+
return messageRegistry.getMessage(key, params);
|
|
86
|
+
}
|
|
87
|
+
function resetErrorMessages() {
|
|
88
|
+
messageRegistry.reset();
|
|
89
|
+
}
|
|
90
|
+
function crossFieldValidator(fields, validate) {
|
|
91
|
+
return (_value, allValues) => {
|
|
92
|
+
const fieldValues = fields.reduce(
|
|
93
|
+
(acc, field) => {
|
|
94
|
+
acc[field] = allValues[field];
|
|
95
|
+
return acc;
|
|
96
|
+
},
|
|
97
|
+
{}
|
|
98
|
+
);
|
|
99
|
+
return validate(fieldValues);
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function when(condition, validator) {
|
|
103
|
+
return (value, allValues) => {
|
|
104
|
+
if (condition(allValues)) {
|
|
105
|
+
return validator(value, allValues);
|
|
106
|
+
}
|
|
107
|
+
return void 0;
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function withRaceConditionPrevention(validator) {
|
|
111
|
+
let latestCallId = 0;
|
|
112
|
+
return async (value, allValues) => {
|
|
113
|
+
const callId = ++latestCallId;
|
|
114
|
+
const result = await validator(value, allValues);
|
|
115
|
+
if (callId === latestCallId) {
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
return void 0;
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function asyncValidator(validator, options = {}) {
|
|
122
|
+
return debounce(withRaceConditionPrevention(validator), options);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export { asyncValidator, crossFieldValidator, debounce, defaultMessages, getErrorMessage, messageRegistry, resetErrorMessages, setErrorMessages, when, withRaceConditionPrevention };
|
|
126
|
+
//# sourceMappingURL=chunk-H6NNFV64.js.map
|
|
127
|
+
//# sourceMappingURL=chunk-H6NNFV64.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/validation/utils.ts"],"names":[],"mappings":"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","file":"chunk-H6NNFV64.js","sourcesContent":["/**\n * @page-speed/forms - Validation Utilities\n *\n * Utilities for advanced validation scenarios\n */\n\nimport type { FieldValidator, FormValues } from \"../core/types\";\n\n/**\n * Debounce options for async validators\n */\nexport interface DebounceOptions {\n /**\n * Debounce delay in milliseconds\n * @default 300\n */\n delay?: number;\n\n /**\n * Leading edge - call immediately on first invocation\n * @default false\n */\n leading?: boolean;\n\n /**\n * Trailing edge - call after delay\n * @default true\n */\n trailing?: boolean;\n}\n\n/**\n * Debounce an async validator\n * Prevents rapid validation calls (e.g., for username availability checks)\n *\n * @example\n * ```tsx\n * const checkUsername = debounce(\n * async (value) => {\n * const available = await api.checkUsername(value);\n * return available ? undefined : 'Username is taken';\n * },\n * { delay: 500 }\n * );\n * ```\n */\nexport function debounce<T = any>(\n validator: FieldValidator<T>,\n options: DebounceOptions = {}\n): FieldValidator<T> {\n const { delay = 300, leading = false, trailing = true } = options;\n\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n let lastCallTime = 0;\n let lastResult: string | undefined;\n\n return async (value: T, allValues: FormValues) => {\n const now = Date.now();\n const timeSinceLastCall = now - lastCallTime;\n\n // Clear existing timeout\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n\n // Leading edge call\n if (leading && timeSinceLastCall >= delay) {\n lastCallTime = now;\n lastResult = await validator(value, allValues);\n return lastResult;\n }\n\n // Trailing edge call\n if (trailing) {\n return new Promise((resolve) => {\n timeoutId = setTimeout(async () => {\n lastCallTime = Date.now();\n lastResult = await validator(value, allValues);\n resolve(lastResult);\n }, delay);\n });\n }\n\n return lastResult;\n };\n}\n\n/**\n * Error message template function\n */\nexport type MessageTemplate = (params?: any) => string;\n\n/**\n * Error message templates for internationalization\n */\nexport interface ErrorMessages {\n [key: string]: string | MessageTemplate;\n}\n\n/**\n * Default error messages (English)\n */\nexport const defaultMessages: ErrorMessages = {\n required: \"This field is required\",\n email: \"Please enter a valid email address\",\n url: \"Please enter a valid URL\",\n phone: \"Please enter a valid phone number\",\n minLength: ({ min }: { min: number }) =>\n `Must be at least ${min} characters`,\n maxLength: ({ max }: { max: number }) =>\n `Must be no more than ${max} characters`,\n min: ({ min }: { min: number }) => `Must be at least ${min}`,\n max: ({ max }: { max: number }) => `Must be no more than ${max}`,\n pattern: \"Invalid format\",\n matches: ({ field }: { field: string }) => `Must match ${field}`,\n oneOf: \"Invalid value\",\n creditCard: \"Please enter a valid credit card number\",\n postalCode: \"Please enter a valid ZIP code\",\n alpha: \"Must contain only letters\",\n alphanumeric: \"Must contain only letters and numbers\",\n numeric: \"Must be a valid number\",\n integer: \"Must be a whole number\",\n};\n\n/**\n * Error message registry for i18n support\n */\nclass MessageRegistry {\n private messages: ErrorMessages = { ...defaultMessages };\n\n /**\n * Set custom messages (for i18n or customization)\n */\n setMessages(messages: ErrorMessages): void {\n this.messages = { ...this.messages, ...messages };\n }\n\n /**\n * Get message by key\n */\n getMessage(key: string, params?: Record<string, any>): string {\n const message = this.messages[key];\n\n if (!message) {\n return `Validation error: ${key}`;\n }\n\n if (typeof message === \"function\") {\n return message(params || {});\n }\n\n return message;\n }\n\n /**\n * Reset to default messages\n */\n reset(): void {\n this.messages = { ...defaultMessages };\n }\n}\n\n/**\n * Global message registry instance\n */\nexport const messageRegistry = new MessageRegistry();\n\n/**\n * Set custom error messages globally\n * Useful for internationalization\n *\n * @example\n * ```tsx\n * setErrorMessages({\n * required: 'Este campo es obligatorio',\n * email: 'Por favor ingrese un email válido',\n * });\n * ```\n */\nexport function setErrorMessages(messages: ErrorMessages): void {\n messageRegistry.setMessages(messages);\n}\n\n/**\n * Get error message by key\n */\nexport function getErrorMessage(\n key: string,\n params?: Record<string, any>\n): string {\n return messageRegistry.getMessage(key, params);\n}\n\n/**\n * Reset error messages to defaults\n */\nexport function resetErrorMessages(): void {\n messageRegistry.reset();\n}\n\n/**\n * Cross-field validator helper\n * Creates a validator that depends on multiple fields\n *\n * @example\n * ```tsx\n * const passwordMatch = crossFieldValidator(\n * ['password', 'confirmPassword'],\n * (values) => {\n * if (values.password !== values.confirmPassword) {\n * return 'Passwords must match';\n * }\n * return undefined;\n * }\n * );\n * ```\n */\nexport function crossFieldValidator<T extends FormValues = FormValues>(\n fields: (keyof T)[],\n validate: (values: Pick<T, keyof T>) => string | undefined | Promise<string | undefined>\n): FieldValidator {\n return (_value, allValues) => {\n const fieldValues = fields.reduce(\n (acc, field) => {\n acc[field] = allValues[field as string];\n return acc;\n },\n {} as Record<keyof T, any>\n );\n\n return validate(fieldValues as Pick<T, keyof T>);\n };\n}\n\n/**\n * Conditional validator\n * Only validates when condition is met\n *\n * @example\n * ```tsx\n * const conditionalRequired = when(\n * (values) => values.country === 'US',\n * required()\n * );\n * ```\n */\nexport function when<T = any>(\n condition: (allValues: FormValues) => boolean,\n validator: FieldValidator<T>\n): FieldValidator<T> {\n return (value, allValues) => {\n if (condition(allValues)) {\n return validator(value, allValues);\n }\n return undefined;\n };\n}\n\n/**\n * Async validator with race condition prevention\n * Ensures only the latest validation call resolves\n *\n * @example\n * ```tsx\n * const checkUsername = withRaceConditionPrevention(\n * async (value) => {\n * const available = await api.checkUsername(value);\n * return available ? undefined : 'Username is taken';\n * }\n * );\n * ```\n */\nexport function withRaceConditionPrevention<T = any>(\n validator: FieldValidator<T>\n): FieldValidator<T> {\n let latestCallId = 0;\n\n return async (value: T, allValues: FormValues) => {\n const callId = ++latestCallId;\n\n const result = await validator(value, allValues);\n\n // Only return result if this is still the latest call\n if (callId === latestCallId) {\n return result;\n }\n\n // Ignore stale results\n return undefined;\n };\n}\n\n/**\n * Combine debounce with race condition prevention\n * Best practice for async validators\n *\n * @example\n * ```tsx\n * const checkUsername = asyncValidator(\n * async (value) => {\n * const available = await api.checkUsername(value);\n * return available ? undefined : 'Username is taken';\n * },\n * { delay: 500 }\n * );\n * ```\n */\nexport function asyncValidator<T = any>(\n validator: FieldValidator<T>,\n options: DebounceOptions = {}\n): FieldValidator<T> {\n return debounce(withRaceConditionPrevention(validator), options);\n}\n"]}
|