@underverse-ui/underverse 0.2.29 → 0.2.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -2539,6 +2539,8 @@ var Modal = ({
2539
2539
  const [isMounted, setIsMounted] = React10.useState(false);
2540
2540
  const [isVisible, setIsVisible] = React10.useState(false);
2541
2541
  const [isAnimating, setIsAnimating] = React10.useState(true);
2542
+ const mouseDownTarget = React10.useRef(null);
2543
+ const modalContentRef = React10.useRef(null);
2542
2544
  React10.useEffect(() => {
2543
2545
  setIsMounted(true);
2544
2546
  return () => setIsMounted(false);
@@ -2578,68 +2580,84 @@ var Modal = ({
2578
2580
  document.body.style.overflow = "unset";
2579
2581
  };
2580
2582
  }, [isOpen]);
2581
- const handleOverlayClick = (event) => {
2582
- if (closeOnOverlayClick) {
2583
+ const handleOverlayMouseDown = (event) => {
2584
+ mouseDownTarget.current = event.target;
2585
+ };
2586
+ const handleOverlayMouseUp = (event) => {
2587
+ const modalContent2 = modalContentRef.current;
2588
+ const mouseDownOutside = modalContent2 && !modalContent2.contains(mouseDownTarget.current);
2589
+ const mouseUpOutside = modalContent2 && !modalContent2.contains(event.target);
2590
+ if (closeOnOverlayClick && mouseDownOutside && mouseUpOutside) {
2583
2591
  onClose();
2584
2592
  }
2593
+ mouseDownTarget.current = null;
2585
2594
  };
2586
2595
  if (!isMounted || !isOpen && !isVisible) {
2587
2596
  return null;
2588
2597
  }
2589
2598
  const maxWidthClass = width ? "max-w-none" : fullWidth ? "max-w-full" : sizeStyles3[size];
2590
- const modalContent = /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: cn("fixed inset-0 z-9999 flex items-center justify-center", overlayClassName), onClick: handleOverlayClick, children: [
2591
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2592
- "div",
2593
- {
2594
- className: "absolute inset-0 bg-background/80 backdrop-blur-sm transition-opacity duration-200 ease-out",
2595
- style: {
2596
- opacity: isOpen && !isAnimating ? 1 : 0
2597
- }
2598
- }
2599
- ),
2600
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
2601
- "div",
2602
- {
2603
- className: cn(
2604
- "relative w-full rounded-lg bg-card text-card-foreground shadow-xl",
2605
- "transition-all duration-200 ease-out",
2606
- maxWidthClass,
2607
- fullWidth && "mx-0",
2608
- className
2599
+ const modalContent = /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
2600
+ "div",
2601
+ {
2602
+ className: cn("fixed inset-0 z-9999 flex items-center justify-center", overlayClassName),
2603
+ onMouseDown: handleOverlayMouseDown,
2604
+ onMouseUp: handleOverlayMouseUp,
2605
+ children: [
2606
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2607
+ "div",
2608
+ {
2609
+ className: "absolute inset-0 bg-background/80 backdrop-blur-sm transition-opacity duration-200 ease-out",
2610
+ style: {
2611
+ opacity: isOpen && !isAnimating ? 1 : 0
2612
+ }
2613
+ }
2609
2614
  ),
2610
- style: {
2611
- opacity: isOpen && !isAnimating ? 1 : 0,
2612
- transform: isOpen && !isAnimating ? "scale(1)" : "scale(0.9)",
2613
- // Thêm dòng này để tạo hiệu ứng nảy
2614
- transition: "all 300ms cubic-bezier(0.34, 1.76, 0.64, 1)",
2615
- width,
2616
- height
2617
- },
2618
- onClick: (e) => e.stopPropagation(),
2619
- children: [
2620
- (title || description || showCloseButton) && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-start justify-between p-6 pb-0", children: [
2621
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "space-y-1.5", children: [
2622
- title && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h2", { className: "text-lg font-semibold leading-none tracking-tight", children: title }),
2623
- description && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm text-muted-foreground", children: description })
2624
- ] }),
2625
- showCloseButton && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2626
- "button",
2627
- {
2628
- onClick: onClose,
2629
- className: cn(
2630
- "rounded-sm opacity-70 ring-offset-background transition-opacity",
2631
- "hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
2632
- "disabled:pointer-events-none "
2633
- ),
2634
- children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react6.X, { className: "h-4 w-4 cursor-pointer" })
2635
- }
2636
- )
2637
- ] }),
2638
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: cn("p-6", noPadding && "p-0", contentClassName), children })
2639
- ]
2640
- }
2641
- )
2642
- ] });
2615
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
2616
+ "div",
2617
+ {
2618
+ ref: modalContentRef,
2619
+ className: cn(
2620
+ "relative w-full rounded-lg bg-card text-card-foreground shadow-xl",
2621
+ "transition-all duration-200 ease-out",
2622
+ maxWidthClass,
2623
+ fullWidth && "mx-0",
2624
+ className
2625
+ ),
2626
+ style: {
2627
+ opacity: isOpen && !isAnimating ? 1 : 0,
2628
+ transform: isOpen && !isAnimating ? "scale(1)" : "scale(0.9)",
2629
+ // Thêm dòng này để tạo hiệu ứng nảy
2630
+ transition: "all 300ms cubic-bezier(0.34, 1.76, 0.64, 1)",
2631
+ width,
2632
+ height
2633
+ },
2634
+ onClick: (e) => e.stopPropagation(),
2635
+ children: [
2636
+ (title || description || showCloseButton) && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-start justify-between p-6 pb-0", children: [
2637
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "space-y-1.5", children: [
2638
+ title && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h2", { className: "text-lg font-semibold leading-none tracking-tight", children: title }),
2639
+ description && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm text-muted-foreground", children: description })
2640
+ ] }),
2641
+ showCloseButton && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2642
+ "button",
2643
+ {
2644
+ onClick: onClose,
2645
+ className: cn(
2646
+ "rounded-sm opacity-70 ring-offset-background transition-opacity",
2647
+ "hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
2648
+ "disabled:pointer-events-none "
2649
+ ),
2650
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react6.X, { className: "h-4 w-4 cursor-pointer" })
2651
+ }
2652
+ )
2653
+ ] }),
2654
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: cn("p-6", noPadding && "p-0", contentClassName), children })
2655
+ ]
2656
+ }
2657
+ )
2658
+ ]
2659
+ }
2660
+ );
2643
2661
  return typeof window !== "undefined" ? (0, import_react_dom.createPortal)(modalContent, document.body) : null;
2644
2662
  };
2645
2663
  var Modal_default = Modal;
@@ -2670,11 +2688,6 @@ var ToastProvider = ({ children, position = "top-right", maxToasts = 5 }) => {
2670
2688
  const updated = [newToast, ...prev];
2671
2689
  return updated.slice(0, maxToasts);
2672
2690
  });
2673
- if (toast.duration !== 0) {
2674
- setTimeout(() => {
2675
- removeToast(id);
2676
- }, toast.duration || 5e3);
2677
- }
2678
2691
  },
2679
2692
  [maxToasts, removeToast]
2680
2693
  );
@@ -2695,33 +2708,31 @@ var ToastComponent = ({ toast, onRemove }) => {
2695
2708
  const [isVisible, setIsVisible] = (0, import_react8.useState)(false);
2696
2709
  const [progress, setProgress] = (0, import_react8.useState)(100);
2697
2710
  const [paused, setPaused] = (0, import_react8.useState)(false);
2698
- const [startTs] = (0, import_react8.useState)(() => Date.now());
2699
2711
  const total = toast.duration && toast.duration > 0 ? toast.duration : 5e3;
2700
- const [remaining, setRemaining] = (0, import_react8.useState)(total);
2712
+ const endTsRef = (0, import_react8.useRef)(Date.now() + total);
2713
+ const remainingRef = (0, import_react8.useRef)(total);
2714
+ const pausedRef = (0, import_react8.useRef)(false);
2715
+ const handleRemove = (0, import_react8.useCallback)(() => {
2716
+ setIsVisible(false);
2717
+ setTimeout(() => onRemove(toast.id), 150);
2718
+ }, [onRemove, toast.id]);
2701
2719
  (0, import_react8.useEffect)(() => {
2702
2720
  setIsVisible(true);
2703
2721
  if (toast.duration === 0) return;
2704
- let raf;
2705
- const tick = () => {
2706
- if (!paused) {
2707
- const elapsed = Date.now() - startTs;
2708
- const remain = Math.max(total - elapsed, 0);
2709
- setRemaining(remain);
2722
+ remainingRef.current = total;
2723
+ endTsRef.current = Date.now() + total;
2724
+ const intervalId = window.setInterval(() => {
2725
+ if (!pausedRef.current) {
2726
+ const remain = Math.max(endTsRef.current - Date.now(), 0);
2727
+ remainingRef.current = remain;
2710
2728
  setProgress(remain / total * 100);
2711
2729
  if (remain === 0) {
2712
2730
  handleRemove();
2713
- return;
2714
2731
  }
2715
2732
  }
2716
- raf = requestAnimationFrame(tick);
2717
- };
2718
- raf = requestAnimationFrame(tick);
2719
- return () => cancelAnimationFrame(raf);
2720
- }, []);
2721
- const handleRemove = () => {
2722
- setIsVisible(false);
2723
- setTimeout(() => onRemove(toast.id), 150);
2724
- };
2733
+ }, 50);
2734
+ return () => window.clearInterval(intervalId);
2735
+ }, [handleRemove, toast.duration, total]);
2725
2736
  const typeConfig = {
2726
2737
  success: {
2727
2738
  icon: import_lucide_react7.CheckCircle,
@@ -2757,8 +2768,18 @@ var ToastComponent = ({ toast, onRemove }) => {
2757
2768
  ),
2758
2769
  role: "status",
2759
2770
  "aria-live": toast.type === "error" ? "assertive" : "polite",
2760
- onMouseEnter: () => setPaused(true),
2761
- onMouseLeave: () => setPaused(false),
2771
+ onMouseEnter: () => {
2772
+ if (toast.duration === 0) return;
2773
+ pausedRef.current = true;
2774
+ remainingRef.current = Math.max(endTsRef.current - Date.now(), 0);
2775
+ setPaused(true);
2776
+ },
2777
+ onMouseLeave: () => {
2778
+ if (toast.duration === 0) return;
2779
+ pausedRef.current = false;
2780
+ endTsRef.current = Date.now() + remainingRef.current;
2781
+ setPaused(false);
2782
+ },
2762
2783
  children: [
2763
2784
  /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-start gap-3 p-4", children: [
2764
2785
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Icon, { className: cn("h-5 w-5 mt-0.5 shrink-0", config.iconClassName) }),