@juantroconisf/lib 6.0.0 → 6.1.0

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/index.d.mts CHANGED
@@ -43,38 +43,19 @@ type ValidatorErrorMessage = {
43
43
  * @template O The state type.
44
44
  * @template K The key of the array in the state.
45
45
  */
46
- type ArrayRules<O extends StateType, K extends keyof O = keyof O> = {
47
- [F in FieldPaths<ArrayElement<O[K]>>]?: ValidatorParams | ((item: ArrayElement<O[K]>, index: number) => ValidatorParams);
48
- };
49
- /**
50
- * Custom error messages for individual fields in an array item.
51
- * @template O The state type.
52
- * @template K The key of the array in the state.
53
- */
54
- type ArrayMessages<O extends StateType, K extends keyof O = keyof O> = {
55
- [F in FieldPaths<ArrayElement<O[K]>>]?: ValidatorErrorMessage;
56
- };
46
+ type ValidatorRule<item = any> = ValidatorParams | ((item: item, ...args: any[]) => ValidatorParams);
47
+ type RulePath<T> = T extends object ? {
48
+ [K in keyof T & string]: NonNullable<T[K]> extends (infer E)[] ? K | (E extends object ? `${K}.${RulePath<E>}` : never) : NonNullable<T[K]> extends object ? NonNullable<T[K]> extends Date ? K : K | `${K}.${RulePath<NonNullable<T[K]>>}` : K;
49
+ }[keyof T & string] : never;
57
50
  /**
58
51
  * Options for the useForm hook.
59
52
  * @template O The state type.
60
53
  */
61
54
  interface FormOptions<O extends StateType> {
62
55
  /** Validation rules for top-level fields. */
63
- rules?: {
64
- [key in keyof O]?: ValidatorParams;
65
- };
56
+ rules?: Partial<Record<RulePath<O>, ValidatorRule>>;
66
57
  /** Custom error messages for top-level fields. */
67
- messages?: {
68
- [key in keyof O]?: ValidatorErrorMessage;
69
- };
70
- /** Validation rules for items within arrays. */
71
- arrayRules?: {
72
- [K in ArrayKeys<O>]?: ArrayRules<O, K>;
73
- };
74
- /** Custom error messages for items within arrays. */
75
- arrayMessages?: {
76
- [K in ArrayKeys<O>]?: ArrayMessages<O, K>;
77
- };
58
+ messages?: Partial<Record<RulePath<O>, ValidatorErrorMessage>>;
78
59
  /**
79
60
  * Custom property names used as unique identifiers for items in specific arrays.
80
61
  * Default is "id".
@@ -196,6 +177,6 @@ interface UseFormResponse<O extends StateType> {
196
177
  resetTouched: (preservedKeys?: (keyof O)[]) => void;
197
178
  }
198
179
 
199
- declare function useForm<O extends StateType>(initialState: O, { rules, messages, arrayRules, arrayMessages, arrayIdentifiers, }?: FormOptions<O>): UseFormResponse<O>;
180
+ declare function useForm<O extends StateType>(initialState: O, { rules, messages, arrayIdentifiers }?: FormOptions<O>): UseFormResponse<O>;
200
181
 
201
- export { type ArrayMessages, type ArrayRules, type BlurFunc, type ErrorsType, type FormOptions, type HelpersFunc, type NestedChangeProps, NextUIError, type OnMethods, type StateType, type TouchedType, type UXProps, type UseFormResponse, type Validator, type ValidatorErrorMessage, type ValidatorParams, type ValidatorTypes, type ValueChangeFunc, useForm };
182
+ export { type BlurFunc, type ErrorsType, type FormOptions, type HelpersFunc, type NestedChangeProps, NextUIError, type OnMethods, type StateType, type TouchedType, type UXProps, type UseFormResponse, type Validator, type ValidatorErrorMessage, type ValidatorParams, type ValidatorRule, type ValidatorTypes, type ValueChangeFunc, useForm };
package/dist/index.d.ts CHANGED
@@ -43,38 +43,19 @@ type ValidatorErrorMessage = {
43
43
  * @template O The state type.
44
44
  * @template K The key of the array in the state.
45
45
  */
46
- type ArrayRules<O extends StateType, K extends keyof O = keyof O> = {
47
- [F in FieldPaths<ArrayElement<O[K]>>]?: ValidatorParams | ((item: ArrayElement<O[K]>, index: number) => ValidatorParams);
48
- };
49
- /**
50
- * Custom error messages for individual fields in an array item.
51
- * @template O The state type.
52
- * @template K The key of the array in the state.
53
- */
54
- type ArrayMessages<O extends StateType, K extends keyof O = keyof O> = {
55
- [F in FieldPaths<ArrayElement<O[K]>>]?: ValidatorErrorMessage;
56
- };
46
+ type ValidatorRule<item = any> = ValidatorParams | ((item: item, ...args: any[]) => ValidatorParams);
47
+ type RulePath<T> = T extends object ? {
48
+ [K in keyof T & string]: NonNullable<T[K]> extends (infer E)[] ? K | (E extends object ? `${K}.${RulePath<E>}` : never) : NonNullable<T[K]> extends object ? NonNullable<T[K]> extends Date ? K : K | `${K}.${RulePath<NonNullable<T[K]>>}` : K;
49
+ }[keyof T & string] : never;
57
50
  /**
58
51
  * Options for the useForm hook.
59
52
  * @template O The state type.
60
53
  */
61
54
  interface FormOptions<O extends StateType> {
62
55
  /** Validation rules for top-level fields. */
63
- rules?: {
64
- [key in keyof O]?: ValidatorParams;
65
- };
56
+ rules?: Partial<Record<RulePath<O>, ValidatorRule>>;
66
57
  /** Custom error messages for top-level fields. */
67
- messages?: {
68
- [key in keyof O]?: ValidatorErrorMessage;
69
- };
70
- /** Validation rules for items within arrays. */
71
- arrayRules?: {
72
- [K in ArrayKeys<O>]?: ArrayRules<O, K>;
73
- };
74
- /** Custom error messages for items within arrays. */
75
- arrayMessages?: {
76
- [K in ArrayKeys<O>]?: ArrayMessages<O, K>;
77
- };
58
+ messages?: Partial<Record<RulePath<O>, ValidatorErrorMessage>>;
78
59
  /**
79
60
  * Custom property names used as unique identifiers for items in specific arrays.
80
61
  * Default is "id".
@@ -196,6 +177,6 @@ interface UseFormResponse<O extends StateType> {
196
177
  resetTouched: (preservedKeys?: (keyof O)[]) => void;
197
178
  }
198
179
 
199
- declare function useForm<O extends StateType>(initialState: O, { rules, messages, arrayRules, arrayMessages, arrayIdentifiers, }?: FormOptions<O>): UseFormResponse<O>;
180
+ declare function useForm<O extends StateType>(initialState: O, { rules, messages, arrayIdentifiers }?: FormOptions<O>): UseFormResponse<O>;
200
181
 
201
- export { type ArrayMessages, type ArrayRules, type BlurFunc, type ErrorsType, type FormOptions, type HelpersFunc, type NestedChangeProps, NextUIError, type OnMethods, type StateType, type TouchedType, type UXProps, type UseFormResponse, type Validator, type ValidatorErrorMessage, type ValidatorParams, type ValidatorTypes, type ValueChangeFunc, useForm };
182
+ export { type BlurFunc, type ErrorsType, type FormOptions, type HelpersFunc, type NestedChangeProps, NextUIError, type OnMethods, type StateType, type TouchedType, type UXProps, type UseFormResponse, type Validator, type ValidatorErrorMessage, type ValidatorParams, type ValidatorRule, type ValidatorTypes, type ValueChangeFunc, useForm };
package/dist/index.js CHANGED
@@ -219,13 +219,7 @@ function useValidate() {
219
219
  }
220
220
 
221
221
  // src/hooks/useForm.tsx
222
- function useForm(initialState, {
223
- rules,
224
- messages,
225
- arrayRules,
226
- arrayMessages,
227
- arrayIdentifiers
228
- } = {}) {
222
+ function useForm(initialState, { rules, messages, arrayIdentifiers } = {}) {
229
223
  const [state, setState] = (0, import_react.useState)(initialState), [touched, setTouched] = (0, import_react.useState)(/* @__PURE__ */ new Map()), [errors2, setErrors] = (0, import_react.useState)(/* @__PURE__ */ new Map()), { performValidations } = useValidate();
230
224
  const indexMap = (0, import_react.useMemo)(() => {
231
225
  const map2 = /* @__PURE__ */ new Map();
@@ -252,12 +246,21 @@ function useForm(initialState, {
252
246
  },
253
247
  [indexMap]
254
248
  );
249
+ const getRule = (path) => {
250
+ if (rules?.[path]) return rules[path];
251
+ const genericPath = path.replace(/\.\d+\./g, ".").replace(/\.\d+$/, "");
252
+ return rules?.[genericPath];
253
+ };
254
+ const getMessage = (path) => {
255
+ if (messages?.[path]) return messages[path];
256
+ const genericPath = path.replace(/\.\d+\./g, ".").replace(/\.\d+$/, "");
257
+ return messages?.[genericPath];
258
+ };
255
259
  const validateInput = (compositeKey, value, ruleKey) => {
256
- const error = performValidations(
257
- value,
258
- rules?.[ruleKey],
259
- messages?.[ruleKey]
260
- );
260
+ const ruleDef = getRule(ruleKey);
261
+ const rule = typeof ruleDef === "function" ? ruleDef(value, state) : ruleDef;
262
+ const message = getMessage(ruleKey);
263
+ const error = performValidations(value, rule, message);
261
264
  setErrors((prev) => new Map(prev).set(compositeKey, error));
262
265
  };
263
266
  const getUXProps = (0, import_react.useCallback)(
@@ -272,14 +275,14 @@ function useForm(initialState, {
272
275
  [errors2, touched]
273
276
  );
274
277
  const onBlur = (id) => {
275
- validateInput(String(id), state[id], id);
278
+ validateInput(String(id), state[id], String(id));
276
279
  if (touched.get(String(id))) return;
277
280
  setTouched((prev) => new Map(prev).set(String(id), true));
278
281
  }, onValueChange = ((...args) => {
279
282
  if (args.length === 2) {
280
283
  const [id, value] = args;
281
284
  setState((prev) => handleNestedChange({ state: prev, id, value }));
282
- validateInput(String(id), value, id);
285
+ validateInput(String(id), value, String(id));
283
286
  return;
284
287
  }
285
288
  if (args.length === 3) {
@@ -325,7 +328,7 @@ function useForm(initialState, {
325
328
  value: fixedValue
326
329
  })
327
330
  );
328
- validateInput(String(id), fixedValue, id);
331
+ validateInput(String(id), fixedValue, String(id));
329
332
  };
330
333
  const getItemCompositeKey = (arrayKey, itemId, field) => `${arrayKey}.${itemId}.${field}`;
331
334
  const getPrimitiveCompositeKey = (arrayKey, index) => `${arrayKey}.@${index}`;
@@ -333,16 +336,20 @@ function useForm(initialState, {
333
336
  const validateItemInput = (arrayKey, itemId, field, value) => {
334
337
  const index = getIndex(String(arrayKey), itemId);
335
338
  if (index === void 0) return;
336
- const arrayRuleDef = arrayRules?.[arrayKey]?.[field];
337
339
  const item = state[arrayKey][index];
338
- const fieldRules = typeof arrayRuleDef === "function" ? arrayRuleDef(item, index) : arrayRuleDef;
339
- const fieldMessages = arrayMessages?.[arrayKey]?.[field];
340
340
  const compositeKey = getItemCompositeKey(String(arrayKey), itemId, field);
341
+ let genericField = field.replace(/\.@\d+/, "");
342
+ if (genericField === "")
343
+ genericField = String(arrayKey);
344
+ else genericField = `${String(arrayKey)}.${genericField}`;
345
+ let effectivePath = `${String(arrayKey)}.${field}`;
346
+ if (field === "") effectivePath = String(arrayKey);
347
+ effectivePath = effectivePath.replace(/\.@\d+/g, "").replace(/\.@\d+$/, "");
348
+ const ruleDef = getRule(effectivePath);
349
+ const rule = typeof ruleDef === "function" ? ruleDef(value, state, item, index) : ruleDef;
350
+ const message = getMessage(effectivePath);
341
351
  setErrors(
342
- (prev) => new Map(prev).set(
343
- compositeKey,
344
- performValidations(value, fieldRules, fieldMessages)
345
- )
352
+ (prev) => new Map(prev).set(compositeKey, performValidations(value, rule, message))
346
353
  );
347
354
  };
348
355
  const getItemUXProps = (0, import_react.useCallback)(
@@ -394,12 +401,11 @@ function useForm(initialState, {
394
401
  validateItemInput(arrayKey, itemId, field, fixedValue);
395
402
  };
396
403
  const validateNestedInput = (dotPath, value) => {
397
- const topKey = dotPath.split(".")[0];
404
+ const ruleDef = getRule(dotPath);
405
+ const rule = typeof ruleDef === "function" ? ruleDef(value, state) : ruleDef;
406
+ const message = getMessage(dotPath);
398
407
  setErrors(
399
- (prev) => new Map(prev).set(
400
- dotPath,
401
- performValidations(value, rules?.[topKey], messages?.[topKey])
402
- )
408
+ (prev) => new Map(prev).set(dotPath, performValidations(value, rule, message))
403
409
  );
404
410
  };
405
411
  const onNestedBlur = (dotPath) => {
package/dist/index.mjs CHANGED
@@ -200,13 +200,7 @@ function useValidate() {
200
200
  }
201
201
 
202
202
  // src/hooks/useForm.tsx
203
- function useForm(initialState, {
204
- rules,
205
- messages,
206
- arrayRules,
207
- arrayMessages,
208
- arrayIdentifiers
209
- } = {}) {
203
+ function useForm(initialState, { rules, messages, arrayIdentifiers } = {}) {
210
204
  const [state, setState] = useState(initialState), [touched, setTouched] = useState(/* @__PURE__ */ new Map()), [errors2, setErrors] = useState(/* @__PURE__ */ new Map()), { performValidations } = useValidate();
211
205
  const indexMap = useMemo(() => {
212
206
  const map2 = /* @__PURE__ */ new Map();
@@ -233,12 +227,21 @@ function useForm(initialState, {
233
227
  },
234
228
  [indexMap]
235
229
  );
230
+ const getRule = (path) => {
231
+ if (rules?.[path]) return rules[path];
232
+ const genericPath = path.replace(/\.\d+\./g, ".").replace(/\.\d+$/, "");
233
+ return rules?.[genericPath];
234
+ };
235
+ const getMessage = (path) => {
236
+ if (messages?.[path]) return messages[path];
237
+ const genericPath = path.replace(/\.\d+\./g, ".").replace(/\.\d+$/, "");
238
+ return messages?.[genericPath];
239
+ };
236
240
  const validateInput = (compositeKey, value, ruleKey) => {
237
- const error = performValidations(
238
- value,
239
- rules?.[ruleKey],
240
- messages?.[ruleKey]
241
- );
241
+ const ruleDef = getRule(ruleKey);
242
+ const rule = typeof ruleDef === "function" ? ruleDef(value, state) : ruleDef;
243
+ const message = getMessage(ruleKey);
244
+ const error = performValidations(value, rule, message);
242
245
  setErrors((prev) => new Map(prev).set(compositeKey, error));
243
246
  };
244
247
  const getUXProps = useCallback(
@@ -253,14 +256,14 @@ function useForm(initialState, {
253
256
  [errors2, touched]
254
257
  );
255
258
  const onBlur = (id) => {
256
- validateInput(String(id), state[id], id);
259
+ validateInput(String(id), state[id], String(id));
257
260
  if (touched.get(String(id))) return;
258
261
  setTouched((prev) => new Map(prev).set(String(id), true));
259
262
  }, onValueChange = ((...args) => {
260
263
  if (args.length === 2) {
261
264
  const [id, value] = args;
262
265
  setState((prev) => handleNestedChange({ state: prev, id, value }));
263
- validateInput(String(id), value, id);
266
+ validateInput(String(id), value, String(id));
264
267
  return;
265
268
  }
266
269
  if (args.length === 3) {
@@ -306,7 +309,7 @@ function useForm(initialState, {
306
309
  value: fixedValue
307
310
  })
308
311
  );
309
- validateInput(String(id), fixedValue, id);
312
+ validateInput(String(id), fixedValue, String(id));
310
313
  };
311
314
  const getItemCompositeKey = (arrayKey, itemId, field) => `${arrayKey}.${itemId}.${field}`;
312
315
  const getPrimitiveCompositeKey = (arrayKey, index) => `${arrayKey}.@${index}`;
@@ -314,16 +317,20 @@ function useForm(initialState, {
314
317
  const validateItemInput = (arrayKey, itemId, field, value) => {
315
318
  const index = getIndex(String(arrayKey), itemId);
316
319
  if (index === void 0) return;
317
- const arrayRuleDef = arrayRules?.[arrayKey]?.[field];
318
320
  const item = state[arrayKey][index];
319
- const fieldRules = typeof arrayRuleDef === "function" ? arrayRuleDef(item, index) : arrayRuleDef;
320
- const fieldMessages = arrayMessages?.[arrayKey]?.[field];
321
321
  const compositeKey = getItemCompositeKey(String(arrayKey), itemId, field);
322
+ let genericField = field.replace(/\.@\d+/, "");
323
+ if (genericField === "")
324
+ genericField = String(arrayKey);
325
+ else genericField = `${String(arrayKey)}.${genericField}`;
326
+ let effectivePath = `${String(arrayKey)}.${field}`;
327
+ if (field === "") effectivePath = String(arrayKey);
328
+ effectivePath = effectivePath.replace(/\.@\d+/g, "").replace(/\.@\d+$/, "");
329
+ const ruleDef = getRule(effectivePath);
330
+ const rule = typeof ruleDef === "function" ? ruleDef(value, state, item, index) : ruleDef;
331
+ const message = getMessage(effectivePath);
322
332
  setErrors(
323
- (prev) => new Map(prev).set(
324
- compositeKey,
325
- performValidations(value, fieldRules, fieldMessages)
326
- )
333
+ (prev) => new Map(prev).set(compositeKey, performValidations(value, rule, message))
327
334
  );
328
335
  };
329
336
  const getItemUXProps = useCallback(
@@ -375,12 +382,11 @@ function useForm(initialState, {
375
382
  validateItemInput(arrayKey, itemId, field, fixedValue);
376
383
  };
377
384
  const validateNestedInput = (dotPath, value) => {
378
- const topKey = dotPath.split(".")[0];
385
+ const ruleDef = getRule(dotPath);
386
+ const rule = typeof ruleDef === "function" ? ruleDef(value, state) : ruleDef;
387
+ const message = getMessage(dotPath);
379
388
  setErrors(
380
- (prev) => new Map(prev).set(
381
- dotPath,
382
- performValidations(value, rules?.[topKey], messages?.[topKey])
383
- )
389
+ (prev) => new Map(prev).set(dotPath, performValidations(value, rule, message))
384
390
  );
385
391
  };
386
392
  const onNestedBlur = (dotPath) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juantroconisf/lib",
3
- "version": "6.0.0",
3
+ "version": "6.1.0",
4
4
  "description": "A form validation library for HeroUI.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",