@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 CHANGED
@@ -181,6 +181,8 @@ function Form({
181
181
  const pendingChangedFields = react.useRef(/* @__PURE__ */ new Set());
182
182
  const pendingAffectedFields = react.useRef(/* @__PURE__ */ new Set());
183
183
  const executionVersionRef = react.useRef(0);
184
+ const fieldDebouncersRef = react.useRef(/* @__PURE__ */ new Map());
185
+ const getOrCreateDebouncedRef = react.useRef();
184
186
  const registerField = react.useCallback((name) => {
185
187
  fieldRegistry.current.add(name);
186
188
  setRegisteredFields(new Set(fieldRegistry.current));
@@ -281,8 +283,11 @@ function Form({
281
283
  for (const field of affected) {
282
284
  pendingAffectedFields.current.add(field);
283
285
  }
284
- if (inputConfig?.debounce === false) {
286
+ const fieldDebounce = inputConfig?.debounce;
287
+ if (fieldDebounce === false) {
285
288
  executeAutoSaveRef.current?.();
289
+ } else if (typeof fieldDebounce === "number") {
290
+ getOrCreateDebouncedRef.current?.(fieldDebounce)();
286
291
  } else {
287
292
  debouncedSubmitRef.current?.();
288
293
  }
@@ -423,53 +428,67 @@ function Form({
423
428
  return;
424
429
  }
425
430
  }
426
- const formState = methods.formState;
427
- if (Object.keys(formState.errors).length > 0) {
428
- return;
429
- }
430
431
  const values = methods.getValues();
431
432
  await handleSubmit(values);
432
433
  }, [methods, handleSubmit, waitForFieldValidation]);
433
434
  executeAutoSaveRef.current = executeAutoSave;
435
+ const getOrCreateDebounced = react.useCallback(
436
+ (ms) => {
437
+ const cached = fieldDebouncersRef.current.get(ms);
438
+ if (cached) return cached;
439
+ const debouncedFn = lodashEs.debounce(() => {
440
+ executeAutoSaveRef.current?.();
441
+ }, ms);
442
+ const fn = Object.assign(debouncedFn, {
443
+ pending: () => false
444
+ // lodash debounce tracks pending internally
445
+ });
446
+ fieldDebouncersRef.current.set(ms, fn);
447
+ return fn;
448
+ },
449
+ []
450
+ );
451
+ getOrCreateDebouncedRef.current = getOrCreateDebounced;
434
452
  react.useEffect(() => {
453
+ return () => {
454
+ fieldDebouncersRef.current.forEach((fn) => fn.cancel());
455
+ fieldDebouncersRef.current.clear();
456
+ };
457
+ }, [getOrCreateDebounced]);
458
+ const debouncedSubmit = react.useMemo(() => {
435
459
  if (debounceMs === false) {
436
- const immediateFn = Object.assign(
460
+ return Object.assign(
437
461
  () => {
438
- executeAutoSave();
462
+ executeAutoSaveRef.current?.();
439
463
  },
440
464
  {
441
465
  cancel: () => {
442
466
  },
443
467
  // No-op for immediate function
444
- flush: () => executeAutoSave(),
468
+ flush: () => executeAutoSaveRef.current?.(),
445
469
  // Execute immediately on flush
446
470
  pending: () => false
447
471
  // Never pending when immediate
448
472
  }
449
473
  );
450
- debouncedSubmitRef.current = immediateFn;
451
- return () => {
452
- };
453
474
  }
454
475
  const debouncedFn = lodashEs.debounce(() => {
455
- executeAutoSave();
476
+ executeAutoSaveRef.current?.();
456
477
  }, debounceMs);
457
- const fn = Object.assign(debouncedFn, {
478
+ return Object.assign(debouncedFn, {
458
479
  pending: () => false
459
480
  // lodash debounce handles this internally
460
481
  });
461
- debouncedSubmitRef.current = fn;
482
+ }, [debounceMs]);
483
+ debouncedSubmitRef.current = debouncedSubmit;
484
+ react.useEffect(() => {
462
485
  return () => {
463
- debouncedFn.cancel();
486
+ debouncedSubmit.cancel();
464
487
  };
465
- }, [executeAutoSave, debounceMs]);
488
+ }, [debouncedSubmit]);
466
489
  const submitImmediate = react.useCallback(() => {
467
- if (debouncedSubmitRef.current?.flush) {
468
- debouncedSubmitRef.current.flush();
469
- } else {
470
- executeAutoSave();
471
- }
472
- }, [executeAutoSave]);
490
+ debouncedSubmitRef.current?.flush();
491
+ }, []);
473
492
  const unusedFields = react.useMemo(() => {
474
493
  const allFields = Object.keys(config);
475
494
  return allFields.filter((name) => !registeredFields.has(name));
@@ -508,7 +527,7 @@ function Form({
508
527
  setFieldValidating,
509
528
  getFormState,
510
529
  onSubmit,
511
- debouncedSubmit: debouncedSubmitRef.current,
530
+ debouncedSubmit,
512
531
  submitImmediate,
513
532
  unusedFields,
514
533
  methods
@@ -527,6 +546,7 @@ function Form({
527
546
  setFieldValidating,
528
547
  getFormState,
529
548
  onSubmit,
549
+ debouncedSubmit,
530
550
  submitImmediate,
531
551
  unusedFields,
532
552
  methods
@@ -562,11 +582,18 @@ function useInferredInputs(options) {
562
582
  selectProps,
563
583
  formDefaultFieldProps,
564
584
  providerDefaultFieldProps,
565
- conditions = [],
566
- subscribesTo = []
585
+ conditions,
586
+ subscribesTo
567
587
  } = options;
588
+ const signature = JSON.stringify({
589
+ selectProps,
590
+ formDefaultFieldProps,
591
+ providerDefaultFieldProps,
592
+ conditions,
593
+ subscribesTo
594
+ });
568
595
  return react.useMemo(() => {
569
- const inferred = [...subscribesTo];
596
+ const inferred = [...subscribesTo ?? []];
570
597
  if (providerDefaultFieldProps) {
571
598
  inferred.push(...core.inferFieldsFromDescriptor(providerDefaultFieldProps));
572
599
  }
@@ -576,17 +603,11 @@ function useInferredInputs(options) {
576
603
  if (selectProps) {
577
604
  inferred.push(...core.inferFieldsFromDescriptor(selectProps));
578
605
  }
579
- if (conditions.length > 0) {
606
+ if (conditions && conditions.length > 0) {
580
607
  inferred.push(...core.inferFieldsFromConditions(conditions));
581
608
  }
582
609
  return [...new Set(inferred)];
583
- }, [
584
- providerDefaultFieldProps,
585
- formDefaultFieldProps,
586
- selectProps,
587
- conditions,
588
- subscribesTo
589
- ]);
610
+ }, [signature]);
590
611
  }
591
612
 
592
613
  // src/hooks/useConditions.ts
@@ -1121,17 +1142,29 @@ function Field({
1121
1142
  }
1122
1143
  });
1123
1144
  const Component = inputConfig.component;
1145
+ const isHostComponent = typeof inputConfig.component === "string";
1124
1146
  const template = inputConfig.template ?? providerConfig.inputTemplates[type] ?? providerConfig.defaultInputTemplate;
1125
1147
  const TemplateComponent = template;
1126
- const renderedField = TemplateComponent ? /* @__PURE__ */ jsxRuntime.jsx(
1127
- TemplateComponent,
1128
- {
1129
- Field: Component,
1130
- fieldProps: finalProps,
1131
- fieldState,
1132
- formState
1133
- }
1134
- ) : /* @__PURE__ */ jsxRuntime.jsx(Component, { ...finalProps });
1148
+ let renderedField;
1149
+ if (TemplateComponent) {
1150
+ renderedField = /* @__PURE__ */ jsxRuntime.jsx(
1151
+ TemplateComponent,
1152
+ {
1153
+ Field: Component,
1154
+ fieldProps: finalProps,
1155
+ fieldState,
1156
+ formState
1157
+ }
1158
+ );
1159
+ } else if (isHostComponent) {
1160
+ const { forwardRef: hostRef, ...restHostProps } = finalProps;
1161
+ renderedField = react.createElement(inputConfig.component, {
1162
+ ...restHostProps,
1163
+ ref: hostRef
1164
+ });
1165
+ } else {
1166
+ renderedField = /* @__PURE__ */ jsxRuntime.jsx(Component, { ...finalProps });
1167
+ }
1135
1168
  if (typeof children === "function") {
1136
1169
  const result = children({
1137
1170
  fieldState,