When a user drags the native CSS resize grip from inside a modal and releases the mouse over the overlay, the synthesised click event targets the common ancestor (the overlay) — fooling the existing e.target === e.currentTarget dismiss check. Audited every modal with `resize: both` and switched them to a shared mousedown→mouseup tracking pattern (new useOverlayDismiss hook) so dismiss only fires when both events land on the overlay. Modals fixed: TaskDetail, Settings, FileBrowser, GitHubImport, GitManager, ScheduledTasks, Scripts, WorkflowStepManager. (Terminal and AgentDetail were already fixed in 56e8e7324; PlanningModeModal already had the right pattern inline.) Also: in Settings → Authentication, the "Anthropic via Claude CLI" card now lives inside the Authenticated group when authenticated and the Available group otherwise, instead of floating at the top. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
39 lines
1.4 KiB
TypeScript
39 lines
1.4 KiB
TypeScript
import { useCallback, useRef } from "react";
|
|
|
|
/**
|
|
* Returns props for a modal-overlay element that dismisses only when a real
|
|
* overlay click happens — i.e. both mousedown AND mouseup land on the overlay
|
|
* itself.
|
|
*
|
|
* This avoids a subtle dismiss-during-resize bug: when a user drags the
|
|
* native CSS `resize: both` grip from inside a modal and releases the mouse
|
|
* over the overlay, the synthesised click event targets the common ancestor
|
|
* (the overlay). A naive `onClick` handler that checks `e.target === e.currentTarget`
|
|
* is fooled and closes the modal mid-resize.
|
|
*
|
|
* Spread the returned props on the overlay element. The inner modal element
|
|
* does NOT need to stopPropagation — mousedown on the modal sets the ref to
|
|
* `false`, so the overlay's mouseup handler bails.
|
|
*/
|
|
export function useOverlayDismiss(onClose: () => void): {
|
|
onMouseDown: (e: React.MouseEvent) => void;
|
|
onMouseUp: (e: React.MouseEvent) => void;
|
|
} {
|
|
const startedOnOverlayRef = useRef(false);
|
|
|
|
const onMouseDown = useCallback((e: React.MouseEvent) => {
|
|
startedOnOverlayRef.current = e.target === e.currentTarget;
|
|
}, []);
|
|
|
|
const onMouseUp = useCallback(
|
|
(e: React.MouseEvent) => {
|
|
const shouldClose = startedOnOverlayRef.current && e.target === e.currentTarget;
|
|
startedOnOverlayRef.current = false;
|
|
if (shouldClose) onClose();
|
|
},
|
|
[onClose],
|
|
);
|
|
|
|
return { onMouseDown, onMouseUp };
|
|
}
|