From 2efd1c6df42562218db70fe7fbe775f79818e9d3 Mon Sep 17 00:00:00 2001 From: BOHA Date: Sat, 6 Jun 2026 22:11:58 +0200 Subject: [PATCH] fix(mui): freeze loading state + button label during dialog close (Modal + ConfirmDialog) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On save/delete success the parent flips loading false a beat before the dialog finishes its fade-out, so the button flashed back to 'Uložit změny'/'Smazat'. Both dialogs now freeze the loading flag (and the label props) while open, so the closing dialog keeps showing 'Ukládám…'/'Pracuji…'. Same derive-state-from-props pattern as the title freeze. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/admin/ui/ConfirmDialog.tsx | 32 +++++++++++++++++++------------- src/admin/ui/Modal.tsx | 19 ++++++++++++------- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/src/admin/ui/ConfirmDialog.tsx b/src/admin/ui/ConfirmDialog.tsx index 0bd207a..a3f0ec7 100644 --- a/src/admin/ui/ConfirmDialog.tsx +++ b/src/admin/ui/ConfirmDialog.tsx @@ -32,21 +32,27 @@ export default function ConfirmDialog({ loading = false, }: ConfirmDialogProps) { useDialogScrollLock(isOpen); - // Freeze the shown title/message while closing, so the fade-out transition - // never flashes empty when the caller clears its source data (e.g. sets the - // selected row to null) at the same instant it closes the dialog. This is - // React's "adjust state from props during render" pattern: update only while - // open, keep the last values once closed. (Self-contained — callers can clear - // their data on close however they like.) - const [shown, setShown] = useState({ title, message }); - if (isOpen && (shown.title !== title || shown.message !== message)) { - setShown({ title, message }); + // Freeze content + label + loading while open, so neither the message NOR the + // confirm button label flips during the close fade-out (e.g. on confirm, + // `loading` goes false a beat before the dialog finishes closing — without + // this the button would flash back to "Smazat"/confirmText). React + // "adjust state from props during render": update only while open. Also keeps + // content from flashing empty when the caller clears its row on close. + const [shown, setShown] = useState({ title, message, confirmText, loading }); + if ( + isOpen && + (shown.title !== title || + shown.message !== message || + shown.confirmText !== confirmText || + shown.loading !== loading) + ) { + setShown({ title, message, confirmText, loading }); } return ( @@ -55,16 +61,16 @@ export default function ConfirmDialog({ {shown.message} - + {cancelText} - {loading ? "Pracuji…" : confirmText} + {shown.loading ? "Pracuji…" : shown.confirmText} diff --git a/src/admin/ui/Modal.tsx b/src/admin/ui/Modal.tsx index 7b55fb0..9e1f6a7 100644 --- a/src/admin/ui/Modal.tsx +++ b/src/admin/ui/Modal.tsx @@ -39,20 +39,25 @@ export default function Modal({ // Freeze the shown title/subtitle/submitText while closing, so the fade-out // never flashes the "create" variant when the caller clears `editingUser` on // close (React derive-state-from-props: update only while open). - const [shown, setShown] = useState({ title, subtitle, submitText }); + // Freeze label-affecting props (incl. `loading`) while open, so neither the + // title/submitText NOR the loading state flips during the close fade-out + // (e.g. on save, `loading` goes false a beat before the dialog finishes + // closing — without this the button would flash back to "Uložit změny"). + const [shown, setShown] = useState({ title, subtitle, submitText, loading }); if ( isOpen && (shown.title !== title || shown.subtitle !== subtitle || - shown.submitText !== submitText) + shown.submitText !== submitText || + shown.loading !== loading) ) { - setShown({ title, subtitle, submitText }); + setShown({ title, subtitle, submitText, loading }); } return ( {children} - + {cancelText} - {loading ? "Ukládám…" : shown.submitText} + {shown.loading ? "Ukládám…" : shown.submitText}