[SOLVED] How to manually close an overlay?

Issue

This Content is from Stack Overflow. Question asked by Mintee

I’m using NextUI Popover component and I want to manually close the Popover with a cancel button. I can open and close it with isOpen prop but the main problem is that I have it in a map loop, when I open one all the other opens. How can I solve this?

enter image description here

Popover JSX

  <Popover placement="top">
    <Popover.Trigger>
      <Button auto light>
        <DeleteDocumentIcon
          fill="#F31260"
          className="cursor-pointer"
          width={26}
          height={26}
        />
      </Button>
    </Popover.Trigger>
    <Popover.Content>
      <DeleteUser
        snip={snip}
        handleDelete={handleDelete}
      />
    </Popover.Content>
  </Popover>

Overlay buttons

<Grid>
  <Button size="sm" light>
    Cancel
  </Button>
</Grid>
<Grid>
  <Button
    size="sm"
    shadow
    color="error"
    onClick={() => handleDelete(user.id)}
  >
    Delete
  </Button>
</Grid>



Solution

Okay, so the problem is that you’re using a binary state (isOpen) to represent something with more than binary state– namely, the isOpen state of every snip.

There are a few ways to do this, but I personally like having the state be an object containing a mapping from index => boolean

const [allOpenStates, setAllOpenStates] = useState({});
// in this case, we'll have an object that maps from an
// index to a boolean
// the default state is closed, so a missing value in the map
// will get interpreted as false

{snipsData.map((snip, index) => (
 <div key={index} className="hoverable-item flex gap-2">

  //....

  <Popover placement="top" 
       isOpen={allOpenStates[index]} // it will be undefined if it hasn't ever been opened, which is fine, since undefined is falsey
       onOpenChange={nowOpen => setAllOpenStates(oldState => {...oldState, [index]: nowOpen} )}>

  //Pop.Trigger Button sets allOpenStates[index] = true
    <Popover.Trigger>
      <Button auto light>
        <DeleteDocumentIcon
          fill="#F31260"
          className="cursor-pointer"
          width={26}
          height={26}
        />
      </Button>
    </Popover.Trigger>
    <Popover.Content>
      <DeleteUser
        snip={snip}
        handleDelete={handleDelete}
        setIsOpen={setIsOpen}
      />
    </Popover.Content>
  </Popover>
 </div>
))}

I’m doing this without trying it myself, so some of the syntax may be off. But the general idea is that you need to keep track of the open state for each index, and use that individual index’s state for determining if the popover is open and how to handle open/close events.

Another solution would be using a Set of numbers (the index values), where Set membership indicates that the popover is open. Opening the popover would add that index to the set, and closing would remove from the set.This would require some ugly code though, since set operations are mutating.


This Question was asked in StackOverflow by Mintee and Answered by Nathan It is licensed under the terms of CC BY-SA 2.5. - CC BY-SA 3.0. - CC BY-SA 4.0.

people found this article helpful. What about you?