github.com/quickfeed/quickfeed@v0.0.0-20240507093252-ed8ca812a09c/public/src/components/alerts/Alert.tsx (about)

     1  import { useEffect, useRef } from "react";
     2  import { Alert as AlertType } from "../../overmind/state";
     3  import { useActions } from "../../overmind";
     4  import React from "react";
     5  
     6  /** Alert is a component that displays a single alert.
     7   * 
     8   *  If the alert has a delay property, the alert will be removed after the delay.
     9   *  In this case, an animated circle will be displayed to indicate the time remaining before the alert is removed.
    10   * 
    11   *  The alert is removed if the alert is clicked, or if the delay has passed.
    12   *
    13   *  @param alert - The alert to be displayed
    14   */
    15  const Alert = ({ alert }: { alert: AlertType }): JSX.Element => {
    16      const circleRef = useRef<SVGCircleElement>(null);
    17      const actions = useActions();
    18  
    19      useEffect(() => {
    20          let id: ReturnType<typeof setTimeout>;
    21          if (alert.delay) {
    22              const circle = circleRef.current;
    23  
    24              // Remove the alert after the delay
    25              id = setTimeout(() => {
    26                  actions.popAlert(alert);
    27              }, alert.delay);
    28  
    29              if (circle) {
    30                  const delay: number = alert.delay
    31                  const circumference = circle.getTotalLength();
    32  
    33                  circle.style.strokeDasharray = `${circumference}px`;
    34                  circle.style.strokeDashoffset = `${circumference}px`;
    35  
    36                  const start = Date.now();
    37                  const animate = () => {
    38                      const elapsed = Date.now() - start;
    39                      const strokeDashoffset = (elapsed / delay) * circumference;
    40                      circle.style.strokeDashoffset = `${strokeDashoffset}px`;
    41                      if (elapsed < delay) {
    42                          requestAnimationFrame(animate);
    43                      }
    44                  };
    45                  requestAnimationFrame(animate);
    46              }
    47          }
    48  
    49          return () => {
    50              if (id) {
    51                  // If the alert is removed (by clicking the alert) before
    52                  // the delay has passed, the timeout will be cleared.
    53                  clearTimeout(id);
    54              }
    55          };
    56      }, []);
    57  
    58      return (
    59          <div className={`alert alert-${alert.color}`} role="button" style={{ marginTop: "20px", whiteSpace: "pre-wrap" }} onClick={() => actions.popAlert(alert)}>
    60              {alert.delay && (
    61                  <svg viewBox="0 0 50 50" style={{ width: 20, height: 20, marginRight: 20 }}>
    62                      <circle ref={circleRef} cx={25} cy={25} r={20} strokeWidth={5} fill="none" stroke="#000" />
    63                  </svg>
    64              )}
    65              {alert.text}
    66          </div>
    67      );
    68  };
    69  
    70  export default Alert;