Kaynağa Gözat

feat: make toast closable and allow custom duration (#5308)

* feat: make toast closable and allow custom duration

* use Infinity to keep prevent auto close

* rename to DEFAULT_TOAST_TIMEOUT and move to toast.tsx

* fix

* set closable as false by default and fix design

* tweak css

* reuse variables

Co-authored-by: dwelle <luzar.david@gmail.com>
Aakansha Doshi 3 yıl önce
ebeveyn
işleme
76a5bb060e
3 değiştirilmiş dosya ile 55 ekleme ve 15 silme
  1. 20 4
      src/components/Toast.scss
  2. 35 10
      src/components/Toast.tsx
  3. 0 1
      src/constants.ts

+ 20 - 4
src/components/Toast.scss

@@ -2,6 +2,9 @@
 
 .excalidraw {
   .Toast {
+    $closeButtonSize: 1.2rem;
+    $closeButtonPadding: 0.4rem;
+
     animation: fade-in 0.5s;
     background-color: var(--button-gray-1);
     border-radius: 4px;
@@ -15,11 +18,24 @@
     text-align: center;
     width: 300px;
     z-index: 999999;
-  }
 
-  .Toast__message {
-    color: var(--popup-text-color);
-    white-space: pre-wrap;
+    .Toast__message {
+      padding: 0 $closeButtonSize + ($closeButtonPadding);
+      color: var(--popup-text-color);
+      white-space: pre-wrap;
+    }
+
+    .close {
+      position: absolute;
+      top: 0;
+      right: 0;
+      padding: $closeButtonPadding;
+
+      .ToolIcon__icon {
+        width: $closeButtonSize;
+        height: $closeButtonSize;
+      }
+    }
   }
 
   @keyframes fade-in {

+ 35 - 10
src/components/Toast.tsx

@@ -1,34 +1,59 @@
 import { useCallback, useEffect, useRef } from "react";
-import { TOAST_TIMEOUT } from "../constants";
+import { close } from "./icons";
 import "./Toast.scss";
+import { ToolButton } from "./ToolButton";
+
+const DEFAULT_TOAST_TIMEOUT = 5000;
 
 export const Toast = ({
   message,
   clearToast,
+  closable = false,
+  // To prevent autoclose, pass duration as Infinity
+  duration = DEFAULT_TOAST_TIMEOUT,
 }: {
   message: string;
   clearToast: () => void;
+  closable?: boolean;
+  duration?: number;
 }) => {
   const timerRef = useRef<number>(0);
-
-  const scheduleTimeout = useCallback(
-    () =>
-      (timerRef.current = window.setTimeout(() => clearToast(), TOAST_TIMEOUT)),
-    [clearToast],
-  );
+  const shouldAutoClose = duration !== Infinity;
+  const scheduleTimeout = useCallback(() => {
+    if (!shouldAutoClose) {
+      return;
+    }
+    timerRef.current = window.setTimeout(() => clearToast(), duration);
+  }, [clearToast, duration, shouldAutoClose]);
 
   useEffect(() => {
+    if (!shouldAutoClose) {
+      return;
+    }
     scheduleTimeout();
     return () => clearTimeout(timerRef.current);
-  }, [scheduleTimeout, message]);
+  }, [scheduleTimeout, message, duration, shouldAutoClose]);
 
+  const onMouseEnter = shouldAutoClose
+    ? () => clearTimeout(timerRef?.current)
+    : undefined;
+  const onMouseLeave = shouldAutoClose ? scheduleTimeout : undefined;
   return (
     <div
       className="Toast"
-      onMouseEnter={() => clearTimeout(timerRef?.current)}
-      onMouseLeave={scheduleTimeout}
+      onMouseEnter={onMouseEnter}
+      onMouseLeave={onMouseLeave}
     >
       <p className="Toast__message">{message}</p>
+      {closable && (
+        <ToolButton
+          icon={close}
+          aria-label="close"
+          type="icon"
+          onClick={clearToast}
+          className="close"
+        />
+      )}
     </div>
   );
 };

+ 0 - 1
src/constants.ts

@@ -116,7 +116,6 @@ export const IMAGE_RENDER_TIMEOUT = 500;
 export const TAP_TWICE_TIMEOUT = 300;
 export const TOUCH_CTX_MENU_TIMEOUT = 500;
 export const TITLE_TIMEOUT = 10000;
-export const TOAST_TIMEOUT = 5000;
 export const VERSION_TIMEOUT = 30000;
 export const SCROLL_TIMEOUT = 100;
 export const ZOOM_STEP = 0.1;