nextui icon indicating copy to clipboard operation
nextui copied to clipboard

[Feature Request] NextUI V2, When user opens dropdown/popup, close the another dropdown/popup

Open alibabayev0 opened this issue 1 year ago • 5 comments

Is your feature request related to a problem? Please describe.

Yes, when a user opens a modal or popup while another modal/popup is already open, it can create confusion or lead to visual clutter. Multiple overlays can be distracting, and the user might not know which modal to focus on. It can also lead to potential UI glitches, especially when z-index values or other styles conflict between multiple modals.

Describe the solution you'd like

I'd like the nextui dropdown/popupover components to have built-in functionality that ensures only one dropdown or popup is visible at a given time for specific key or same labeled.

Can be resolved with: Context || DOM Events etc

Describe alternatives you've considered

.

Screenshots or Videos

No response

alibabayev0 avatar Aug 05 '23 19:08 alibabayev0

Hey @alibabayev0 could you send a repo/codesandbox/stackblitz ?, I couldn't replicate this issue, on the other hand, we rely on @react-aria overlays implementations which already has a context to control which modals/popovers are visible https://github.com/adobe/react-spectrum/blob/main/packages/%40react-aria/overlays/src/useOverlay.ts#L79-L86

jrgarciadev avatar Aug 05 '23 21:08 jrgarciadev

Hey, @jrgarciadev, check this out: https://codesandbox.io/p/sandbox/eager-fog-l9smkq. In the simplified version, I noticed we need to place the button within the Dropdown trigger, or it won't work correctly. I believe you've already implemented this, but just in case, it might be good to handle 'div' and other view types as well.

alibabayev0 avatar Aug 05 '23 22:08 alibabayev0

I found shouldCloseOnBlur in the docs, which seems not to work - but doesnt this describe your wanted behaviour? @alibabayev0

ynn1k avatar Aug 18 '23 11:08 ynn1k

I also tried out using shouldCloseOnBlur and it does not work as I originally expected. However, after reviewing I remembered that focus has to mainly do with input elements. You can see an example from Mozilla here.

I did look into the code and it looks like their using Adobe's React Spectrum, specifically the useOverlay. In the React Spectrum's issue page a fix to this issue has been discussed here. But it appears there is no solution currently.

But I currently do not see away to make the click away from a dropdown also be an event to activate a new dropdown. If anyone knows a fix let me know.

@alibabayev0 I was unable to replicate the effect where you when you click away, it activates the other popup. How were you able to do this?

SellersEvan avatar Nov 09 '23 23:11 SellersEvan

Here is a solution I developed. Honestly, not sure why it works but it does.

import { Dropdown as NativeDropdown, DropdownProps } from "@nextui-org/react";
import React from "react";
import { useState } from "react";

export function Dropdown(props:DropdownProps) {
    const [visible, setVisible] = useState(false);
    return (
        <NativeDropdown
            {...props}
            isOpen={visible}
            onOpenChange={(isOpen) => setVisible(isOpen)}
            shouldCloseOnInteractOutside={() => {
                setVisible(false);
                return false;
            }}
        />
    );
}

SellersEvan avatar Nov 10 '23 00:11 SellersEvan

I agree that this part of the API is extremely counter-intuitive.

This is a more generic hook version I use to fix it:

const fixProps = usePopoverFix();

 <Popover
            {...fixProps}
            // other props and children etc.

defined as:

import { useEffect, useMemo, useState } from "react";

export function usePopoverFix() {
  const [popoverVisible, setPopoverVisible] = useState(false);

  const shouldCloseOnInteractOutside = useMemo(() => {
    return () => {
      setPopoverVisible(false);
      return false;
    };
  }, [setPopoverVisible]);

  useEffect(() => {
    if (!popoverVisible) {
      return;
    }

    function keyDownHandler(e: KeyboardEvent) {
      if (popoverVisible && e.key === "Escape") {
        e.preventDefault();
        setPopoverVisible(false);
      }
    }

    document.addEventListener("keydown", keyDownHandler);

    return () => {
      document.removeEventListener("keydown", keyDownHandler);
    };
  }, [popoverVisible, setPopoverVisible]);

  return {
    isOpen: popoverVisible,
    onOpenChange: setPopoverVisible,
    isKeyboardDismissDisabled: false,
    shouldCloseOnInteractOutside,
  };
}

EDITED: I added an escape key listener, because that doesn't seem to be working correctly either.

buchananwill avatar May 17 '24 11:05 buchananwill