Search code examples
javascriptreactjswebreact-reduxprogressive-web-apps

How to close an open modal window when a new one opens?


I would like to know how to close a series of modal windows in React. What I want is that when there is one already open, the previous one will be closed.

enter image description here

export const Labels = ({ id, content, color }) => {
  const [changeColor, setChangeColor] = useState(false);

  const hanldeChange = () => { };

  return (
    <>
      <div className="px-3 border-2 border-blur-lg flex justify-between m-5 rounded-md">
        <div className="flex py-3">
          <BlockColor color={color} />
          <input
            type="text"
            id="txtLabel"
            name="txtLabel"
            className="text-base ring-white focus:ring-transparent mx-3 text-gray-700 outline-none"
            value={content}
            onChange={hanldeChange}
          />
        </div>
        <div className="flex py-3">
          <button className="mx-2">
            <img
              src={`./assets/Pantone.svg`}
              alt="New Label"
              onClick={() => setChangeColor(!changeColor)}
            />
          </button>
          <button className="mx-3">
            <img src={`./assets/Delete.svg`} alt="Delete" />
          </button>
          <ModalColor
            changeColorState={changeColor}
            setChangeColorState={setChangeColor}
          />
        </div>
      </div>
    </>
  );
};

It is a modal component that receives three properties, ID, content and color. This is dynamically generated and each "label" has an ID, content and color. When they press a button, it triggers another modal that presents the available colors. Every time they click on the button to change the color, the window opens and if they go to another label and press again a new one opens.

What I want to know is how to do so that I know when a modal window opens to select the color, and another modal window is open to select a color, it closes.

export const ModalColor = ({ changeColorState, setChangeColorState }) => {


  const [blockColor, setBlockColor] = useState("#409FFF");

  const handleColorChange = (e) => {
    setBlockColor(e.target.value);
    setChangeColorState(false);
  };

  return (
    <>
      {changeColorState && (
        <div className="container absolute bg-white -right-2 top-auto mt-7 shadow-lg border-2 w-64 m-5 rounded-md overflow-x-hidden">
          <p className="mx-2 text-gray-700 font-medium">Change Color</p>
          <div className="mx-3 grid grid-cols-5 gap-4 my-2 rounded-full">
            <input
              type="radio"
              className="cursor-pointer form-radio h-6 w-6 text-rojo"
              value="#FD7972"
              checked
              readOnly
              onClick={handleColorChange}
            />

            <input
              type="radio"
              value="#FE9F5E"
              checked
              readOnly
              onClick={handleColorChange}
              className="cursor-pointer form-radio h-6 w-6 text-naranja"
            />

            <input
              type="radio"
              value="#FFD454"
              checked
              readOnly
              onClick={handleColorChange}
              className="cursor-pointer form-radio h-6 w-6 text-amarillo"
            />

          </div>
          <p>{blockColor}</p>
        </div>
      )}
    </>
  );
};

Solution

  • You could have a listener that detects whenever you click outside of you color window and when you click outside you could set you changeColorState to false. So whenever you click outside of that window, it will close.

    You could implement the detect-outside-click function with the useRef Hook or you could also use a package like this one: https://www.npmjs.com/package/react-outside-click-handler

    With the package that I linked above it could look like this

    import OutsideClickHandler from 'react-outside-click-handler';
    
    export const ModalColor = ({ changeColorState, setChangeColorState }) => {
    const [blockColor, setBlockColor] = useState("#409FFF");
    
    const handleColorChange = (e) => {
         setBlockColor(e.target.value);
        setChangeColorState(false);
    };
    
    return (
        <>
         <OutsideClickHandler
           onOutsideClick={() => {
             setChangeColorState(false)
           }}
         >
            {changeColorState && (
                <div className="container absolute bg-white -right-2 top-auto mt-7 shadow-lg border-2 w-64 m-5 rounded-md overflow-x-hidden">
                    <p className="mx-2 text-gray-700 font-medium">Change Color</p>
                    <div className="mx-3 grid grid-cols-5 gap-4 my-2 rounded-full">
                        <input
                            type="radio"
                            className="cursor-pointer form-radio h-6 w-6 text-rojo"
                            value="#FD7972"
                            checked
                            readOnly
                            onClick={handleColorChange}
                        />
    
                        <input
                            type="radio"
                            value="#FE9F5E"
                            checked
                            readOnly
                            onClick={handleColorChange}
                            className="cursor-pointer form-radio h-6 w-6 text-naranja"
                        />
    
                        <input
                            type="radio"
                            value="#FFD454"
                            checked
                            readOnly
                            onClick={handleColorChange}
                            className="cursor-pointer form-radio h-6 w-6 text-amarillo"
                        />
    
                    </div>
                    <p>{blockColor}</p>
                </div>
            )}
           </OutsideClickHandler>
        </>
    );
    
    

    I am not sure if this is the best way to solve this problem, but it should at least work.