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