Fixed callback firing on clickoutside but mousedown inside. (#2434)
Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -158,7 +158,7 @@ export const Modal = ({
|
|||||||
useListenClickOutside({
|
useListenClickOutside({
|
||||||
refs: [modalRef],
|
refs: [modalRef],
|
||||||
callback: () => onClose?.(),
|
callback: () => onClose?.(),
|
||||||
mode: ClickOutsideMode.absolute,
|
mode: ClickOutsideMode.comparePixels,
|
||||||
});
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
|||||||
@ -56,7 +56,7 @@ export const RightDrawer = () => {
|
|||||||
useListenClickOutside({
|
useListenClickOutside({
|
||||||
refs: [rightDrawerRef],
|
refs: [rightDrawerRef],
|
||||||
callback: () => closeRightDrawer(),
|
callback: () => closeRightDrawer(),
|
||||||
mode: ClickOutsideMode.absolute,
|
mode: ClickOutsideMode.comparePixels,
|
||||||
});
|
});
|
||||||
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
export enum ClickOutsideMode {
|
export enum ClickOutsideMode {
|
||||||
absolute = 'absolute',
|
comparePixels = 'comparePixels',
|
||||||
dom = 'dom',
|
compareHTMLRef = 'compareHTMLRef',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useListenClickOutside = <T extends Element>({
|
export const useListenClickOutside = <T extends Element>({
|
||||||
refs,
|
refs,
|
||||||
callback,
|
callback,
|
||||||
mode = ClickOutsideMode.dom,
|
mode = ClickOutsideMode.compareHTMLRef,
|
||||||
enabled = true,
|
enabled = true,
|
||||||
}: {
|
}: {
|
||||||
refs: Array<React.RefObject<T>>;
|
refs: Array<React.RefObject<T>>;
|
||||||
@ -16,19 +16,19 @@ export const useListenClickOutside = <T extends Element>({
|
|||||||
mode?: ClickOutsideMode;
|
mode?: ClickOutsideMode;
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
|
const [isMouseDownInside, setIsMouseDownInside] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleClickOutside = (event: MouseEvent | TouchEvent) => {
|
const handleMouseDown = (event: MouseEvent | TouchEvent) => {
|
||||||
if (mode === ClickOutsideMode.dom) {
|
if (mode === ClickOutsideMode.compareHTMLRef) {
|
||||||
const clickedOnAtLeastOneRef = refs
|
const clickedOnAtLeastOneRef = refs
|
||||||
.filter((ref) => !!ref.current)
|
.filter((ref) => !!ref.current)
|
||||||
.some((ref) => ref.current?.contains(event.target as Node));
|
.some((ref) => ref.current?.contains(event.target as Node));
|
||||||
|
|
||||||
if (!clickedOnAtLeastOneRef) {
|
setIsMouseDownInside(clickedOnAtLeastOneRef);
|
||||||
callback(event);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode === ClickOutsideMode.absolute) {
|
if (mode === ClickOutsideMode.comparePixels) {
|
||||||
const clickedOnAtLeastOneRef = refs
|
const clickedOnAtLeastOneRef = refs
|
||||||
.filter((ref) => !!ref.current)
|
.filter((ref) => !!ref.current)
|
||||||
.some((ref) => {
|
.some((ref) => {
|
||||||
@ -57,28 +57,86 @@ export const useListenClickOutside = <T extends Element>({
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
if (!clickedOnAtLeastOneRef) {
|
|
||||||
|
setIsMouseDownInside(clickedOnAtLeastOneRef);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClickOutside = (event: MouseEvent | TouchEvent) => {
|
||||||
|
if (mode === ClickOutsideMode.compareHTMLRef) {
|
||||||
|
const clickedOnAtLeastOneRef = refs
|
||||||
|
.filter((ref) => !!ref.current)
|
||||||
|
.some((ref) => ref.current?.contains(event.target as Node));
|
||||||
|
|
||||||
|
if (!clickedOnAtLeastOneRef && !isMouseDownInside) {
|
||||||
|
callback(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode === ClickOutsideMode.comparePixels) {
|
||||||
|
const clickedOnAtLeastOneRef = refs
|
||||||
|
.filter((ref) => !!ref.current)
|
||||||
|
.some((ref) => {
|
||||||
|
if (!ref.current) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { x, y, width, height } = ref.current.getBoundingClientRect();
|
||||||
|
|
||||||
|
const clientX =
|
||||||
|
'clientX' in event
|
||||||
|
? event.clientX
|
||||||
|
: event.changedTouches[0].clientX;
|
||||||
|
const clientY =
|
||||||
|
'clientY' in event
|
||||||
|
? event.clientY
|
||||||
|
: event.changedTouches[0].clientY;
|
||||||
|
|
||||||
|
if (
|
||||||
|
clientX < x ||
|
||||||
|
clientX > x + width ||
|
||||||
|
clientY < y ||
|
||||||
|
clientY > y + height
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!clickedOnAtLeastOneRef && !isMouseDownInside) {
|
||||||
callback(event);
|
callback(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
|
document.addEventListener('mousedown', handleMouseDown, {
|
||||||
|
capture: true,
|
||||||
|
});
|
||||||
document.addEventListener('click', handleClickOutside, { capture: true });
|
document.addEventListener('click', handleClickOutside, { capture: true });
|
||||||
|
document.addEventListener('touchstart', handleMouseDown, {
|
||||||
|
capture: true,
|
||||||
|
});
|
||||||
document.addEventListener('touchend', handleClickOutside, {
|
document.addEventListener('touchend', handleClickOutside, {
|
||||||
capture: true,
|
capture: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
document.removeEventListener('mousedown', handleMouseDown, {
|
||||||
|
capture: true,
|
||||||
|
});
|
||||||
document.removeEventListener('click', handleClickOutside, {
|
document.removeEventListener('click', handleClickOutside, {
|
||||||
capture: true,
|
capture: true,
|
||||||
});
|
});
|
||||||
|
document.removeEventListener('touchstart', handleMouseDown, {
|
||||||
|
capture: true,
|
||||||
|
});
|
||||||
document.removeEventListener('touchend', handleClickOutside, {
|
document.removeEventListener('touchend', handleClickOutside, {
|
||||||
capture: true,
|
capture: true,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}, [refs, callback, mode, enabled]);
|
}, [refs, callback, mode, enabled, isMouseDownInside]);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useListenClickOutsideByClassName = ({
|
export const useListenClickOutsideByClassName = ({
|
||||||
|
|||||||
Reference in New Issue
Block a user