skip to Main Content

I am attempting to create a popup modal by pushing the action button in the lower right corner. I want the modal to slide upwards and the background behind the modal to blur. Right now, the modal just appears when the button is pushed and closes again. It’s nice but I want the background blurred and the menu to slide upwards smoothly. If possible, I’d like to have the button turn white when modal is open. Here is the code:

import React, { useState } from "react";
import ActionButton from "../../components/ActionButton";
import AddEventActionCard from "../../components/AddEventActionCard";

import AppHeader from "../../components/AppHeader";
import FooterNav from "../../components/FooterNav";
import "../Upcoming_Events/UpcomingEvents.css";

const UpcomingEvents = () => {
  const [click, setClick] = useState(false);
  const handleClick = () => setClick(!click);

  return (
    <div>
      <AppHeader />
      <div className="upcoming-container">
        <div className="upcoming-card-container">
          <div className="upcoming-card-top-header">Upcoming Events</div>
          <div className="upcoming-card-top-events-scroll-container"></div>
        </div>
        {click ? <AddEventActionCard /> : ""}
        <div onClick={handleClick}>
          <ActionButton />
        </div>
      </div>
      <FooterNav />
    </div>
  );
};

export default UpcomingEvents;

I’d love some assistance! Thank you so much!

2

Answers


  1. To achieve the desired functionality of a blurred background and a smoothly sliding modal, you can use CSS and React animations.

    So let’s make some changes to your code in order to archive that.

    UpcomingEvents Component

    import React, { useState } from "react";
    import ActionButton from "../../components/ActionButton";
    import AddEventActionCard from "../../components/AddEventActionCard";
    
    import AppHeader from "../../components/AppHeader";
    import FooterNav from "../../components/FooterNav";
    import "../Upcoming_Events/UpcomingEvents.css";
    
    const UpcomingEvents = () => {
      const [click, setClick] = useState(false);
      const handleClick = () => setClick(!click);
    
      return (
        <div>
          <AppHeader />
          <div className={`upcoming-container ${click ? "blurred" : ""}`}>
            <div className="upcoming-card-container">
              <div className="upcoming-card-top-header">Upcoming Events</div>
              <div className="upcoming-card-top-events-scroll-container"></div>
            </div>
            <div className={`modal ${click ? "open" : ""}`}>
              {click ? <AddEventActionCard /> : ""}
            </div>
            <div onClick={handleClick}>
              <ActionButton active={click} />
            </div>
          </div>
          <FooterNav />
        </div>
      );
    };
    
    export default UpcomingEvents;
    

    CSS

    .upcoming-container {
      position: relative;
    }
    
    .upcoming-container.blurred {
      filter: blur(5px);
    }
    
    .modal {
      position: fixed;
      bottom: -100%;
      left: 0;
      width: 100%;
      height: 100%;
      background-color: white;
      box-shadow: 0 -2px 4px rgba(0, 0, 0, 0.2);
      transition: bottom 0.3s ease-in-out;
    }
    
    .modal.open {
      bottom: 0;
    }
    

    ActionButton Component

    import React from "react";
    
    const ActionButton = ({ active, onClick }) => {
      return (
        <div
          className={`action-button ${active ? "active" : ""}`}
          onClick={onClick}
        >
          <span className="plus">+</span>
        </div>
      );
    };
    
    export default ActionButton;
    
    Login or Signup to reply.
  2. Modal Component

    In order for the modal component to smoothly transition into view rather than abruptly appearing, two boolean values are necessary. The first value is the open prop, which determines whether the component is mounted and triggers the useEffect to update the isVisible state value. This state value is then used to add or remove the open class.

    When the backdrop is clicked, the handleClose function is invoked, setting the isVisible value to false and triggering the onClose function once the transition has ended. The onClose function is responsible for resetting the open prop externally.

    import { useState, useEffect, PropsWithChildren, useRef } from 'react';
    import ReactDOM from 'react-dom';
    import styles from './modal.module.css';
    
    type ModalProps = PropsWithChildren<{
      open: boolean;
      onClose?: () => void;
    }>;
    
    const Modal = (props: ModalProps) => {
      const [isVisible, setIsVisible] = useState(false);
      const ref = useRef<HTMLDivElement>(null);
      const { open, onClose, children } = props;
      const className = `${styles.modal} ${isVisible ? styles.open : ''}`.trim();
    
      const handleClose = () => {
        setIsVisible(false);
        document.body.style.overflow = 'auto';
        ref.current?.addEventListener('transitionend', () => onClose?.());
      };
    
      useEffect(() => {
        if (open) {
          setIsVisible(true);
          document.body.style.overflow = 'hidden';
        }
      }, [open]);
    
      return open
        ? ReactDOM.createPortal(
            <>
              <div ref={ref} className={className}>
                <div className={styles.content}>{children}</div>
              </div>
              <div className={styles.backdrop} onClick={handleClose} />
            </>,
            document.getElementById('portal')!
          )
        : null;
    };
    
    export default Modal;
    

    CSS Module

    The CSS stylesheet includes four classes: .modal, .open, .backdrop, and .content. The .content class is not relevant to the current discussion as it only pertains to the modal’s content.

    The .modal class is responsible for giving the parent container a fixed position, with full width and height dimensions relative to the viewport. Additionally, it includes a translateY offset of 100%, which places the element off the bottom of the screen.

    The .open class is used to reset the .modal translateY property to 0, bringing the element back into view.

    The .backdrop class has an opacity of 0, but includes a transition that gradually fades it in. This is primarily for aesthetic purposes. The backdrop-filter: blur(0.25rem); style property is used to achieve the desired blurring effect.

    .modal {
      z-index: 9999;
      position: fixed;
      inset: 0;
      display: flex;
      justify-content: center;
      align-items: center;
      transform: translateY(100%);
      transition: transform 0.3s ease-out;
      pointer-events: none !important;
    }
    
    .modal.open {
      transform: translateY(0);
    }
    
    .backdrop {
      z-index: 9998;
      position: fixed;
      inset: 0;
      background-color: rgba(0, 0, 0, 0.5);
      opacity: 0;
      pointer-events: none;
      transition: opacity 0.3s ease-out;
      backdrop-filter: blur(0.25rem);
    }
    
    .modal.open ~ .backdrop {
      opacity: 1;
      pointer-events: auto;
    }
    
    .content {
      position: relative;
      z-index: 1;
      color: #333;
      background-color: #fff;
      padding: 2rem;
      border-radius: 0.5rem;
      box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.3);
      pointer-events: auto;
    }
    

    #Usage

    import React, { useState } from "react";
    import ActionButton from "../../components/ActionButton";
    import AddEventActionCard from "../../components/AddEventActionCard";
    import Modal from "../../components/Modal";
    
    import AppHeader from "../../components/AppHeader";
    import FooterNav from "../../components/FooterNav";
    import "../Upcoming_Events/UpcomingEvents.css";
    
    const UpcomingEvents = () => {
      const [click, setClick] = useState(false);
      const handleClick = () => setClick(!click);
    
      return (
        <div>
          <Modal open={click} onClose={() => setClick(false)}>
            <AddEventActionCard />
          </Modal>
          <AppHeader />
          <div className="upcoming-container">
            <div className="upcoming-card-container">
              <div className="upcoming-card-top-header">Upcoming Events</div>
              <div className="upcoming-card-top-events-scroll-container"></div>
            </div>
            <div onClick={handleClick}>
              <ActionButton />
            </div>
          </div>
          <FooterNav />
        </div>
      );
    };
    
    export default UpcomingEvents;
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search