wellyshen / react-cool-onclickoutside

😎 🖱 React hook to listen for clicks outside of the component(s).
https://react-cool-onclickoutside.netlify.app
MIT License
550 stars 13 forks source link

Support detached windows via portals #645

Open mdsilberberg opened 3 years ago

mdsilberberg commented 3 years ago

It would be great to support the case of having our component rendered inside a portal located in a new browser window.

With react portals, we can create a new browser window and render our components there. For example using react-new-window library (https://github.com/rmariuzzo/react-new-window). I am using this in my application to being able of split my application in several small separated browser windows.

The problem here is that now the react-cool-onclickoutside library is always attaching the event listeners to the document, and when we are creating a new window and rendering our components there via portals, this premise is not valid. So, in this new scenario, if you click outside the target component but inside your new window, that is not under the main document the listeners are not triggered. I can try to provide a simple demo example to show you the use case if necessary.

What I propose is to have an optional parameter to pass a reference of the element that you want to use as root to attach the events (customRootRef or something like that) to be used instead of document. This way it would be possible to pass a reference to the higher level component rendered in this new window. So if I click outside the target component but inside the window where this component is rendered, the react-cool-clickoutside ill accomplish its purpose.

wellyshen commented 3 years ago

@mdsilberberg Sorry for the late reply. I'm wondering how many users need this feature? If this feature only for small-group users, is it cost a large implementing effort?

mdsilberberg commented 3 years ago

Hi @wellyshen. We are developing an app for a reduced group of users. No, I think its a very short effort to implement that. Just adding an extra parameter to pass a reference where to attach the library events detection instead of using the document.

wellyshen commented 3 years ago

@mdsilberberg Would you like to send me a PR for this feature?

Lariveg commented 3 years ago

Yes, the pr would also help me.

bradydowling commented 2 years ago

This kind of situation also applies for iframes, which I'm guessing will be more widely applicable than the case mentioned here. @mdsilberberg any luck on a PR for this?

Update: Solution in https://github.com/wellyshen/react-cool-onclickoutside/issues/645#issuecomment-983759482

bradydowling commented 2 years ago

@Lariveg I actually think this is what this line of code is for in use-onclickoutside.

I believe it would allow you to pass another item (which is not the current document) to which an event listener is attached. Once that item is clicked or touched, the outside click event will fire. If I get this working for my case locally then I'd be happy to make a PR for this.

Lariveg commented 2 years ago

@Lariveg I actually think this is what this line of code is for in use-onclickoutside.

I believe it would allow you to pass another item (which is not the current document) to which an event listener is attached. Once that item is clicked or touched, the outside click event will fire. If I get this working for my case locally then I'd be happy to make a PR for this.

I actually used this method to go around it. // Ignore if we're clicking inside the menu if (event.target.closest('[role="menu"]') instanceof Element) { return; } https://github.com/sparksuite/react-accessible-dropdown-menu-hook/blob/master/src/use-dropdown-menu.ts#L82-L85

bradydowling commented 2 years ago

@Lariveg your approach doesn't seem relevant to this issue. The code you referenced is not using this library and is explained well by the name of the function it lives in: handleEveryClick. This library only handles clicks outside a specific element, which it does using the following chunk of code:

(el) => !el.contains(e.target)

Basically, given an element to handle clicks outside of (el), this is checking if the element that was actually clicked (e.target) is within the original element (el`).

bradydowling commented 2 years ago

Ah, nevermind for my use case as well! Mine is handled by this line of code:

if (activeElement?.tagName === "IFRAME"

You just need to make sure to pass nothing or false as detectIFrame Options item.