Skip to content

Commit 4d8ae83

Browse files
committed
Fix detecting click outside of element
1 parent 84d4fd9 commit 4d8ae83

2 files changed

Lines changed: 42 additions & 20 deletions

File tree

src/components/BorderedApp/BorderedAppMenu/BorderedAppMenu.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@ function BorderedAppMenu({ title, items }: BorderedAppMenuProps) {
2121
}, [elementRef]);
2222

2323
// Close the menu if an outside click occurs
24-
useDetectMouseDownOutside({ elementRef, onMouseDown: () => setOpen(false) });
24+
useDetectMouseDownOutside({
25+
elementRef,
26+
onMouseDown: () => setOpen(false),
27+
enabled: open,
28+
});
2529

2630
return (
2731
<div

src/hooks/useDetectMouseDownOutside.ts

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,45 +9,63 @@ interface UseDetectMouseDownOutsideProps<Element extends HTMLElement> {
99
elementRef: React.RefObject<Element>;
1010

1111
/**
12-
* The callback to be invoked when a click
13-
* has been detected outside
12+
* The callback to be invoked when a mouse down
13+
* has been detected outside the element
1414
*/
1515
onMouseDown: () => void;
16+
17+
enabled?: boolean;
1618
}
1719

1820
/**
19-
* Detects a click that has occurred outside of the specified element.
21+
* Detects a mouse down event that has occurred outside of the specified element.
22+
*
23+
* Note that the term "_outside_" refers to the DOM structure and not the
24+
* position that the element is displayed on screen. Any clicks on the specified
25+
* element of any of its children (or it's childrens childrens, recursively)
26+
* is considered _inside_. Everything else is considered _outside_
2027
*/
2128
function useDetectMouseDownOutside<Element extends HTMLElement>({
2229
elementRef,
2330
onMouseDown,
31+
enabled = true,
2432
}: UseDetectMouseDownOutsideProps<Element>) {
2533
useEffect(() => {
26-
function handler(e: MouseEvent) {
27-
// TODO: Fix this - doesnt work for fixed/absolute positioned items.
28-
// Instead of checking if it's inside/outside the rect,
29-
// we'll probs have to recursively check all children to see
30-
// if the child element is equal to the event target. If so,
31-
// the click is within, otherwise it's outside.
32-
const bounds = elementRef.current?.getBoundingClientRect();
33-
if (e.clientX < (bounds?.left ?? 0)) {
34-
return onMouseDown();
34+
if (!enabled) return;
35+
function handler(event: MouseEvent) {
36+
if (!elementRef.current || elementRef.current === event.target) {
37+
return;
3538
}
36-
if (e.clientX > (bounds?.left ?? 0) + (bounds?.width ?? 0)) {
37-
return onMouseDown();
39+
40+
console.log(elementRef.current);
41+
42+
let call = true;
43+
function process(children: HTMLCollection) {
44+
for (const child of children) {
45+
if (child === event.target) {
46+
call = false;
47+
return;
48+
}
49+
50+
if (child.children) {
51+
process(child.children);
52+
}
53+
}
3854
}
39-
if (e.clientY < (bounds?.top ?? 0)) {
40-
return onMouseDown();
55+
56+
if (elementRef.current.children) {
57+
process(elementRef.current.children);
4158
}
42-
if (e.clientY > (bounds?.top ?? 0) + (bounds?.height ?? 0)) {
43-
return onMouseDown();
59+
60+
if (call) {
61+
onMouseDown();
4462
}
4563
}
4664

4765
window.addEventListener("mousedown", handler);
4866

4967
return () => window.removeEventListener("mousedown", handler);
50-
});
68+
}, [elementRef, enabled, onMouseDown]);
5169
}
5270

5371
export default useDetectMouseDownOutside;

0 commit comments

Comments
 (0)