mirror of
https://github.com/docmost/docmost.git
synced 2026-05-07 06:23:06 +08:00
fix(base): prompt unsaved changes when discarding dirty rename
This commit is contained in:
@@ -50,9 +50,12 @@ export function PropertyMenuContent({
|
|||||||
const renameInputRef = useRef<HTMLInputElement>(null);
|
const renameInputRef = useRef<HTMLInputElement>(null);
|
||||||
const [optionsDirty, setOptionsDirty] = useState(false);
|
const [optionsDirty, setOptionsDirty] = useState(false);
|
||||||
const pendingActionRef = useRef<"back" | "close" | null>(null);
|
const pendingActionRef = useRef<"back" | "close" | null>(null);
|
||||||
|
const sourcePanelRef = useRef<"rename" | "options" | null>(null);
|
||||||
const [closeRequest] = useAtom(propertyMenuCloseRequestAtom) as unknown as [number];
|
const [closeRequest] = useAtom(propertyMenuCloseRequestAtom) as unknown as [number];
|
||||||
const closeRequestRef = useRef(closeRequest);
|
const closeRequestRef = useRef(closeRequest);
|
||||||
|
|
||||||
|
const renameDirty = renameValue !== property.name;
|
||||||
|
|
||||||
const updatePropertyMutation = useUpdatePropertyMutation();
|
const updatePropertyMutation = useUpdatePropertyMutation();
|
||||||
const deletePropertyMutation = useDeletePropertyMutation();
|
const deletePropertyMutation = useDeletePropertyMutation();
|
||||||
|
|
||||||
@@ -70,13 +73,21 @@ export function PropertyMenuContent({
|
|||||||
}
|
}
|
||||||
}, [panel]);
|
}, [panel]);
|
||||||
|
|
||||||
const handleOptionsDirtyChange = useCallback(
|
const handleOptionsDirtyChange = useCallback((dirty: boolean) => {
|
||||||
(dirty: boolean) => {
|
setOptionsDirty(dirty);
|
||||||
setOptionsDirty(dirty);
|
}, []);
|
||||||
onDirtyChange?.(dirty);
|
|
||||||
},
|
// Single dirty signal to the outside — reflects whichever panel is
|
||||||
[onDirtyChange],
|
// currently accumulating unsaved work. Keeps rename and options in
|
||||||
);
|
// lockstep with the `propertyMenuDirtyAtom` so the grid-container's
|
||||||
|
// outside-click handler and the header's ESC handler both prompt
|
||||||
|
// "Unsaved changes" consistently.
|
||||||
|
useEffect(() => {
|
||||||
|
const dirty =
|
||||||
|
(panel === "rename" && renameDirty) ||
|
||||||
|
(panel === "options" && optionsDirty);
|
||||||
|
onDirtyChange?.(dirty);
|
||||||
|
}, [panel, renameDirty, optionsDirty, onDirtyChange]);
|
||||||
|
|
||||||
const commitRename = useCallback(() => {
|
const commitRename = useCallback(() => {
|
||||||
const trimmed = renameValue.trim();
|
const trimmed = renameValue.trim();
|
||||||
@@ -94,6 +105,20 @@ export function PropertyMenuContent({
|
|||||||
onClose();
|
onClose();
|
||||||
}, [commitRename, onClose]);
|
}, [commitRename, onClose]);
|
||||||
|
|
||||||
|
const requestClose = useCallback(() => {
|
||||||
|
if (panel === "rename" && renameDirty) {
|
||||||
|
sourcePanelRef.current = "rename";
|
||||||
|
pendingActionRef.current = "close";
|
||||||
|
setPanel("confirmDiscard");
|
||||||
|
} else if (panel === "options" && optionsDirty) {
|
||||||
|
sourcePanelRef.current = "options";
|
||||||
|
pendingActionRef.current = "close";
|
||||||
|
setPanel("confirmDiscard");
|
||||||
|
} else {
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
}, [panel, renameDirty, optionsDirty, onClose]);
|
||||||
|
|
||||||
const handleRenameKeyDown = useCallback(
|
const handleRenameKeyDown = useCallback(
|
||||||
(e: React.KeyboardEvent) => {
|
(e: React.KeyboardEvent) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@@ -103,10 +128,10 @@ export function PropertyMenuContent({
|
|||||||
}
|
}
|
||||||
if (e.key === "Escape") {
|
if (e.key === "Escape") {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
onClose();
|
requestClose();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[handleRenameAndClose, onClose],
|
[handleRenameAndClose, requestClose],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleOptionsUpdate = useCallback(
|
const handleOptionsUpdate = useCallback(
|
||||||
@@ -131,6 +156,7 @@ export function PropertyMenuContent({
|
|||||||
|
|
||||||
const handleOptionsBack = useCallback(() => {
|
const handleOptionsBack = useCallback(() => {
|
||||||
if (optionsDirty) {
|
if (optionsDirty) {
|
||||||
|
sourcePanelRef.current = "options";
|
||||||
pendingActionRef.current = "back";
|
pendingActionRef.current = "back";
|
||||||
setPanel("confirmDiscard");
|
setPanel("confirmDiscard");
|
||||||
} else {
|
} else {
|
||||||
@@ -138,15 +164,6 @@ export function PropertyMenuContent({
|
|||||||
}
|
}
|
||||||
}, [optionsDirty]);
|
}, [optionsDirty]);
|
||||||
|
|
||||||
const requestClose = useCallback(() => {
|
|
||||||
if (panel === "options" && optionsDirty) {
|
|
||||||
pendingActionRef.current = "close";
|
|
||||||
setPanel("confirmDiscard");
|
|
||||||
} else {
|
|
||||||
onClose();
|
|
||||||
}
|
|
||||||
}, [panel, optionsDirty, onClose]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (closeRequest !== closeRequestRef.current) {
|
if (closeRequest !== closeRequestRef.current) {
|
||||||
closeRequestRef.current = closeRequest;
|
closeRequestRef.current = closeRequest;
|
||||||
@@ -158,19 +175,22 @@ export function PropertyMenuContent({
|
|||||||
|
|
||||||
const handleConfirmDiscard = useCallback(() => {
|
const handleConfirmDiscard = useCallback(() => {
|
||||||
setOptionsDirty(false);
|
setOptionsDirty(false);
|
||||||
onDirtyChange?.(false);
|
setRenameValue(property.name);
|
||||||
const action = pendingActionRef.current;
|
const action = pendingActionRef.current;
|
||||||
pendingActionRef.current = null;
|
pendingActionRef.current = null;
|
||||||
|
sourcePanelRef.current = null;
|
||||||
if (action === "back") {
|
if (action === "back") {
|
||||||
setPanel("main");
|
setPanel("main");
|
||||||
} else {
|
} else {
|
||||||
onClose();
|
onClose();
|
||||||
}
|
}
|
||||||
}, [onClose, onDirtyChange]);
|
}, [property.name, onClose]);
|
||||||
|
|
||||||
const handleCancelDiscard = useCallback(() => {
|
const handleCancelDiscard = useCallback(() => {
|
||||||
|
const source = sourcePanelRef.current ?? "options";
|
||||||
pendingActionRef.current = null;
|
pendingActionRef.current = null;
|
||||||
setPanel("options");
|
sourcePanelRef.current = null;
|
||||||
|
setPanel(source);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -197,7 +217,7 @@ export function PropertyMenuContent({
|
|||||||
/>
|
/>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Group justify="flex-end" gap="xs">
|
<Group justify="flex-end" gap="xs">
|
||||||
<Button variant="default" size="xs" onClick={onClose}>
|
<Button variant="default" size="xs" onClick={requestClose}>
|
||||||
{t("Cancel")}
|
{t("Cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
Reference in New Issue
Block a user