@pagamio/frontend-commons-lib 0.8.342 → 0.8.344

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.
@@ -22,6 +22,12 @@ interface DrawerContentProps {
22
22
  processingDependencyRef?: React.MutableRefObject<boolean>;
23
23
  pendingUpdatesRef?: React.MutableRefObject<Map<string, unknown>>;
24
24
  persistenceKey?: string;
25
+ /**
26
+ * When true, persisted form data is wiped whenever the drawer transitions
27
+ * from open → closed. Use for one-shot create/register flows where the
28
+ * next open should start blank.
29
+ */
30
+ clearOnClose?: boolean;
25
31
  /** Ref updated every render with current dirty state — read by FormEngineDrawer for X-button check. */
26
32
  isDirtyRef?: React.MutableRefObject<boolean>;
27
33
  /** Ref updated every render with handleCancel — call from outside (e.g. X button) to show the same unsaved-changes modal. */
@@ -1,28 +1,39 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useForm } from 'react-hook-form';
3
- import { useEffect, useRef, useState } from 'react';
3
+ import { useEffect, useMemo, useRef, useState } from 'react';
4
4
  import { Button, NotificationModal } from '../../components';
5
5
  import { useToast } from '../../context';
6
6
  import { FieldWrapper } from '../../form-engine';
7
7
  import { useFormPersistence } from '../../form-engine/hooks/useFormPersistence';
8
- const DrawerContent = ({ fields, onSubmit, isOpen, initialValues, submitButtonText = 'Submit', cancelButtonText = 'Cancel', showForm = true, showDrawerButtons = true, handleCloseDrawer, onFieldUpdate, onFieldChange, children, updateFieldOptions, updateFieldLabel, setFieldHidden, addField, updateFieldValidation, processInitialFieldUpdates, processingDependencyRef, pendingUpdatesRef, persistenceKey, isDirtyRef, cancelRef, }) => {
8
+ const DrawerContent = ({ fields, onSubmit, isOpen, initialValues, submitButtonText = 'Submit', cancelButtonText = 'Cancel', showForm = true, showDrawerButtons = true, handleCloseDrawer, onFieldUpdate, onFieldChange, children, updateFieldOptions, updateFieldLabel, setFieldHidden, addField, updateFieldValidation, processInitialFieldUpdates, processingDependencyRef, pendingUpdatesRef, persistenceKey, clearOnClose = false, isDirtyRef, cancelRef, }) => {
9
9
  const { saveFormData, restoreFormData, clearPersistedData, hasPersistedData } = useFormPersistence(persistenceKey);
10
10
  const { addToast } = useToast();
11
- // Determine initial values: initialValues take precedence over persisted data
12
- // This ensures that when editing different items, the form shows the correct item's data
11
+ // Build a `{ fieldName: '' }` baseline so every registered input starts
12
+ // controlled (with a defined string value). Without this, RHF defaults
13
+ // unspecified fields to `undefined`, which React flags as a "controlled
14
+ // → uncontrolled" transition the moment we reset, and Radix Select keeps
15
+ // showing its previously-selected display value.
16
+ const emptyFieldValues = useMemo(() => {
17
+ const acc = {};
18
+ for (const f of fields) {
19
+ acc[f.name] = '';
20
+ }
21
+ return acc;
22
+ }, [fields]);
23
+ // Determine initial values: initialValues take precedence over persisted data.
24
+ // For clearOnClose drawers we always start from the empty baseline so a
25
+ // stale persistence entry from a previous lifetime can't leak in.
13
26
  const getEffectiveInitialValues = () => {
14
- // If initialValues are provided, use them
15
27
  if (initialValues && Object.keys(initialValues).length > 0) {
16
- return initialValues;
28
+ return { ...emptyFieldValues, ...initialValues };
17
29
  }
18
- // Otherwise, check for persisted data
19
- if (persistenceKey && hasPersistedData()) {
30
+ if (!clearOnClose && persistenceKey && hasPersistedData()) {
20
31
  const persistedData = restoreFormData();
21
32
  if (persistedData) {
22
- return persistedData;
33
+ return { ...emptyFieldValues, ...persistedData };
23
34
  }
24
35
  }
25
- return initialValues || {};
36
+ return emptyFieldValues;
26
37
  };
27
38
  const { control, handleSubmit, watch, setValue, clearErrors, formState: { errors, isSubmitting }, reset, getValues, } = useForm({ mode: 'onBlur', defaultValues: getEffectiveInitialValues() });
28
39
  const [pendingUpdateCount, setPendingUpdateCount] = useState(0);
@@ -39,6 +50,27 @@ const DrawerContent = ({ fields, onSubmit, isOpen, initialValues, submitButtonTe
39
50
  mountInitialValuesRef.current = initialValues || {};
40
51
  }
41
52
  wasOpenRef.current = !!isOpen;
53
+ // Clear persisted data AND react-hook-form state on every open → close
54
+ // transition when the consumer opts in. Resetting RHF is critical: the
55
+ // watch()-driven save effect would otherwise re-populate persistence with
56
+ // the old values on the very next render (e.g. during the close animation).
57
+ const clearOnCloseRef = useRef(clearOnClose);
58
+ clearOnCloseRef.current = clearOnClose;
59
+ const clearPersistedDataRef = useRef(clearPersistedData);
60
+ clearPersistedDataRef.current = clearPersistedData;
61
+ const resetRef = useRef(reset);
62
+ resetRef.current = reset;
63
+ const emptyFieldValuesRef = useRef(emptyFieldValues);
64
+ emptyFieldValuesRef.current = emptyFieldValues;
65
+ const previousIsOpenRef = useRef(!!isOpen);
66
+ useEffect(() => {
67
+ const wasOpen = previousIsOpenRef.current;
68
+ previousIsOpenRef.current = !!isOpen;
69
+ if (wasOpen && !isOpen && clearOnCloseRef.current) {
70
+ clearPersistedDataRef.current();
71
+ resetRef.current(emptyFieldValuesRef.current);
72
+ }
73
+ }, [isOpen]);
42
74
  const password = watch('password');
43
75
  const allFields = watch();
44
76
  // Function to check if there are unsaved changes
@@ -59,16 +91,20 @@ const DrawerContent = ({ fields, onSubmit, isOpen, initialValues, submitButtonTe
59
91
  return true;
60
92
  });
61
93
  };
62
- // Save form data when fields change (for persistence)
94
+ // Save form data when fields change (for persistence). Guarded by `isOpen`
95
+ // so a closing drawer (whose form state hasn't been reset yet) doesn't
96
+ // re-write the values we just cleared via clearOnClose. Without this guard,
97
+ // the close-time clear races with the watch()-driven save and the save wins.
63
98
  useEffect(() => {
99
+ if (!isOpen)
100
+ return;
64
101
  if (persistenceKey && allFields && Object.keys(allFields).length > 0) {
65
- // Only save if there are actual field values (not just empty object)
66
102
  const hasValues = Object.values(allFields).some((value) => value !== undefined && value !== null && value !== '');
67
103
  if (hasValues) {
68
104
  saveFormData(allFields, true);
69
105
  }
70
106
  }
71
- }, [allFields, persistenceKey, saveFormData]);
107
+ }, [isOpen, allFields, persistenceKey, saveFormData]);
72
108
  // Process initial field updates once when drawer opens
73
109
  useEffect(() => {
74
110
  if (isOpen && !initialLoadPerformedRef.current && processInitialFieldUpdates) {
@@ -16,6 +16,14 @@ interface FormEngineDrawerProps {
16
16
  onFieldUpdate?: Record<string, DependentFieldUpdate[]>;
17
17
  onFieldChange?: (fieldName: string, value: unknown) => void;
18
18
  persistenceKey?: string;
19
+ /**
20
+ * When true, persisted form data is wiped whenever the drawer transitions
21
+ * from open → closed (cancel, X, ESC, programmatic close, successful submit).
22
+ * Default `false` preserves the original "resume a half-filled form" behaviour.
23
+ * Set to `true` for one-shot create/register flows where the next open should
24
+ * start blank — e.g. a "register device" or "deploy terminal" drawer.
25
+ */
26
+ clearOnClose?: boolean;
19
27
  }
20
28
  declare const FormEngineDrawer: React.FC<FormEngineDrawerProps>;
21
29
  export default FormEngineDrawer;
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useEffect, useRef, useState } from 'react';
3
3
  import BaseDrawer from './components/BaseDrawer';
4
4
  import DrawerContent from './components/DrawerContent';
5
- const FormEngineDrawer = ({ title, cancelButtonText = 'Cancel', submitButtonText = 'Submit', showForm = true, showDrawerButtons = true, isOpen, fields: initialFields, initialValues, children, marginTop = '0px', onClose, onSubmit, onFieldUpdate, onFieldChange, persistenceKey, }) => {
5
+ const FormEngineDrawer = ({ title, cancelButtonText = 'Cancel', submitButtonText = 'Submit', showForm = true, showDrawerButtons = true, isOpen, fields: initialFields, initialValues, children, marginTop = '0px', onClose, onSubmit, onFieldUpdate, onFieldChange, persistenceKey, clearOnClose = false, }) => {
6
6
  const [fields, setFields] = useState(initialFields);
7
7
  const initializedRef = useRef(false);
8
8
  const processingDependencyRef = useRef(false);
@@ -133,6 +133,6 @@ const FormEngineDrawer = ({ title, cancelButtonText = 'Cancel', submitButtonText
133
133
  processingDependencyRef.current = false;
134
134
  }
135
135
  };
136
- return (_jsx(BaseDrawer, { title: title, isOpen: isOpen, marginTop: marginTop, onClose: onClose, onCloseAttempt: () => cancelRef.current(), children: _jsx(DrawerContent, { isOpen: isOpen, fields: fields, onSubmit: onSubmit, handleCloseDrawer: onClose, initialValues: initialValues, showForm: showForm, showDrawerButtons: showDrawerButtons, cancelButtonText: cancelButtonText, submitButtonText: submitButtonText, onFieldUpdate: onFieldUpdate, onFieldChange: onFieldChange, updateFieldOptions: updateFieldOptions, updateFieldLabel: updateFieldLabel, setFieldHidden: setFieldHidden, addField: addField, updateFieldValidation: updateFieldValidation, processInitialFieldUpdates: processInitialFieldUpdates, processingDependencyRef: processingDependencyRef, pendingUpdatesRef: pendingUpdatesRef, persistenceKey: persistenceKey, cancelRef: cancelRef, children: children }, persistenceKey || 'drawer-content') }));
136
+ return (_jsx(BaseDrawer, { title: title, isOpen: isOpen, marginTop: marginTop, onClose: onClose, onCloseAttempt: () => cancelRef.current(), children: _jsx(DrawerContent, { isOpen: isOpen, fields: fields, onSubmit: onSubmit, handleCloseDrawer: onClose, initialValues: initialValues, showForm: showForm, showDrawerButtons: showDrawerButtons, cancelButtonText: cancelButtonText, submitButtonText: submitButtonText, onFieldUpdate: onFieldUpdate, onFieldChange: onFieldChange, updateFieldOptions: updateFieldOptions, updateFieldLabel: updateFieldLabel, setFieldHidden: setFieldHidden, addField: addField, updateFieldValidation: updateFieldValidation, processInitialFieldUpdates: processInitialFieldUpdates, processingDependencyRef: processingDependencyRef, pendingUpdatesRef: pendingUpdatesRef, persistenceKey: persistenceKey, clearOnClose: clearOnClose, cancelRef: cancelRef, children: children }, persistenceKey || 'drawer-content') }));
137
137
  };
138
138
  export default FormEngineDrawer;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pagamio/frontend-commons-lib",
3
3
  "description": "Pagamio library for Frontend reusable components like the form engine and table container",
4
- "version": "0.8.342",
4
+ "version": "0.8.344",
5
5
  "publishConfig": {
6
6
  "access": "public",
7
7
  "provenance": false