@formality-ui/react 0.0.0 → 0.2.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/README.md +251 -32
- package/dist/index.cjs +343 -78
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +250 -19
- package/dist/index.d.ts +250 -19
- package/dist/index.js +343 -79
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -23,7 +23,9 @@ function useConfigContext() {
|
|
|
23
23
|
var FormContext = createContext(null);
|
|
24
24
|
FormContext.displayName = "FormalityFormContext";
|
|
25
25
|
function useFormContext() {
|
|
26
|
-
const context = useContext(
|
|
26
|
+
const context = useContext(
|
|
27
|
+
FormContext
|
|
28
|
+
);
|
|
27
29
|
if (!context) {
|
|
28
30
|
throw new Error(
|
|
29
31
|
"useFormContext must be used within a Form component. Make sure your component is wrapped in a <Form> component."
|
|
@@ -109,7 +111,6 @@ function makeDeepProxyState(source) {
|
|
|
109
111
|
const result = {};
|
|
110
112
|
for (const key in source) {
|
|
111
113
|
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
112
|
-
source[key];
|
|
113
114
|
Object.defineProperty(result, key, {
|
|
114
115
|
get: () => {
|
|
115
116
|
const currentValue = source[key];
|
|
@@ -191,6 +192,11 @@ function Form({
|
|
|
191
192
|
invertedSubscriptions.current.set(target, /* @__PURE__ */ new Set());
|
|
192
193
|
}
|
|
193
194
|
invertedSubscriptions.current.get(target).add(subscriber);
|
|
195
|
+
if (process.env.NODE_ENV !== "production") {
|
|
196
|
+
console.warn(
|
|
197
|
+
`[Formality Subscription] "${subscriber}" added to watch "${target}"`
|
|
198
|
+
);
|
|
199
|
+
}
|
|
194
200
|
const setter = watcherSetters.current.get(target);
|
|
195
201
|
if (setter) {
|
|
196
202
|
setter((prev) => ({ ...prev, [subscriber]: true }));
|
|
@@ -203,7 +209,20 @@ function Form({
|
|
|
203
209
|
}, []);
|
|
204
210
|
const removeSubscription = useCallback(
|
|
205
211
|
(target, subscriber) => {
|
|
212
|
+
const subscribers = invertedSubscriptions.current.get(target);
|
|
213
|
+
const subscriptionExists = subscribers?.has(subscriber) ?? false;
|
|
206
214
|
invertedSubscriptions.current.get(target)?.delete(subscriber);
|
|
215
|
+
if (process.env.NODE_ENV !== "production") {
|
|
216
|
+
if (subscriptionExists) {
|
|
217
|
+
console.warn(
|
|
218
|
+
`[Formality Subscription] "${subscriber}" removed from watching "${target}"`
|
|
219
|
+
);
|
|
220
|
+
} else {
|
|
221
|
+
console.warn(
|
|
222
|
+
`[Formality Subscription] WARNING: Double-cleanup attempt - "${subscriber}" was not watching "${target}"`
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
207
226
|
const setter = watcherSetters.current.get(target);
|
|
208
227
|
if (setter) {
|
|
209
228
|
setter((prev) => {
|
|
@@ -253,14 +272,18 @@ function Form({
|
|
|
253
272
|
return affected;
|
|
254
273
|
}, []);
|
|
255
274
|
const changeField = useCallback(
|
|
256
|
-
(name, value) => {
|
|
275
|
+
(name, value, inputConfig) => {
|
|
257
276
|
if (autoSave) {
|
|
258
277
|
pendingChangedFields.current.add(name);
|
|
259
278
|
const affected = getAffectedFields(name);
|
|
260
279
|
for (const field of affected) {
|
|
261
280
|
pendingAffectedFields.current.add(field);
|
|
262
281
|
}
|
|
263
|
-
|
|
282
|
+
if (inputConfig?.debounce === false) {
|
|
283
|
+
executeAutoSaveRef.current?.();
|
|
284
|
+
} else {
|
|
285
|
+
debouncedSubmitRef.current?.();
|
|
286
|
+
}
|
|
264
287
|
}
|
|
265
288
|
},
|
|
266
289
|
[autoSave, getAffectedFields]
|
|
@@ -303,7 +326,7 @@ function Form({
|
|
|
303
326
|
};
|
|
304
327
|
}, [methods, config, record, defaultValues]);
|
|
305
328
|
const handleSubmit = useCallback(
|
|
306
|
-
async (values) => {
|
|
329
|
+
async (values, overrideOnSubmit) => {
|
|
307
330
|
for (const [, isValidating] of validatingFields.current) {
|
|
308
331
|
if (isValidating) return;
|
|
309
332
|
}
|
|
@@ -316,12 +339,26 @@ function Form({
|
|
|
316
339
|
return;
|
|
317
340
|
}
|
|
318
341
|
}
|
|
319
|
-
const submitValues = transformValuesForSubmit(
|
|
320
|
-
|
|
342
|
+
const submitValues = transformValuesForSubmit(
|
|
343
|
+
values,
|
|
344
|
+
config,
|
|
345
|
+
mergedInputs
|
|
346
|
+
);
|
|
347
|
+
const sink = overrideOnSubmit ?? onSubmit;
|
|
348
|
+
await sink?.(submitValues);
|
|
321
349
|
},
|
|
322
350
|
[validate, methods, onSubmit, config, mergedInputs]
|
|
323
351
|
);
|
|
352
|
+
const handleRenderSubmit = useCallback(
|
|
353
|
+
(userOnSubmit) => {
|
|
354
|
+
return methods.handleSubmit(async (values) => {
|
|
355
|
+
await handleSubmit(values, userOnSubmit);
|
|
356
|
+
});
|
|
357
|
+
},
|
|
358
|
+
[methods, handleSubmit]
|
|
359
|
+
);
|
|
324
360
|
const debouncedSubmitRef = useRef();
|
|
361
|
+
const executeAutoSaveRef = useRef();
|
|
325
362
|
const waitForFieldValidation = useCallback(
|
|
326
363
|
async (fields, version) => {
|
|
327
364
|
const maxWaitMs = 1e4;
|
|
@@ -353,29 +390,36 @@ function Form({
|
|
|
353
390
|
if (changedFields.size === 0) {
|
|
354
391
|
return;
|
|
355
392
|
}
|
|
356
|
-
const
|
|
393
|
+
const fieldsToWaitFor = [...changedFields, ...affectedFields];
|
|
394
|
+
const fieldsToTrigger = [...affectedFields];
|
|
357
395
|
const validationsComplete = await waitForFieldValidation(
|
|
358
|
-
|
|
396
|
+
fieldsToWaitFor,
|
|
359
397
|
executionVersion
|
|
360
398
|
);
|
|
361
399
|
if (!validationsComplete || executionVersionRef.current !== executionVersion) {
|
|
362
400
|
return;
|
|
363
401
|
}
|
|
364
|
-
|
|
365
|
-
const
|
|
402
|
+
for (const fieldName of changedFields) {
|
|
403
|
+
const fieldState = methods.getFieldState(fieldName);
|
|
404
|
+
if (fieldState.error) {
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
if (fieldsToTrigger.length > 0) {
|
|
409
|
+
const isValid = await methods.trigger(fieldsToTrigger);
|
|
366
410
|
if (executionVersionRef.current !== executionVersion) {
|
|
367
411
|
return;
|
|
368
412
|
}
|
|
369
413
|
if (!isValid) {
|
|
370
414
|
return;
|
|
371
415
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
416
|
+
const postTriggerComplete = await waitForFieldValidation(
|
|
417
|
+
fieldsToTrigger,
|
|
418
|
+
executionVersion
|
|
419
|
+
);
|
|
420
|
+
if (!postTriggerComplete || executionVersionRef.current !== executionVersion) {
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
379
423
|
}
|
|
380
424
|
const formState = methods.formState;
|
|
381
425
|
if (Object.keys(formState.errors).length > 0) {
|
|
@@ -384,7 +428,27 @@ function Form({
|
|
|
384
428
|
const values = methods.getValues();
|
|
385
429
|
await handleSubmit(values);
|
|
386
430
|
}, [methods, handleSubmit, waitForFieldValidation]);
|
|
431
|
+
executeAutoSaveRef.current = executeAutoSave;
|
|
387
432
|
useEffect(() => {
|
|
433
|
+
if (debounceMs === false) {
|
|
434
|
+
const immediateFn = Object.assign(
|
|
435
|
+
() => {
|
|
436
|
+
executeAutoSave();
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
cancel: () => {
|
|
440
|
+
},
|
|
441
|
+
// No-op for immediate function
|
|
442
|
+
flush: () => executeAutoSave(),
|
|
443
|
+
// Execute immediately on flush
|
|
444
|
+
pending: () => false
|
|
445
|
+
// Never pending when immediate
|
|
446
|
+
}
|
|
447
|
+
);
|
|
448
|
+
debouncedSubmitRef.current = immediateFn;
|
|
449
|
+
return () => {
|
|
450
|
+
};
|
|
451
|
+
}
|
|
388
452
|
const debouncedFn = debounce(() => {
|
|
389
453
|
executeAutoSave();
|
|
390
454
|
}, debounceMs);
|
|
@@ -397,12 +461,13 @@ function Form({
|
|
|
397
461
|
debouncedFn.cancel();
|
|
398
462
|
};
|
|
399
463
|
}, [executeAutoSave, debounceMs]);
|
|
400
|
-
const debouncedSubmit = useCallback(() => {
|
|
401
|
-
debouncedSubmitRef.current?.();
|
|
402
|
-
}, []);
|
|
403
464
|
const submitImmediate = useCallback(() => {
|
|
404
|
-
debouncedSubmitRef.current?.flush
|
|
405
|
-
|
|
465
|
+
if (debouncedSubmitRef.current?.flush) {
|
|
466
|
+
debouncedSubmitRef.current.flush();
|
|
467
|
+
} else {
|
|
468
|
+
executeAutoSave();
|
|
469
|
+
}
|
|
470
|
+
}, [executeAutoSave]);
|
|
406
471
|
const unusedFields = useMemo(() => {
|
|
407
472
|
const allFields = Object.keys(config);
|
|
408
473
|
return allFields.filter((name) => !registeredFields.has(name));
|
|
@@ -470,6 +535,7 @@ function Form({
|
|
|
470
535
|
unusedFields,
|
|
471
536
|
formState: methods.formState,
|
|
472
537
|
methods,
|
|
538
|
+
handleSubmit: handleRenderSubmit,
|
|
473
539
|
resolvedTitle
|
|
474
540
|
}) : children }) }) });
|
|
475
541
|
}
|
|
@@ -490,9 +556,21 @@ function transformValuesForSubmit(values, config, inputs) {
|
|
|
490
556
|
return result;
|
|
491
557
|
}
|
|
492
558
|
function useInferredInputs(options) {
|
|
493
|
-
const {
|
|
559
|
+
const {
|
|
560
|
+
selectProps,
|
|
561
|
+
formDefaultFieldProps,
|
|
562
|
+
providerDefaultFieldProps,
|
|
563
|
+
conditions = [],
|
|
564
|
+
subscribesTo = []
|
|
565
|
+
} = options;
|
|
494
566
|
return useMemo(() => {
|
|
495
567
|
const inferred = [...subscribesTo];
|
|
568
|
+
if (providerDefaultFieldProps) {
|
|
569
|
+
inferred.push(...inferFieldsFromDescriptor(providerDefaultFieldProps));
|
|
570
|
+
}
|
|
571
|
+
if (formDefaultFieldProps) {
|
|
572
|
+
inferred.push(...inferFieldsFromDescriptor(formDefaultFieldProps));
|
|
573
|
+
}
|
|
496
574
|
if (selectProps) {
|
|
497
575
|
inferred.push(...inferFieldsFromDescriptor(selectProps));
|
|
498
576
|
}
|
|
@@ -500,12 +578,18 @@ function useInferredInputs(options) {
|
|
|
500
578
|
inferred.push(...inferFieldsFromConditions(conditions));
|
|
501
579
|
}
|
|
502
580
|
return [...new Set(inferred)];
|
|
503
|
-
}, [
|
|
581
|
+
}, [
|
|
582
|
+
providerDefaultFieldProps,
|
|
583
|
+
formDefaultFieldProps,
|
|
584
|
+
selectProps,
|
|
585
|
+
conditions,
|
|
586
|
+
subscribesTo
|
|
587
|
+
]);
|
|
504
588
|
}
|
|
505
589
|
|
|
506
590
|
// src/hooks/useConditions.ts
|
|
507
591
|
function useConditions(options) {
|
|
508
|
-
const { conditions, subscribesTo, props } = options;
|
|
592
|
+
const { conditions, subscribesTo, props, allFieldsConfig } = options;
|
|
509
593
|
const { record, methods } = useFormContext();
|
|
510
594
|
const watchFields = useInferredInputs({
|
|
511
595
|
conditions,
|
|
@@ -529,12 +613,14 @@ function useConditions(options) {
|
|
|
529
613
|
}
|
|
530
614
|
return values;
|
|
531
615
|
}, [watchFields, watchedValues]);
|
|
532
|
-
const
|
|
616
|
+
const baseFieldStates = useMemo(() => {
|
|
533
617
|
const states = {};
|
|
534
|
-
|
|
618
|
+
const currentFieldName = props?.name;
|
|
619
|
+
const allFieldsForPass1 = currentFieldName ? [.../* @__PURE__ */ new Set([...watchFields, currentFieldName])] : watchFields;
|
|
620
|
+
if (allFieldsForPass1.length === 0) {
|
|
535
621
|
return states;
|
|
536
622
|
}
|
|
537
|
-
|
|
623
|
+
allFieldsForPass1.forEach((fieldName) => {
|
|
538
624
|
const fieldState = methods.getFieldState(fieldName);
|
|
539
625
|
states[fieldName] = {
|
|
540
626
|
value: fieldValues[fieldName],
|
|
@@ -544,10 +630,112 @@ function useConditions(options) {
|
|
|
544
630
|
invalid: fieldState.invalid,
|
|
545
631
|
isValidating: false
|
|
546
632
|
// Not easily available per-field
|
|
633
|
+
// ❌ NO disabled property - this is critical for Pass 1 to prevent circular dependency
|
|
547
634
|
};
|
|
548
635
|
});
|
|
549
636
|
return states;
|
|
550
|
-
}, [watchFields, fieldValues, methods]);
|
|
637
|
+
}, [watchFields, fieldValues, methods, props]);
|
|
638
|
+
const disabledStates = useMemo(() => {
|
|
639
|
+
const disabled = {};
|
|
640
|
+
const currentFieldName = props?.name;
|
|
641
|
+
const configFields = allFieldsConfig ? Object.keys(allFieldsConfig) : [];
|
|
642
|
+
const allFieldsForPass2 = [.../* @__PURE__ */ new Set([...watchFields, ...configFields])];
|
|
643
|
+
if (currentFieldName && !allFieldsForPass2.includes(currentFieldName)) {
|
|
644
|
+
allFieldsForPass2.push(currentFieldName);
|
|
645
|
+
}
|
|
646
|
+
if (allFieldsForPass2.length === 0) {
|
|
647
|
+
return disabled;
|
|
648
|
+
}
|
|
649
|
+
const allBaseStates = {};
|
|
650
|
+
for (const fieldName of allFieldsForPass2) {
|
|
651
|
+
if (baseFieldStates[fieldName]) {
|
|
652
|
+
allBaseStates[fieldName] = baseFieldStates[fieldName];
|
|
653
|
+
} else {
|
|
654
|
+
const fieldState = methods.getFieldState(fieldName);
|
|
655
|
+
allBaseStates[fieldName] = {
|
|
656
|
+
value: fieldValues[fieldName],
|
|
657
|
+
isTouched: fieldState.isTouched,
|
|
658
|
+
isDirty: fieldState.isDirty,
|
|
659
|
+
error: fieldState.error,
|
|
660
|
+
invalid: fieldState.invalid,
|
|
661
|
+
isValidating: false
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
let currentStates = allBaseStates;
|
|
666
|
+
let hasChanged = true;
|
|
667
|
+
let iteration = 0;
|
|
668
|
+
const maxIterations = 10;
|
|
669
|
+
while (hasChanged && iteration < maxIterations) {
|
|
670
|
+
hasChanged = false;
|
|
671
|
+
iteration++;
|
|
672
|
+
const newDisabled = {};
|
|
673
|
+
for (const fieldName of allFieldsForPass2) {
|
|
674
|
+
const fieldConfig = allFieldsConfig?.[fieldName];
|
|
675
|
+
const fieldConditions = fieldConfig?.conditions ?? [];
|
|
676
|
+
const configDisabled = fieldConfig?.disabled;
|
|
677
|
+
const result = evaluateConditions({
|
|
678
|
+
conditions: fieldConditions,
|
|
679
|
+
fieldValues,
|
|
680
|
+
fieldStates: currentStates,
|
|
681
|
+
record,
|
|
682
|
+
props: { name: fieldName }
|
|
683
|
+
});
|
|
684
|
+
newDisabled[fieldName] = configDisabled ?? result.disabled ?? false;
|
|
685
|
+
}
|
|
686
|
+
const newStates = {};
|
|
687
|
+
for (const fieldName of allFieldsForPass2) {
|
|
688
|
+
const baseState = allBaseStates[fieldName];
|
|
689
|
+
if (baseState) {
|
|
690
|
+
newStates[fieldName] = {
|
|
691
|
+
...baseState,
|
|
692
|
+
disabled: newDisabled[fieldName]
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
for (const fieldName of allFieldsForPass2) {
|
|
697
|
+
const oldDisabled = disabled[fieldName];
|
|
698
|
+
const newDisabledValue = newDisabled[fieldName];
|
|
699
|
+
if (oldDisabled !== newDisabledValue) {
|
|
700
|
+
hasChanged = true;
|
|
701
|
+
disabled[fieldName] = newDisabledValue;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
currentStates = newStates;
|
|
705
|
+
}
|
|
706
|
+
return disabled;
|
|
707
|
+
}, [
|
|
708
|
+
watchFields,
|
|
709
|
+
allFieldsConfig,
|
|
710
|
+
fieldValues,
|
|
711
|
+
baseFieldStates,
|
|
712
|
+
record,
|
|
713
|
+
props,
|
|
714
|
+
methods
|
|
715
|
+
]);
|
|
716
|
+
const fieldStates = useMemo(() => {
|
|
717
|
+
const states = { ...baseFieldStates };
|
|
718
|
+
Object.entries(disabledStates).forEach(([fieldName, disabled]) => {
|
|
719
|
+
if (states[fieldName]) {
|
|
720
|
+
states[fieldName] = {
|
|
721
|
+
...states[fieldName],
|
|
722
|
+
disabled
|
|
723
|
+
};
|
|
724
|
+
} else {
|
|
725
|
+
const fieldState = methods.getFieldState(fieldName);
|
|
726
|
+
states[fieldName] = {
|
|
727
|
+
value: fieldValues[fieldName],
|
|
728
|
+
isTouched: fieldState.isTouched,
|
|
729
|
+
isDirty: fieldState.isDirty,
|
|
730
|
+
error: fieldState.error,
|
|
731
|
+
invalid: fieldState.invalid,
|
|
732
|
+
isValidating: false,
|
|
733
|
+
disabled
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
});
|
|
737
|
+
return states;
|
|
738
|
+
}, [baseFieldStates, disabledStates, fieldValues, methods]);
|
|
551
739
|
return useMemo(() => {
|
|
552
740
|
if (conditions.length === 0) {
|
|
553
741
|
return {
|
|
@@ -569,9 +757,17 @@ function useConditions(options) {
|
|
|
569
757
|
}, [conditions, fieldValues, fieldStates, record, props]);
|
|
570
758
|
}
|
|
571
759
|
function usePropsEvaluation(options) {
|
|
572
|
-
const {
|
|
760
|
+
const {
|
|
761
|
+
selectProps,
|
|
762
|
+
formDefaultFieldProps,
|
|
763
|
+
providerDefaultFieldProps,
|
|
764
|
+
subscribesTo,
|
|
765
|
+
fieldName
|
|
766
|
+
} = options;
|
|
573
767
|
const { record, methods } = useFormContext();
|
|
574
768
|
const watchFields = useInferredInputs({
|
|
769
|
+
providerDefaultFieldProps,
|
|
770
|
+
formDefaultFieldProps,
|
|
575
771
|
selectProps,
|
|
576
772
|
subscribesTo
|
|
577
773
|
});
|
|
@@ -582,10 +778,13 @@ function usePropsEvaluation(options) {
|
|
|
582
778
|
const formState = useMemo(() => {
|
|
583
779
|
const fields = {};
|
|
584
780
|
if (watchFields.length > 0) {
|
|
585
|
-
const values =
|
|
586
|
-
acc
|
|
587
|
-
|
|
588
|
-
|
|
781
|
+
const values = Array.isArray(watchedValues) ? watchFields.reduce(
|
|
782
|
+
(acc, field, i) => {
|
|
783
|
+
acc[field] = watchedValues[i];
|
|
784
|
+
return acc;
|
|
785
|
+
},
|
|
786
|
+
{}
|
|
787
|
+
) : watchedValues;
|
|
589
788
|
watchFields.forEach((name) => {
|
|
590
789
|
fields[name] = makeProxyState({
|
|
591
790
|
value: values[name],
|
|
@@ -611,40 +810,75 @@ function usePropsEvaluation(options) {
|
|
|
611
810
|
};
|
|
612
811
|
}, [watchFields, watchedValues, record]);
|
|
613
812
|
return useMemo(() => {
|
|
614
|
-
|
|
615
|
-
|
|
813
|
+
let providerResult = {};
|
|
814
|
+
if (providerDefaultFieldProps) {
|
|
815
|
+
if (typeof providerDefaultFieldProps === "function") {
|
|
816
|
+
providerResult = providerDefaultFieldProps(formState, methods) ?? {};
|
|
817
|
+
} else {
|
|
818
|
+
const context = buildFieldContext(formState, fieldName);
|
|
819
|
+
providerResult = evaluateDescriptor(providerDefaultFieldProps, context) ?? {};
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
let formResult = {};
|
|
823
|
+
if (formDefaultFieldProps) {
|
|
824
|
+
if (typeof formDefaultFieldProps === "function") {
|
|
825
|
+
formResult = formDefaultFieldProps(formState, methods) ?? {};
|
|
826
|
+
} else {
|
|
827
|
+
const context = buildFieldContext(formState, fieldName);
|
|
828
|
+
formResult = evaluateDescriptor(formDefaultFieldProps, context) ?? {};
|
|
829
|
+
}
|
|
616
830
|
}
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
831
|
+
let fieldResult = {};
|
|
832
|
+
if (selectProps) {
|
|
833
|
+
if (typeof selectProps === "function") {
|
|
834
|
+
fieldResult = selectProps(formState, methods) ?? {};
|
|
835
|
+
} else {
|
|
836
|
+
const context = buildFieldContext(formState, fieldName);
|
|
837
|
+
fieldResult = evaluateDescriptor(selectProps, context) ?? {};
|
|
838
|
+
}
|
|
620
839
|
}
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
840
|
+
return {
|
|
841
|
+
providerSelectProps: providerResult,
|
|
842
|
+
formSelectProps: formResult,
|
|
843
|
+
fieldSelectProps: fieldResult
|
|
844
|
+
};
|
|
845
|
+
}, [
|
|
846
|
+
providerDefaultFieldProps,
|
|
847
|
+
formDefaultFieldProps,
|
|
848
|
+
selectProps,
|
|
849
|
+
formState,
|
|
850
|
+
methods,
|
|
851
|
+
fieldName
|
|
852
|
+
]);
|
|
625
853
|
}
|
|
626
854
|
function useSubscriptions(fieldName, subscriptions) {
|
|
627
855
|
const { addSubscription, removeSubscription } = useFormContext();
|
|
628
|
-
const
|
|
856
|
+
const runIdRef = useRef(0);
|
|
857
|
+
const runSubscriptionsRef = useRef(/* @__PURE__ */ new Map());
|
|
629
858
|
useEffect(() => {
|
|
630
|
-
const
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
);
|
|
634
|
-
const toAdd = subscriptions.filter(
|
|
635
|
-
(target) => !prevSubscriptions.includes(target)
|
|
636
|
-
);
|
|
637
|
-
toRemove.forEach((target) => {
|
|
638
|
-
removeSubscription(target, fieldName);
|
|
639
|
-
});
|
|
640
|
-
toAdd.forEach((target) => {
|
|
859
|
+
const currentRunId = ++runIdRef.current;
|
|
860
|
+
runSubscriptionsRef.current.set(currentRunId, [...subscriptions]);
|
|
861
|
+
subscriptions.forEach((target) => {
|
|
641
862
|
addSubscription(target, fieldName);
|
|
863
|
+
if (process.env.NODE_ENV !== "production") {
|
|
864
|
+
console.warn(
|
|
865
|
+
`[Formality Subscription] Run ${currentRunId}: "${fieldName}" subscribing to "${target}"`
|
|
866
|
+
);
|
|
867
|
+
}
|
|
642
868
|
});
|
|
643
|
-
prevSubscriptionsRef.current = subscriptions;
|
|
644
869
|
return () => {
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
870
|
+
const thisRunSubscriptions = runSubscriptionsRef.current.get(currentRunId);
|
|
871
|
+
if (thisRunSubscriptions) {
|
|
872
|
+
if (process.env.NODE_ENV !== "production") {
|
|
873
|
+
console.warn(
|
|
874
|
+
`[Formality Subscription] Run ${currentRunId}: "${fieldName}" cleaning up [${thisRunSubscriptions.join(", ")}]`
|
|
875
|
+
);
|
|
876
|
+
}
|
|
877
|
+
[...thisRunSubscriptions].reverse().forEach((target) => {
|
|
878
|
+
removeSubscription(target, fieldName);
|
|
879
|
+
});
|
|
880
|
+
runSubscriptionsRef.current.delete(currentRunId);
|
|
881
|
+
}
|
|
648
882
|
};
|
|
649
883
|
}, [fieldName, subscriptions, addSubscription, removeSubscription]);
|
|
650
884
|
}
|
|
@@ -655,6 +889,7 @@ function Field({
|
|
|
655
889
|
hidden: hiddenProp,
|
|
656
890
|
children,
|
|
657
891
|
shouldRegister = true,
|
|
892
|
+
inputConfig: inputConfigProp,
|
|
658
893
|
...restProps
|
|
659
894
|
}) {
|
|
660
895
|
const {
|
|
@@ -674,14 +909,23 @@ function Field({
|
|
|
674
909
|
const type = typeProp ?? fieldConfig.type ?? "textField";
|
|
675
910
|
const inputConfig = useMemo(() => {
|
|
676
911
|
const formInputs = typeof formConfig.inputs === "function" ? formConfig.inputs(providerConfig.inputs) : formConfig.inputs ?? {};
|
|
677
|
-
const mergedInputs = {
|
|
912
|
+
const mergedInputs = {
|
|
913
|
+
...providerConfig.inputs
|
|
914
|
+
};
|
|
678
915
|
for (const [key, override] of Object.entries(formInputs)) {
|
|
679
916
|
if (mergedInputs[key]) {
|
|
680
|
-
mergedInputs[key] = {
|
|
917
|
+
mergedInputs[key] = {
|
|
918
|
+
...mergedInputs[key],
|
|
919
|
+
...override
|
|
920
|
+
};
|
|
681
921
|
}
|
|
682
922
|
}
|
|
683
|
-
|
|
684
|
-
|
|
923
|
+
const baseInputConfig = resolveInputConfig(type, mergedInputs) ?? {
|
|
924
|
+
component: "input",
|
|
925
|
+
defaultValue: ""
|
|
926
|
+
};
|
|
927
|
+
return inputConfigProp ? { ...baseInputConfig, ...inputConfigProp } : baseInputConfig;
|
|
928
|
+
}, [type, providerConfig.inputs, formConfig.inputs, inputConfigProp]);
|
|
685
929
|
useEffect(() => {
|
|
686
930
|
if (shouldRegister) {
|
|
687
931
|
registerField(name);
|
|
@@ -699,13 +943,17 @@ function Field({
|
|
|
699
943
|
subscribesTo: fieldConfig.subscribesTo
|
|
700
944
|
});
|
|
701
945
|
const allSubscriptions = useMemo(() => {
|
|
702
|
-
return [
|
|
946
|
+
return [
|
|
947
|
+
.../* @__PURE__ */ new Set([...inferredSubscriptions, ...groupContext.subscriptions])
|
|
948
|
+
];
|
|
703
949
|
}, [inferredSubscriptions, groupContext.subscriptions]);
|
|
704
950
|
useSubscriptions(name, allSubscriptions);
|
|
705
951
|
const conditionResult = useConditions({
|
|
706
952
|
conditions: fieldConfig.conditions ?? [],
|
|
707
953
|
subscribesTo: fieldConfig.subscribesTo,
|
|
708
|
-
props: { name }
|
|
954
|
+
props: { name },
|
|
955
|
+
allFieldsConfig: config
|
|
956
|
+
// Pass all field configs for two-pass evaluation of disabled states
|
|
709
957
|
});
|
|
710
958
|
const setValueRef = useRef(methods.setValue);
|
|
711
959
|
setValueRef.current = methods.setValue;
|
|
@@ -757,15 +1005,22 @@ function Field({
|
|
|
757
1005
|
return conditionResult.visible ?? true;
|
|
758
1006
|
if (!groupContext.state.isVisible) return false;
|
|
759
1007
|
return true;
|
|
760
|
-
}, [
|
|
761
|
-
|
|
1008
|
+
}, [
|
|
1009
|
+
hiddenProp,
|
|
1010
|
+
fieldConfig.hidden,
|
|
1011
|
+
conditionResult,
|
|
1012
|
+
groupContext.state.isVisible
|
|
1013
|
+
]);
|
|
1014
|
+
const { providerSelectProps, formSelectProps, fieldSelectProps } = usePropsEvaluation({
|
|
762
1015
|
selectProps: fieldConfig.selectProps,
|
|
1016
|
+
formDefaultFieldProps: formConfig.selectDefaultFieldProps,
|
|
1017
|
+
providerDefaultFieldProps: providerConfig.selectDefaultFieldProps,
|
|
763
1018
|
subscribesTo: fieldConfig.subscribesTo,
|
|
764
1019
|
fieldName: name
|
|
765
1020
|
});
|
|
766
1021
|
const label = useMemo(() => {
|
|
767
|
-
return resolveLabel(name, fieldConfig,
|
|
768
|
-
}, [name, fieldConfig,
|
|
1022
|
+
return resolveLabel(name, fieldConfig, fieldSelectProps, restProps);
|
|
1023
|
+
}, [name, fieldConfig, fieldSelectProps, restProps]);
|
|
769
1024
|
const validationRules = useMemo(() => {
|
|
770
1025
|
return {
|
|
771
1026
|
...fieldConfig.rules,
|
|
@@ -818,9 +1073,15 @@ function Field({
|
|
|
818
1073
|
providerConfig.parsers
|
|
819
1074
|
);
|
|
820
1075
|
onChange(parsedValue);
|
|
821
|
-
changeField(name, parsedValue);
|
|
1076
|
+
changeField(name, parsedValue, inputConfig);
|
|
822
1077
|
},
|
|
823
|
-
[
|
|
1078
|
+
[
|
|
1079
|
+
inputConfig.parser,
|
|
1080
|
+
providerConfig.parsers,
|
|
1081
|
+
changeField,
|
|
1082
|
+
name,
|
|
1083
|
+
inputConfig
|
|
1084
|
+
]
|
|
824
1085
|
);
|
|
825
1086
|
if (!isVisible) {
|
|
826
1087
|
return null;
|
|
@@ -839,14 +1100,12 @@ function Field({
|
|
|
839
1100
|
);
|
|
840
1101
|
const finalProps = mergeFieldProps({
|
|
841
1102
|
providerDefaultFieldProps: providerConfig.defaultFieldProps,
|
|
842
|
-
providerSelectDefaultFieldProps:
|
|
843
|
-
// Evaluated at provider level if needed
|
|
1103
|
+
providerSelectDefaultFieldProps: providerSelectProps,
|
|
844
1104
|
formDefaultFieldProps: formConfig.defaultFieldProps,
|
|
845
|
-
formSelectDefaultFieldProps:
|
|
846
|
-
// Evaluated at form level if needed
|
|
1105
|
+
formSelectDefaultFieldProps: formSelectProps,
|
|
847
1106
|
inputProps: inputConfig.props,
|
|
848
1107
|
fieldConfigProps: fieldConfig.props,
|
|
849
|
-
selectProps:
|
|
1108
|
+
selectProps: fieldSelectProps,
|
|
850
1109
|
componentProps: restProps,
|
|
851
1110
|
coreProps: {
|
|
852
1111
|
name,
|
|
@@ -856,7 +1115,7 @@ function Field({
|
|
|
856
1115
|
[inputConfig.inputFieldProp ?? "value"]: formattedValue,
|
|
857
1116
|
onChange: handleChange(field.onChange),
|
|
858
1117
|
onBlur: field.onBlur,
|
|
859
|
-
|
|
1118
|
+
forwardRef: field.ref
|
|
860
1119
|
}
|
|
861
1120
|
});
|
|
862
1121
|
const Component = inputConfig.component;
|
|
@@ -1023,6 +1282,11 @@ function useFormState(options) {
|
|
|
1023
1282
|
return result;
|
|
1024
1283
|
}
|
|
1025
1284
|
|
|
1026
|
-
|
|
1285
|
+
// src/overlays.ts
|
|
1286
|
+
function defineInputs(inputs) {
|
|
1287
|
+
return inputs;
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
export { ConfigContext, Field, FieldGroup, Form, FormContext, FormalityProvider, GroupContext, UnusedFields, defineInputs, makeDeepProxyState, makeProxyState, useConditions, useConfigContext, useFormContext, useFormState, useGroupContext, useInferredInputs, usePropsEvaluation, useSubscriptions };
|
|
1027
1291
|
//# sourceMappingURL=index.js.map
|
|
1028
1292
|
//# sourceMappingURL=index.js.map
|