@formality-ui/react 0.2.0 → 0.2.2
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.cjs +76 -43
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +77 -44
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -581,7 +581,15 @@ interface FormProps<TFieldValues extends FieldValues = FieldValues> {
|
|
|
581
581
|
record?: Partial<TFieldValues>;
|
|
582
582
|
/** Enable auto-save on field changes */
|
|
583
583
|
autoSave?: boolean;
|
|
584
|
-
/**
|
|
584
|
+
/**
|
|
585
|
+
* Form-level auto-save debounce, used as the fallback for any field whose
|
|
586
|
+
* `InputConfig.debounce` is unset.
|
|
587
|
+
* - `false` — submit immediately (no debounce timer).
|
|
588
|
+
* - `number` — delay auto-save by this many milliseconds (default: 1000).
|
|
589
|
+
*
|
|
590
|
+
* A field can override its own cadence via `InputConfig.debounce`
|
|
591
|
+
* (`false` for immediate, or a number for a per-field delay).
|
|
592
|
+
*/
|
|
585
593
|
debounce?: number | false;
|
|
586
594
|
/** Form-level validation */
|
|
587
595
|
validate?: (values: Partial<TFieldValues>) => Record<string, string> | Promise<Record<string, string>>;
|
package/dist/index.d.ts
CHANGED
|
@@ -581,7 +581,15 @@ interface FormProps<TFieldValues extends FieldValues = FieldValues> {
|
|
|
581
581
|
record?: Partial<TFieldValues>;
|
|
582
582
|
/** Enable auto-save on field changes */
|
|
583
583
|
autoSave?: boolean;
|
|
584
|
-
/**
|
|
584
|
+
/**
|
|
585
|
+
* Form-level auto-save debounce, used as the fallback for any field whose
|
|
586
|
+
* `InputConfig.debounce` is unset.
|
|
587
|
+
* - `false` — submit immediately (no debounce timer).
|
|
588
|
+
* - `number` — delay auto-save by this many milliseconds (default: 1000).
|
|
589
|
+
*
|
|
590
|
+
* A field can override its own cadence via `InputConfig.debounce`
|
|
591
|
+
* (`false` for immediate, or a number for a per-field delay).
|
|
592
|
+
*/
|
|
585
593
|
debounce?: number | false;
|
|
586
594
|
/** Form-level validation */
|
|
587
595
|
validate?: (values: Partial<TFieldValues>) => Record<string, string> | Promise<Record<string, string>>;
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createContext, useContext, useMemo, useRef, useState, useCallback, useEffect } from 'react';
|
|
1
|
+
import { createContext, useContext, useMemo, useRef, useState, useCallback, useEffect, createElement } from 'react';
|
|
2
2
|
import { jsx, Fragment } from 'react/jsx-runtime';
|
|
3
3
|
import { useForm, FormProvider, useWatch, Controller, useFormContext as useFormContext$1 } from 'react-hook-form';
|
|
4
4
|
import { debounce } from 'lodash-es';
|
|
@@ -179,6 +179,8 @@ function Form({
|
|
|
179
179
|
const pendingChangedFields = useRef(/* @__PURE__ */ new Set());
|
|
180
180
|
const pendingAffectedFields = useRef(/* @__PURE__ */ new Set());
|
|
181
181
|
const executionVersionRef = useRef(0);
|
|
182
|
+
const fieldDebouncersRef = useRef(/* @__PURE__ */ new Map());
|
|
183
|
+
const getOrCreateDebouncedRef = useRef();
|
|
182
184
|
const registerField = useCallback((name) => {
|
|
183
185
|
fieldRegistry.current.add(name);
|
|
184
186
|
setRegisteredFields(new Set(fieldRegistry.current));
|
|
@@ -279,8 +281,11 @@ function Form({
|
|
|
279
281
|
for (const field of affected) {
|
|
280
282
|
pendingAffectedFields.current.add(field);
|
|
281
283
|
}
|
|
282
|
-
|
|
284
|
+
const fieldDebounce = inputConfig?.debounce;
|
|
285
|
+
if (fieldDebounce === false) {
|
|
283
286
|
executeAutoSaveRef.current?.();
|
|
287
|
+
} else if (typeof fieldDebounce === "number") {
|
|
288
|
+
getOrCreateDebouncedRef.current?.(fieldDebounce)();
|
|
284
289
|
} else {
|
|
285
290
|
debouncedSubmitRef.current?.();
|
|
286
291
|
}
|
|
@@ -421,53 +426,67 @@ function Form({
|
|
|
421
426
|
return;
|
|
422
427
|
}
|
|
423
428
|
}
|
|
424
|
-
const formState = methods.formState;
|
|
425
|
-
if (Object.keys(formState.errors).length > 0) {
|
|
426
|
-
return;
|
|
427
|
-
}
|
|
428
429
|
const values = methods.getValues();
|
|
429
430
|
await handleSubmit(values);
|
|
430
431
|
}, [methods, handleSubmit, waitForFieldValidation]);
|
|
431
432
|
executeAutoSaveRef.current = executeAutoSave;
|
|
433
|
+
const getOrCreateDebounced = useCallback(
|
|
434
|
+
(ms) => {
|
|
435
|
+
const cached = fieldDebouncersRef.current.get(ms);
|
|
436
|
+
if (cached) return cached;
|
|
437
|
+
const debouncedFn = debounce(() => {
|
|
438
|
+
executeAutoSaveRef.current?.();
|
|
439
|
+
}, ms);
|
|
440
|
+
const fn = Object.assign(debouncedFn, {
|
|
441
|
+
pending: () => false
|
|
442
|
+
// lodash debounce tracks pending internally
|
|
443
|
+
});
|
|
444
|
+
fieldDebouncersRef.current.set(ms, fn);
|
|
445
|
+
return fn;
|
|
446
|
+
},
|
|
447
|
+
[]
|
|
448
|
+
);
|
|
449
|
+
getOrCreateDebouncedRef.current = getOrCreateDebounced;
|
|
432
450
|
useEffect(() => {
|
|
451
|
+
return () => {
|
|
452
|
+
fieldDebouncersRef.current.forEach((fn) => fn.cancel());
|
|
453
|
+
fieldDebouncersRef.current.clear();
|
|
454
|
+
};
|
|
455
|
+
}, [getOrCreateDebounced]);
|
|
456
|
+
const debouncedSubmit = useMemo(() => {
|
|
433
457
|
if (debounceMs === false) {
|
|
434
|
-
|
|
458
|
+
return Object.assign(
|
|
435
459
|
() => {
|
|
436
|
-
|
|
460
|
+
executeAutoSaveRef.current?.();
|
|
437
461
|
},
|
|
438
462
|
{
|
|
439
463
|
cancel: () => {
|
|
440
464
|
},
|
|
441
465
|
// No-op for immediate function
|
|
442
|
-
flush: () =>
|
|
466
|
+
flush: () => executeAutoSaveRef.current?.(),
|
|
443
467
|
// Execute immediately on flush
|
|
444
468
|
pending: () => false
|
|
445
469
|
// Never pending when immediate
|
|
446
470
|
}
|
|
447
471
|
);
|
|
448
|
-
debouncedSubmitRef.current = immediateFn;
|
|
449
|
-
return () => {
|
|
450
|
-
};
|
|
451
472
|
}
|
|
452
473
|
const debouncedFn = debounce(() => {
|
|
453
|
-
|
|
474
|
+
executeAutoSaveRef.current?.();
|
|
454
475
|
}, debounceMs);
|
|
455
|
-
|
|
476
|
+
return Object.assign(debouncedFn, {
|
|
456
477
|
pending: () => false
|
|
457
478
|
// lodash debounce handles this internally
|
|
458
479
|
});
|
|
459
|
-
|
|
480
|
+
}, [debounceMs]);
|
|
481
|
+
debouncedSubmitRef.current = debouncedSubmit;
|
|
482
|
+
useEffect(() => {
|
|
460
483
|
return () => {
|
|
461
|
-
|
|
484
|
+
debouncedSubmit.cancel();
|
|
462
485
|
};
|
|
463
|
-
}, [
|
|
486
|
+
}, [debouncedSubmit]);
|
|
464
487
|
const submitImmediate = useCallback(() => {
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
} else {
|
|
468
|
-
executeAutoSave();
|
|
469
|
-
}
|
|
470
|
-
}, [executeAutoSave]);
|
|
488
|
+
debouncedSubmitRef.current?.flush();
|
|
489
|
+
}, []);
|
|
471
490
|
const unusedFields = useMemo(() => {
|
|
472
491
|
const allFields = Object.keys(config);
|
|
473
492
|
return allFields.filter((name) => !registeredFields.has(name));
|
|
@@ -506,7 +525,7 @@ function Form({
|
|
|
506
525
|
setFieldValidating,
|
|
507
526
|
getFormState,
|
|
508
527
|
onSubmit,
|
|
509
|
-
debouncedSubmit
|
|
528
|
+
debouncedSubmit,
|
|
510
529
|
submitImmediate,
|
|
511
530
|
unusedFields,
|
|
512
531
|
methods
|
|
@@ -525,6 +544,7 @@ function Form({
|
|
|
525
544
|
setFieldValidating,
|
|
526
545
|
getFormState,
|
|
527
546
|
onSubmit,
|
|
547
|
+
debouncedSubmit,
|
|
528
548
|
submitImmediate,
|
|
529
549
|
unusedFields,
|
|
530
550
|
methods
|
|
@@ -560,11 +580,18 @@ function useInferredInputs(options) {
|
|
|
560
580
|
selectProps,
|
|
561
581
|
formDefaultFieldProps,
|
|
562
582
|
providerDefaultFieldProps,
|
|
563
|
-
conditions
|
|
564
|
-
subscribesTo
|
|
583
|
+
conditions,
|
|
584
|
+
subscribesTo
|
|
565
585
|
} = options;
|
|
586
|
+
const signature = JSON.stringify({
|
|
587
|
+
selectProps,
|
|
588
|
+
formDefaultFieldProps,
|
|
589
|
+
providerDefaultFieldProps,
|
|
590
|
+
conditions,
|
|
591
|
+
subscribesTo
|
|
592
|
+
});
|
|
566
593
|
return useMemo(() => {
|
|
567
|
-
const inferred = [...subscribesTo];
|
|
594
|
+
const inferred = [...subscribesTo ?? []];
|
|
568
595
|
if (providerDefaultFieldProps) {
|
|
569
596
|
inferred.push(...inferFieldsFromDescriptor(providerDefaultFieldProps));
|
|
570
597
|
}
|
|
@@ -574,17 +601,11 @@ function useInferredInputs(options) {
|
|
|
574
601
|
if (selectProps) {
|
|
575
602
|
inferred.push(...inferFieldsFromDescriptor(selectProps));
|
|
576
603
|
}
|
|
577
|
-
if (conditions.length > 0) {
|
|
604
|
+
if (conditions && conditions.length > 0) {
|
|
578
605
|
inferred.push(...inferFieldsFromConditions(conditions));
|
|
579
606
|
}
|
|
580
607
|
return [...new Set(inferred)];
|
|
581
|
-
}, [
|
|
582
|
-
providerDefaultFieldProps,
|
|
583
|
-
formDefaultFieldProps,
|
|
584
|
-
selectProps,
|
|
585
|
-
conditions,
|
|
586
|
-
subscribesTo
|
|
587
|
-
]);
|
|
608
|
+
}, [signature]);
|
|
588
609
|
}
|
|
589
610
|
|
|
590
611
|
// src/hooks/useConditions.ts
|
|
@@ -1119,17 +1140,29 @@ function Field({
|
|
|
1119
1140
|
}
|
|
1120
1141
|
});
|
|
1121
1142
|
const Component = inputConfig.component;
|
|
1143
|
+
const isHostComponent = typeof inputConfig.component === "string";
|
|
1122
1144
|
const template = inputConfig.template ?? providerConfig.inputTemplates[type] ?? providerConfig.defaultInputTemplate;
|
|
1123
1145
|
const TemplateComponent = template;
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1146
|
+
let renderedField;
|
|
1147
|
+
if (TemplateComponent) {
|
|
1148
|
+
renderedField = /* @__PURE__ */ jsx(
|
|
1149
|
+
TemplateComponent,
|
|
1150
|
+
{
|
|
1151
|
+
Field: Component,
|
|
1152
|
+
fieldProps: finalProps,
|
|
1153
|
+
fieldState,
|
|
1154
|
+
formState
|
|
1155
|
+
}
|
|
1156
|
+
);
|
|
1157
|
+
} else if (isHostComponent) {
|
|
1158
|
+
const { forwardRef: hostRef, ...restHostProps } = finalProps;
|
|
1159
|
+
renderedField = createElement(inputConfig.component, {
|
|
1160
|
+
...restHostProps,
|
|
1161
|
+
ref: hostRef
|
|
1162
|
+
});
|
|
1163
|
+
} else {
|
|
1164
|
+
renderedField = /* @__PURE__ */ jsx(Component, { ...finalProps });
|
|
1165
|
+
}
|
|
1133
1166
|
if (typeof children === "function") {
|
|
1134
1167
|
const result = children({
|
|
1135
1168
|
fieldState,
|