@juantroconisf/lib 6.1.0 → 7.0.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.js CHANGED
@@ -218,530 +218,387 @@ function useValidate() {
218
218
  return { performValidations };
219
219
  }
220
220
 
221
+ // src/hooks/useForm.utils.ts
222
+ function resolveFieldData(args, state, getIndex, getNestedValue2) {
223
+ const argCount = args.length;
224
+ if (argCount === 1) {
225
+ const id = args[0];
226
+ return {
227
+ type: "scalar" /* Scalar */,
228
+ compositeKey: id,
229
+ fieldPath: id,
230
+ value: getNestedValue2(state, id),
231
+ id
232
+ };
233
+ }
234
+ if (argCount === 2) {
235
+ const [arg0, arg1] = args;
236
+ if (typeof arg0 === "string" && arg0.includes(".")) {
237
+ const parts = arg0.split(".");
238
+ const arrayKey2 = parts[0];
239
+ if (Array.isArray(state[arrayKey2])) {
240
+ const field = parts.slice(1).join(".");
241
+ const itemId = arg1;
242
+ const index2 = getIndex(arrayKey2, itemId);
243
+ if (index2 === void 0) return null;
244
+ const arr2 = state[arrayKey2];
245
+ const item = arr2[index2];
246
+ const value = getNestedValue2(item, field);
247
+ return {
248
+ type: "objectArray" /* ObjectArray */,
249
+ compositeKey: `${arrayKey2}.${itemId}.${field}`,
250
+ fieldPath: arg0,
251
+ // "array.field"
252
+ value,
253
+ arrayKey: arrayKey2,
254
+ itemId,
255
+ field,
256
+ index: index2
257
+ };
258
+ }
259
+ }
260
+ const arrayKey = arg0;
261
+ const index = arg1;
262
+ const arr = state[arrayKey];
263
+ return {
264
+ type: "primitiveArray" /* PrimitiveArray */,
265
+ compositeKey: `${arrayKey}.@${index}`,
266
+ fieldPath: `${arrayKey}`,
267
+ value: arr?.[index],
268
+ arrayKey,
269
+ index
270
+ };
271
+ }
272
+ if (argCount === 3) {
273
+ const [arrayKey, itemId, field] = args;
274
+ const index = getIndex(arrayKey, itemId);
275
+ if (index === void 0) return null;
276
+ const arr = state[arrayKey];
277
+ const item = arr[index];
278
+ const value = getNestedValue2(item, field);
279
+ return {
280
+ type: "objectArray" /* ObjectArray */,
281
+ compositeKey: `${arrayKey}.${itemId}.${field}`,
282
+ fieldPath: `${arrayKey}.${field}`,
283
+ // Normalized path for rules
284
+ value,
285
+ arrayKey,
286
+ itemId,
287
+ field,
288
+ index
289
+ };
290
+ }
291
+ if (argCount === 4) {
292
+ const [parentKey, parentId, field, index] = args;
293
+ const parentIndex = getIndex(parentKey, parentId);
294
+ if (parentIndex === void 0) return null;
295
+ const arr = state[parentKey];
296
+ const item = arr[parentIndex];
297
+ const nestedArr = getNestedValue2(item, field);
298
+ const value = Array.isArray(nestedArr) ? nestedArr[index] : void 0;
299
+ return {
300
+ type: "nestedPrimitiveArray" /* NestedPrimitiveArray */,
301
+ compositeKey: `${parentKey}.${parentId}.${field}.@${index}`,
302
+ fieldPath: `${parentKey}.${field}`,
303
+ // Roughly?
304
+ value,
305
+ parentKey,
306
+ parentId,
307
+ field,
308
+ index,
309
+ nestedField: field
310
+ };
311
+ }
312
+ return null;
313
+ }
314
+
221
315
  // src/hooks/useForm.tsx
222
316
  function useForm(initialState, { rules, messages, arrayIdentifiers } = {}) {
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();
317
+ const [state, setState] = (0, import_react.useState)(initialState);
318
+ const [touched, setTouched] = (0, import_react.useState)(/* @__PURE__ */ new Map());
319
+ const [errors2, setErrors] = (0, import_react.useState)(/* @__PURE__ */ new Map());
320
+ const { performValidations } = useValidate();
321
+ const stateRef = (0, import_react.useRef)(state);
322
+ const touchedRef = (0, import_react.useRef)(touched);
323
+ const errorsRef = (0, import_react.useRef)(errors2);
324
+ stateRef.current = state;
325
+ touchedRef.current = touched;
326
+ errorsRef.current = errors2;
224
327
  const indexMap = (0, import_react.useMemo)(() => {
225
328
  const map2 = /* @__PURE__ */ new Map();
226
- for (const key in state) {
227
- if (Array.isArray(state[key])) {
329
+ const traverse = (current, path) => {
330
+ if (!current || typeof current !== "object") return;
331
+ if (Array.isArray(current)) {
228
332
  const itemMap = /* @__PURE__ */ new Map();
229
- const idKey = arrayIdentifiers?.[key] || "id";
230
- state[key].forEach((item, index) => {
333
+ const genericPath = path.replace(/\.\d+/g, "");
334
+ const idKey = arrayIdentifiers?.[genericPath] || "id";
335
+ current.forEach((item, index) => {
231
336
  if (item && typeof item === "object") {
232
337
  const idValue = item[idKey];
233
338
  if (idValue !== void 0) {
234
339
  itemMap.set(idValue, index);
235
340
  }
341
+ traverse(item, `${path}.${index}`);
236
342
  }
237
343
  });
238
- map2.set(key, itemMap);
344
+ map2.set(path, itemMap);
345
+ } else {
346
+ Object.keys(current).forEach((key) => {
347
+ const nextPath = path ? `${path}.${key}` : key;
348
+ traverse(current[key], nextPath);
349
+ });
239
350
  }
240
- }
351
+ };
352
+ traverse(state, "");
241
353
  return map2;
242
354
  }, [state, arrayIdentifiers]);
243
- const getIndex = (0, import_react.useCallback)(
244
- (arrayKey, itemId) => {
245
- return indexMap.get(arrayKey)?.get(itemId);
355
+ const indexMapRef = (0, import_react.useRef)(indexMap);
356
+ indexMapRef.current = indexMap;
357
+ const getIndex = (0, import_react.useCallback)((arrayKey, itemId) => {
358
+ return indexMapRef.current.get(arrayKey)?.get(itemId);
359
+ }, []);
360
+ const getRule = (0, import_react.useCallback)(
361
+ (path) => {
362
+ if (rules?.[path]) return rules[path];
363
+ const genericPath = path.replace(/\.\d+\./g, ".").replace(/\.\d+$/, "");
364
+ return rules?.[genericPath];
246
365
  },
247
- [indexMap]
366
+ [rules]
248
367
  );
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
- };
259
- const validateInput = (compositeKey, value, ruleKey) => {
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);
264
- setErrors((prev) => new Map(prev).set(compositeKey, error));
265
- };
266
- const getUXProps = (0, import_react.useCallback)(
267
- (compositeKey) => {
268
- const inputError = errors2.get(compositeKey);
269
- const isTouched = touched.get(compositeKey);
270
- return {
271
- isInvalid: Boolean(isTouched && inputError?.isInvalid),
272
- errorMessage: isTouched ? inputError?.errorMessage || "" : ""
273
- };
368
+ const getMessage = (0, import_react.useCallback)(
369
+ (path) => {
370
+ if (messages?.[path]) return messages[path];
371
+ const genericPath = path.replace(/\.\d+\./g, ".").replace(/\.\d+$/, "");
372
+ return messages?.[genericPath];
274
373
  },
275
- [errors2, touched]
374
+ [messages]
276
375
  );
277
- const onBlur = (id) => {
278
- validateInput(String(id), state[id], String(id));
279
- if (touched.get(String(id))) return;
280
- setTouched((prev) => new Map(prev).set(String(id), true));
281
- }, onValueChange = ((...args) => {
282
- if (args.length === 2) {
283
- const [id, value] = args;
284
- setState((prev) => handleNestedChange({ state: prev, id, value }));
285
- validateInput(String(id), value, String(id));
286
- return;
287
- }
288
- if (args.length === 3) {
289
- const [arrayKey, index, value] = args;
290
- setState((prev) => {
291
- const arr = [...prev[arrayKey]];
292
- arr[index] = value;
293
- return { ...prev, [arrayKey]: arr };
294
- });
295
- return;
296
- }
297
- if (args.length === 4) {
298
- const [arrayKey, itemId, field, value] = args;
299
- const index = getIndex(String(arrayKey), itemId);
300
- if (index === void 0) return;
301
- setState(
302
- (prev) => handleArrayItemChange({ state: prev, arrayKey, index, field, value })
303
- );
304
- validateItemInput(arrayKey, itemId, field, value);
305
- return;
306
- }
307
- if (args.length === 5) {
308
- const [parentKey, parentId, field, index, value] = args;
309
- const parentIndex = getIndex(String(parentKey), parentId);
310
- if (parentIndex === void 0) return;
311
- setState((prev) => {
312
- const parentArr = [...prev[parentKey]];
313
- const item = { ...parentArr[parentIndex] };
314
- const nestedArr = [...getNestedValue(item, field) || []];
315
- nestedArr[index] = value;
316
- const updatedItem = setNestedValue(item, field, nestedArr);
317
- parentArr[parentIndex] = updatedItem;
318
- return { ...prev, [parentKey]: parentArr };
376
+ const validateField = (0, import_react.useCallback)(
377
+ (compositeKey, fieldPath, value, extraContext) => {
378
+ let ruleDef = getRule(fieldPath);
379
+ if (!ruleDef) {
380
+ const stripped = fieldPath.replace(/\.@\d+/g, "").replace(/\.@\d+$/, "");
381
+ if (stripped !== fieldPath) ruleDef = getRule(stripped);
382
+ }
383
+ const rule = typeof ruleDef === "function" ? ruleDef(
384
+ value,
385
+ stateRef.current,
386
+ extraContext?.item,
387
+ extraContext?.index
388
+ ) : ruleDef;
389
+ let message = getMessage(fieldPath);
390
+ if (!message && fieldPath !== fieldPath.replace(/\.@\d+/, "")) {
391
+ message = getMessage(fieldPath.replace(/\.@\d+/, ""));
392
+ }
393
+ const error = performValidations(value, rule, message);
394
+ setErrors((prev) => {
395
+ const newMap = new Map(prev);
396
+ newMap.set(compositeKey, error);
397
+ return newMap;
319
398
  });
320
- return;
321
- }
322
- }), onSelectionChange = (id, value) => {
323
- const fixedValue = typeof value === "string" || value === null ? value : Array.from(value);
324
- setState(
325
- (prev) => handleNestedChange({
326
- state: prev,
327
- id,
328
- value: fixedValue
329
- })
330
- );
331
- validateInput(String(id), fixedValue, String(id));
332
- };
333
- const getItemCompositeKey = (arrayKey, itemId, field) => `${arrayKey}.${itemId}.${field}`;
334
- const getPrimitiveCompositeKey = (arrayKey, index) => `${arrayKey}.@${index}`;
335
- const getNestedPrimitiveCompositeKey = (parentKey, parentId, field, index) => `${parentKey}.${parentId}.${field}.@${index}`;
336
- const validateItemInput = (arrayKey, itemId, field, value) => {
337
- const index = getIndex(String(arrayKey), itemId);
338
- if (index === void 0) return;
339
- const item = state[arrayKey][index];
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);
351
- setErrors(
352
- (prev) => new Map(prev).set(compositeKey, performValidations(value, rule, message))
353
- );
354
- };
355
- const getItemUXProps = (0, import_react.useCallback)(
356
- (arrayKey, itemId, field) => {
357
- const compositeKey = getItemCompositeKey(arrayKey, itemId, field);
358
- const inputError = errors2.get(compositeKey);
359
- const isTouched = touched.get(compositeKey);
360
- return {
361
- isInvalid: Boolean(isTouched && inputError?.isInvalid),
362
- errorMessage: isTouched ? inputError?.errorMessage || "" : ""
363
- };
399
+ return error.isInvalid;
364
400
  },
365
- [errors2, touched]
401
+ [getRule, getMessage, performValidations]
366
402
  );
367
- const onItemBlur = (arrayKey, itemId, field) => {
368
- const index = getIndex(String(arrayKey), itemId);
369
- if (index === void 0) return;
370
- const arr = state[arrayKey];
371
- const value = getNestedValue(arr[index], field);
372
- validateItemInput(arrayKey, itemId, field, value);
373
- const compositeKey = getItemCompositeKey(String(arrayKey), itemId, field);
374
- if (touched.get(compositeKey)) return;
375
- setTouched((prev) => new Map(prev).set(compositeKey, true));
376
- };
377
- const onItemValueChange = (arrayKey, itemId, field, value) => {
378
- const index = getIndex(String(arrayKey), itemId);
379
- if (index === void 0) return;
380
- setState(
381
- (prev) => handleArrayItemChange({ state: prev, arrayKey, index, field, value })
382
- );
383
- validateItemInput(arrayKey, itemId, field, value);
384
- };
385
- const onItemSelectionChange = (arrayKey, itemId, field, value) => {
386
- const index = getIndex(String(arrayKey), itemId);
387
- if (index === void 0) return;
388
- const arr = state[arrayKey];
389
- const currentVal = getNestedValue(arr[index], field);
390
- const isString = typeof currentVal === "string" || currentVal === null;
391
- const fixedValue = typeof value === "string" || value === null ? value : isString ? Array.from(value)[0] || null : Array.from(value);
392
- setState(
393
- (prev) => handleArrayItemChange({
394
- state: prev,
395
- arrayKey,
396
- index,
397
- field,
398
- value: fixedValue
399
- })
400
- );
401
- validateItemInput(arrayKey, itemId, field, fixedValue);
402
- };
403
- const validateNestedInput = (dotPath, value) => {
404
- const ruleDef = getRule(dotPath);
405
- const rule = typeof ruleDef === "function" ? ruleDef(value, state) : ruleDef;
406
- const message = getMessage(dotPath);
407
- setErrors(
408
- (prev) => new Map(prev).set(dotPath, performValidations(value, rule, message))
409
- );
410
- };
411
- const onNestedBlur = (dotPath) => {
412
- const value = getNestedValue(state, dotPath);
413
- validateNestedInput(dotPath, value);
414
- if (touched.get(dotPath)) return;
415
- setTouched((prev) => new Map(prev).set(dotPath, true));
416
- };
417
- const onNestedValueChange = (dotPath, value) => {
418
- setState((prev) => setNestedValue(prev, dotPath, value));
419
- validateNestedInput(dotPath, value);
420
- };
421
- const onNestedSelectionChange = (dotPath, value) => {
422
- const currentVal = getNestedValue(state, dotPath);
423
- const isString = typeof currentVal === "string" || currentVal === null;
424
- const fixedValue = typeof value === "string" || value === null ? value : isString ? Array.from(value)[0] || null : Array.from(value);
425
- setState((prev) => setNestedValue(prev, dotPath, fixedValue));
426
- validateNestedInput(dotPath, fixedValue);
427
- };
428
- const on = {
429
- input: (...args) => {
430
- if (args.length === 1) {
431
- const id = args[0];
432
- const key = String(id);
433
- const isTopLevel = key in state;
434
- if (isTopLevel) {
435
- return {
436
- ...getUXProps(key),
437
- id: key,
438
- onBlur: () => onBlur(id),
439
- onValueChange: (v) => onValueChange(id, v),
440
- value: state[id]
441
- };
403
+ const validateAll = (0, import_react.useCallback)(() => {
404
+ if (!rules) return false;
405
+ const nextErrors = /* @__PURE__ */ new Map();
406
+ let hasInvalid = false;
407
+ const processRule = (ruleKey, ruleVal) => {
408
+ const isGeneric = !ruleKey.match(/\.\d+\./) && !ruleKey.match(/\.@\d+/);
409
+ const parts = ruleKey.split(".");
410
+ let arrayKey = "";
411
+ let arrayIndexInPath = -1;
412
+ for (let i = 0; i < parts.length; i++) {
413
+ if (indexMapRef.current.has(parts[i])) {
414
+ arrayKey = parts[i];
415
+ arrayIndexInPath = i;
416
+ break;
442
417
  }
443
- return {
444
- ...getUXProps(key),
445
- id: key,
446
- onBlur: () => onNestedBlur(key),
447
- onValueChange: (v) => onNestedValueChange(key, v),
448
- value: getNestedValue(state, key)
449
- };
450
418
  }
451
- if (args.length === 2) {
452
- const [arrayKey2, index2] = args;
453
- const arr2 = state[arrayKey2];
454
- const value2 = arr2?.[index2];
455
- const compositeKey2 = getPrimitiveCompositeKey(String(arrayKey2), index2);
456
- return {
457
- ...getItemUXProps(String(arrayKey2), `@${index2}`, ""),
458
- id: compositeKey2,
459
- onBlur: () => {
460
- if (touched.get(compositeKey2)) return;
461
- setTouched((prev) => new Map(prev).set(compositeKey2, true));
462
- },
463
- onValueChange: (v) => {
464
- setState((prev) => {
465
- const arr3 = [...prev[arrayKey2]];
466
- arr3[index2] = v;
467
- return { ...prev, [arrayKey2]: arr3 };
468
- });
469
- validateItemInput(arrayKey2, `@${index2}`, "", v);
470
- },
471
- value: value2
472
- };
473
- }
474
- if (args.length === 4) {
475
- const [parentKey, parentId, field2, index2] = args;
476
- const parentIndex = getIndex(String(parentKey), parentId);
477
- const arr2 = state[parentKey];
478
- const nestedArr = parentIndex !== void 0 ? getNestedValue(arr2[parentIndex], field2) : void 0;
479
- const value2 = Array.isArray(nestedArr) ? nestedArr[index2] : void 0;
480
- const compositeKey2 = getNestedPrimitiveCompositeKey(
481
- String(parentKey),
482
- parentId,
483
- field2,
484
- index2
485
- );
486
- return {
487
- ...getItemUXProps(String(parentKey), parentId, `${field2}.@${index2}`),
488
- id: compositeKey2,
489
- onBlur: () => {
490
- if (touched.get(compositeKey2)) return;
491
- setTouched((prev) => new Map(prev).set(compositeKey2, true));
492
- },
493
- onValueChange: (v) => {
494
- if (parentIndex === void 0) return;
495
- setState((prev) => {
496
- const parentArr = [...prev[parentKey]];
497
- const item = { ...parentArr[parentIndex] };
498
- const nestedArr2 = [...getNestedValue(item, field2) || []];
499
- nestedArr2[index2] = v;
500
- parentArr[parentIndex] = setNestedValue(item, field2, nestedArr2);
501
- return { ...prev, [parentKey]: parentArr };
502
- });
503
- const fieldPath = `${field2}.@${index2}`;
504
- validateItemInput(parentKey, parentId, fieldPath, v);
505
- },
506
- value: value2
507
- };
508
- }
509
- const [arrayKey, itemId, field] = args;
510
- const index = getIndex(String(arrayKey), itemId);
511
- const arr = state[arrayKey];
512
- const value = index !== void 0 ? getNestedValue(arr[index], field) : void 0;
513
- const compositeKey = getItemCompositeKey(String(arrayKey), itemId, field);
514
- return {
515
- ...getItemUXProps(String(arrayKey), itemId, field),
516
- id: compositeKey,
517
- onBlur: () => onItemBlur(arrayKey, itemId, field),
518
- onValueChange: (v) => onItemValueChange(arrayKey, itemId, field, v),
519
- value
520
- };
521
- },
522
- select: (...args) => {
523
- if (args.length === 1) {
524
- const id = args[0];
525
- const key = String(id);
526
- const isTopLevel = key in state;
527
- if (isTopLevel) {
528
- const isString3 = typeof state[id] === "string" || state[id] === null;
529
- return {
530
- ...getUXProps(key),
531
- id: key,
532
- onBlur: () => onBlur(id),
533
- onSelectionChange: (v) => onSelectionChange(
534
- id,
535
- !isString3 ? v : Array.from(v)[0] || null
536
- ),
537
- selectedKeys: state[id] === null ? [] : isString3 ? [state[id]] : state[id]
538
- };
419
+ if (arrayKey && arrayIndexInPath !== -1) {
420
+ const itemMap = indexMapRef.current.get(arrayKey);
421
+ if (itemMap) {
422
+ itemMap.forEach((idx, itemId) => {
423
+ const suffix = parts.slice(arrayIndexInPath + 1).join(".");
424
+ const compositeKey = `${arrayKey}.${itemId}` + (suffix ? `.${suffix}` : "");
425
+ const item = stateRef.current[arrayKey][idx];
426
+ const val = getNestedValue(item, suffix);
427
+ const rule = typeof ruleVal === "function" ? ruleVal(val, stateRef.current, item, idx) : ruleVal;
428
+ const msg = getMessage(ruleKey);
429
+ const err = performValidations(val, rule, msg);
430
+ nextErrors.set(compositeKey, err);
431
+ if (err.isInvalid) hasInvalid = true;
432
+ });
539
433
  }
540
- const value2 = getNestedValue(state, key);
541
- const isString2 = typeof value2 === "string" || value2 === null;
542
- return {
543
- ...getUXProps(key),
544
- id: key,
545
- onBlur: () => onNestedBlur(key),
546
- onSelectionChange: (v) => onNestedSelectionChange(key, v),
547
- selectedKeys: value2 === null ? [] : isString2 ? [value2] : value2
548
- };
434
+ } else {
435
+ const val = getNestedValue(stateRef.current, ruleKey);
436
+ const rule = typeof ruleVal === "function" ? ruleVal(val, stateRef.current) : ruleVal;
437
+ const msg = getMessage(ruleKey);
438
+ const err = performValidations(val, rule, msg);
439
+ nextErrors.set(ruleKey, err);
440
+ if (err.isInvalid) hasInvalid = true;
549
441
  }
550
- if (args.length === 2) {
551
- const [arrayKey2, index2] = args;
552
- const arr2 = state[arrayKey2];
553
- const value2 = arr2?.[index2];
554
- const compositeKey2 = getPrimitiveCompositeKey(String(arrayKey2), index2);
555
- return {
556
- ...getItemUXProps(String(arrayKey2), `@${index2}`, ""),
557
- id: compositeKey2,
558
- onBlur: () => {
559
- if (touched.get(compositeKey2)) return;
560
- setTouched((prev) => new Map(prev).set(compositeKey2, true));
561
- },
562
- onSelectionChange: (v) => {
563
- const fixedValue = typeof v === "string" || v === null ? v : Array.from(v)[0] || null;
564
- setState((prev) => {
565
- const arr3 = [...prev[arrayKey2]];
566
- arr3[index2] = fixedValue;
567
- return { ...prev, [arrayKey2]: arr3 };
568
- });
569
- validateItemInput(arrayKey2, `@${index2}`, "", fixedValue);
570
- },
571
- selectedKeys: value2 === null ? [] : [value2]
572
- };
573
- }
574
- if (args.length === 4) {
575
- const [parentKey, parentId, field2, index2] = args;
576
- const parentIndex = getIndex(String(parentKey), parentId);
577
- const arr2 = state[parentKey];
578
- const nestedArr = parentIndex !== void 0 ? getNestedValue(arr2[parentIndex], field2) : void 0;
579
- const value2 = Array.isArray(nestedArr) ? nestedArr[index2] : null;
580
- const compositeKey2 = getNestedPrimitiveCompositeKey(
581
- String(parentKey),
582
- parentId,
583
- field2,
584
- index2
585
- );
586
- return {
587
- ...getItemUXProps(String(parentKey), parentId, `${field2}.@${index2}`),
588
- id: compositeKey2,
589
- onBlur: () => {
590
- if (touched.get(compositeKey2)) return;
591
- setTouched((prev) => new Map(prev).set(compositeKey2, true));
592
- },
593
- onSelectionChange: (v) => {
594
- if (parentIndex === void 0) return;
595
- const fixedValue = typeof v === "string" || v === null ? v : Array.from(v)[0] || null;
596
- setState((prev) => {
597
- const parentArr = [...prev[parentKey]];
598
- const item = { ...parentArr[parentIndex] };
599
- const nestedArr2 = [...getNestedValue(item, field2) || []];
600
- nestedArr2[index2] = fixedValue;
601
- parentArr[parentIndex] = setNestedValue(item, field2, nestedArr2);
602
- return { ...prev, [parentKey]: parentArr };
603
- });
604
- const fieldPath = `${field2}.@${index2}`;
605
- validateItemInput(parentKey, parentId, fieldPath, fixedValue);
606
- },
607
- selectedKeys: value2 === null ? [] : [value2]
442
+ };
443
+ Object.entries(rules).forEach(([k, v]) => processRule(k, v));
444
+ setErrors(nextErrors);
445
+ setTouched((prev) => {
446
+ const next = new Map(prev);
447
+ nextErrors.forEach((_, k) => next.set(k, true));
448
+ return next;
449
+ });
450
+ return hasInvalid;
451
+ }, [rules, getMessage, performValidations]);
452
+ const handleFieldChange = (0, import_react.useCallback)(
453
+ (resolution, newValue) => {
454
+ if (!resolution) return;
455
+ const {
456
+ type,
457
+ compositeKey,
458
+ fieldPath,
459
+ arrayKey,
460
+ index,
461
+ parentKey,
462
+ parentId,
463
+ nestedField
464
+ } = resolution;
465
+ setState((prev) => {
466
+ if (type === "scalar" /* Scalar */) {
467
+ return handleNestedChange({
468
+ state: prev,
469
+ id: compositeKey,
470
+ value: newValue
471
+ });
472
+ }
473
+ if (type === "primitiveArray" /* PrimitiveArray */) {
474
+ const arr = [...prev[arrayKey]];
475
+ arr[index] = newValue;
476
+ return { ...prev, [arrayKey]: arr };
477
+ }
478
+ if (type === "objectArray" /* ObjectArray */) {
479
+ return handleArrayItemChange({
480
+ state: prev,
481
+ arrayKey,
482
+ index,
483
+ field: resolution.field,
484
+ value: newValue
485
+ });
486
+ }
487
+ if (type === "nestedPrimitiveArray" /* NestedPrimitiveArray */) {
488
+ const pIndex = getIndex(parentKey, parentId);
489
+ if (pIndex === void 0) return prev;
490
+ const parentArr = [...prev[parentKey]];
491
+ const pItem = { ...parentArr[pIndex] };
492
+ const nestedArr = [...getNestedValue(pItem, nestedField) || []];
493
+ nestedArr[index] = newValue;
494
+ pItem[nestedField] = nestedArr;
495
+ const updatedItem = setNestedValue(pItem, nestedField, nestedArr);
496
+ parentArr[pIndex] = updatedItem;
497
+ return { ...prev, [parentKey]: parentArr };
498
+ }
499
+ return prev;
500
+ });
501
+ let extraContext = {};
502
+ if (type === "objectArray" /* ObjectArray */ || type === "nestedPrimitiveArray" /* NestedPrimitiveArray */) {
503
+ const idx = index;
504
+ const arrKey = arrayKey || parentKey;
505
+ extraContext = {
506
+ index: idx,
507
+ item: stateRef.current[arrKey]?.[idx]
608
508
  };
609
509
  }
610
- const [arrayKey, itemId, field] = args;
611
- const index = getIndex(String(arrayKey), itemId);
612
- const arr = state[arrayKey];
613
- const value = index !== void 0 ? getNestedValue(arr[index], field) : null;
614
- const isString = typeof value === "string" || value === null;
615
- const compositeKey = getItemCompositeKey(String(arrayKey), itemId, field);
510
+ validateField(compositeKey, fieldPath, newValue, extraContext);
511
+ },
512
+ [getIndex, validateField]
513
+ );
514
+ const createHandlers = (0, import_react.useCallback)(
515
+ (resolution) => {
516
+ if (!resolution) return {};
517
+ const { compositeKey, fieldPath, value } = resolution;
518
+ const err = errorsRef.current.get(compositeKey);
519
+ const isTouched = touchedRef.current.get(compositeKey);
616
520
  return {
617
- ...getItemUXProps(String(arrayKey), itemId, field),
618
521
  id: compositeKey,
619
- onBlur: () => onItemBlur(arrayKey, itemId, field),
620
- onSelectionChange: (v) => onItemSelectionChange(arrayKey, itemId, field, v),
621
- selectedKeys: value === null ? [] : isString ? [value] : value
522
+ isInvalid: Boolean(isTouched && err?.isInvalid),
523
+ errorMessage: isTouched ? err?.errorMessage || "" : "",
524
+ onBlur: () => {
525
+ if (touchedRef.current.get(compositeKey)) return;
526
+ validateField(compositeKey, fieldPath, value);
527
+ setTouched((prev) => new Map(prev).set(compositeKey, true));
528
+ }
622
529
  };
623
530
  },
624
- autocomplete: (...args) => {
625
- if (args.length === 1) {
626
- const id = args[0];
627
- const key = String(id);
628
- const isTopLevel = key in state;
629
- if (isTopLevel) {
630
- return {
631
- ...getUXProps(key),
632
- id: key,
633
- onBlur: () => onBlur(id),
634
- onSelectionChange: (v) => onSelectionChange(id, v),
635
- selectedKey: state[id]
636
- };
637
- }
531
+ [validateField]
532
+ );
533
+ const on = (0, import_react.useMemo)(
534
+ () => ({
535
+ input: (...args) => {
536
+ const data = resolveFieldData(
537
+ args,
538
+ stateRef.current,
539
+ getIndex,
540
+ getNestedValue
541
+ );
542
+ if (!data) return {};
638
543
  return {
639
- ...getUXProps(key),
640
- id: key,
641
- onBlur: () => onNestedBlur(key),
642
- onSelectionChange: (v) => onNestedSelectionChange(key, v),
643
- selectedKey: getNestedValue(state, key)
544
+ ...createHandlers(data),
545
+ value: data.value,
546
+ onValueChange: (v) => handleFieldChange(data, v)
644
547
  };
645
- }
646
- if (args.length === 2) {
647
- const [arrayKey2, index2] = args;
648
- const arr2 = state[arrayKey2];
649
- const value2 = arr2?.[index2];
650
- const compositeKey2 = getPrimitiveCompositeKey(String(arrayKey2), index2);
548
+ },
549
+ select: (...args) => {
550
+ const data = resolveFieldData(
551
+ args,
552
+ stateRef.current,
553
+ getIndex,
554
+ getNestedValue
555
+ );
556
+ if (!data) return {};
557
+ const isString = typeof data.value === "string" || data.value === null;
651
558
  return {
652
- ...getItemUXProps(String(arrayKey2), `@${index2}`, ""),
653
- id: compositeKey2,
654
- onBlur: () => {
655
- if (touched.get(compositeKey2)) return;
656
- setTouched((prev) => new Map(prev).set(compositeKey2, true));
657
- },
559
+ ...createHandlers(data),
560
+ selectedKeys: data.value === null ? [] : isString ? [data.value] : data.value,
658
561
  onSelectionChange: (v) => {
659
- const fixedValue = typeof v === "string" || v === null ? v : String(v);
660
- setState((prev) => {
661
- const arr3 = [...prev[arrayKey2]];
662
- arr3[index2] = fixedValue;
663
- return { ...prev, [arrayKey2]: arr3 };
664
- });
665
- validateItemInput(arrayKey2, `@${index2}`, "", fixedValue);
666
- },
667
- selectedKey: value2
562
+ const fixed = typeof v === "string" || v === null ? v : isString ? Array.from(v)[0] || null : Array.from(v);
563
+ handleFieldChange(data, fixed);
564
+ }
668
565
  };
669
- }
670
- if (args.length === 4) {
671
- const [parentKey, parentId, field2, index2] = args;
672
- const parentIndex = getIndex(String(parentKey), parentId);
673
- const arr2 = state[parentKey];
674
- const nestedArr = parentIndex !== void 0 ? getNestedValue(arr2[parentIndex], field2) : void 0;
675
- const value2 = Array.isArray(nestedArr) ? nestedArr[index2] : null;
676
- const compositeKey2 = getNestedPrimitiveCompositeKey(
677
- String(parentKey),
678
- parentId,
679
- field2,
680
- index2
566
+ },
567
+ autocomplete: (...args) => {
568
+ const data = resolveFieldData(
569
+ args,
570
+ stateRef.current,
571
+ getIndex,
572
+ getNestedValue
681
573
  );
574
+ if (!data) return {};
682
575
  return {
683
- ...getItemUXProps(String(parentKey), parentId, `${field2}.@${index2}`),
684
- id: compositeKey2,
685
- onBlur: () => {
686
- if (touched.get(compositeKey2)) return;
687
- setTouched((prev) => new Map(prev).set(compositeKey2, true));
688
- },
576
+ ...createHandlers(data),
577
+ selectedKey: data.value,
689
578
  onSelectionChange: (v) => {
690
- if (parentIndex === void 0) return;
691
- const fixedValue = typeof v === "string" || v === null ? v : String(v);
692
- setState((prev) => {
693
- const parentArr = [...prev[parentKey]];
694
- const item = { ...parentArr[parentIndex] };
695
- const nestedArr2 = [...getNestedValue(item, field2) || []];
696
- nestedArr2[index2] = fixedValue;
697
- parentArr[parentIndex] = setNestedValue(item, field2, nestedArr2);
698
- return { ...prev, [parentKey]: parentArr };
699
- });
700
- const fieldPath = `${field2}.@${index2}`;
701
- validateItemInput(parentKey, parentId, fieldPath, fixedValue);
702
- },
703
- selectedKey: value2
579
+ const fixed = typeof v === "string" || v === null ? v : String(v);
580
+ handleFieldChange(data, fixed);
581
+ }
704
582
  };
705
583
  }
706
- const [arrayKey, itemId, field] = args;
707
- const index = getIndex(String(arrayKey), itemId);
708
- const arr = state[arrayKey];
709
- const value = index !== void 0 ? getNestedValue(arr[index], field) : null;
710
- const compositeKey = getItemCompositeKey(String(arrayKey), itemId, field);
711
- return {
712
- ...getItemUXProps(String(arrayKey), itemId, field),
713
- id: compositeKey,
714
- onBlur: () => onItemBlur(arrayKey, itemId, field),
715
- onSelectionChange: (v) => onItemSelectionChange(arrayKey, itemId, field, v),
716
- selectedKey: value
717
- };
718
- }
719
- };
720
- return {
721
- onBlur,
722
- onValueChange,
723
- onSelectionChange,
724
- state,
725
- setState,
726
- touched,
727
- errors: errors2,
728
- on,
729
- helpers: {
584
+ }),
585
+ [createHandlers, getIndex, handleFieldChange]
586
+ );
587
+ const helpers = (0, import_react.useMemo)(
588
+ () => ({
730
589
  addItem: (arrayKey, item, index) => {
731
590
  setState((prev) => {
732
591
  const arr = [...prev[arrayKey]];
733
- if (index === void 0) {
734
- arr.push(item);
735
- } else {
736
- arr.splice(index, 0, item);
737
- }
592
+ if (index === void 0) arr.push(item);
593
+ else arr.splice(index, 0, item);
738
594
  return { ...prev, [arrayKey]: arr };
739
595
  });
740
596
  },
741
597
  removeItem: (arrayKey, index) => {
742
- const itemIdKey = arrayIdentifiers?.[arrayKey] || "id";
743
- const item = state[arrayKey][index];
744
- const itemId = item?.[itemIdKey];
598
+ const currentArr = stateRef.current[arrayKey];
599
+ const item = currentArr[index];
600
+ const idKey = arrayIdentifiers?.[arrayKey] || "id";
601
+ const itemId = item?.[idKey];
745
602
  setState((prev) => {
746
603
  const arr = [...prev[arrayKey]];
747
604
  arr.splice(index, 1);
@@ -754,16 +611,25 @@ function useForm(initialState, { rules, messages, arrayIdentifiers } = {}) {
754
611
  }
755
612
  },
756
613
  removeById: (arrayKey, itemId) => {
757
- const index = getIndex(String(arrayKey), itemId);
614
+ const index = getIndex(arrayKey, itemId);
758
615
  if (index !== void 0) {
759
- const arr = [...state[arrayKey]];
760
- arr.splice(index, 1);
761
- setState((prev) => ({ ...prev, [arrayKey]: arr }));
616
+ setState((prev) => {
617
+ const arr = [...prev[arrayKey]];
618
+ arr.splice(index, 1);
619
+ return { ...prev, [arrayKey]: arr };
620
+ });
762
621
  const prefix = `${String(arrayKey)}.${itemId}.`;
763
622
  setTouched((prev) => removeCompositeKeysByPrefix(prev, prefix));
764
623
  setErrors((prev) => removeCompositeKeysByPrefix(prev, prefix));
765
624
  }
766
625
  },
626
+ updateItem: (arrayKey, index, value) => {
627
+ setState((prev) => {
628
+ const arr = [...prev[arrayKey]];
629
+ arr[index] = value;
630
+ return { ...prev, [arrayKey]: arr };
631
+ });
632
+ },
767
633
  moveItem: (arrayKey, from, to) => {
768
634
  setState((prev) => {
769
635
  const arr = [...prev[arrayKey]];
@@ -773,8 +639,8 @@ function useForm(initialState, { rules, messages, arrayIdentifiers } = {}) {
773
639
  });
774
640
  },
775
641
  moveById: (arrayKey, fromId, toId) => {
776
- const fromIndex = getIndex(String(arrayKey), fromId);
777
- const toIndex = getIndex(String(arrayKey), toId);
642
+ const fromIndex = getIndex(arrayKey, fromId);
643
+ const toIndex = getIndex(arrayKey, toId);
778
644
  if (fromIndex !== void 0 && toIndex !== void 0) {
779
645
  setState((prev) => {
780
646
  const arr = [...prev[arrayKey]];
@@ -784,48 +650,80 @@ function useForm(initialState, { rules, messages, arrayIdentifiers } = {}) {
784
650
  });
785
651
  }
786
652
  },
787
- updateItem: (arrayKey, index, value) => {
788
- setState((prev) => {
789
- const arr = [...prev[arrayKey]];
790
- arr[index] = value;
791
- return { ...prev, [arrayKey]: arr };
792
- });
793
- },
794
653
  getItem: (arrayKey, itemId) => {
795
- const index = getIndex(String(arrayKey), itemId);
796
- if (index !== void 0) {
797
- return state[arrayKey][index];
798
- }
799
- return void 0;
654
+ const index = getIndex(arrayKey, itemId);
655
+ if (index === void 0) return void 0;
656
+ return stateRef.current[arrayKey][index];
800
657
  }
658
+ }),
659
+ [getIndex, arrayIdentifiers]
660
+ );
661
+ const onBlur = (id) => {
662
+ validateField(String(id), String(id), stateRef.current[id]);
663
+ setTouched((prev) => new Map(prev).set(String(id), true));
664
+ };
665
+ const polymorphicOnValueChange = (0, import_react.useCallback)(
666
+ (...args) => {
667
+ const value = args[args.length - 1];
668
+ const idArgs = args.slice(0, args.length - 1);
669
+ const data = resolveFieldData(
670
+ idArgs,
671
+ stateRef.current,
672
+ getIndex,
673
+ getNestedValue
674
+ );
675
+ if (data) handleFieldChange(data, value);
801
676
  },
802
- isDirty: JSON.stringify(state) !== JSON.stringify(initialState),
803
- hasInvalidValues: () => {
804
- const isInvalid = Array.from(errors2.values()).some((e) => e?.isInvalid);
805
- return isInvalid;
677
+ [getIndex, handleFieldChange]
678
+ );
679
+ const polymorphicOnSelectionChange = (0, import_react.useCallback)(
680
+ (id, val) => {
681
+ const fixed = typeof val === "string" || val === null ? val : Array.from(val);
682
+ setState((prev) => handleNestedChange({ state: prev, id, value: fixed }));
683
+ validateField(String(id), String(id), fixed);
806
684
  },
685
+ [validateField]
686
+ );
687
+ return {
688
+ state,
689
+ setState,
690
+ touched,
691
+ errors: errors2,
692
+ on,
693
+ helpers,
694
+ onBlur,
695
+ onValueChange: polymorphicOnValueChange,
696
+ onSelectionChange: polymorphicOnSelectionChange,
697
+ isDirty: touched.size > 0,
698
+ hasInvalidValues: validateAll,
807
699
  resetForm: (preservedKeys) => {
700
+ if (preservedKeys && preservedKeys.length > 0) {
701
+ const nextState = { ...initialState };
702
+ preservedKeys.forEach((k) => {
703
+ if (stateRef.current[k] !== void 0)
704
+ nextState[k] = stateRef.current[k];
705
+ });
706
+ setState(nextState);
707
+ } else {
708
+ setState(initialState);
709
+ }
808
710
  setTouched(/* @__PURE__ */ new Map());
809
711
  setErrors(/* @__PURE__ */ new Map());
810
- setState(
811
- (prev) => preservedKeys === void 0 ? initialState : preservedKeys.reduce(
812
- (acc, key) => ({
813
- ...acc,
814
- [key]: prev[key]
815
- }),
816
- initialState
817
- )
818
- );
819
712
  },
820
713
  resetTouched: (preservedKeys) => {
821
- setTouched(
822
- (prev) => preservedKeys === void 0 ? /* @__PURE__ */ new Map() : preservedKeys.reduce((acc, key) => {
823
- if (prev.has(String(key))) {
824
- acc.set(String(key), prev.get(String(key)));
714
+ if (preservedKeys && preservedKeys.length > 0) {
715
+ setTouched((prev) => {
716
+ const next = /* @__PURE__ */ new Map();
717
+ for (const [k, v] of prev.entries()) {
718
+ if (preservedKeys.some((pk) => k.startsWith(String(pk)))) {
719
+ next.set(k, v);
720
+ }
825
721
  }
826
- return acc;
827
- }, /* @__PURE__ */ new Map())
828
- );
722
+ return next;
723
+ });
724
+ } else {
725
+ setTouched(/* @__PURE__ */ new Map());
726
+ }
829
727
  }
830
728
  };
831
729
  }