Prechádzať zdrojové kódy

feat(hooks): add useDebouncedVisibility hook for controlled visibility transitions

Code_Uwe 1 týždeň pred
rodič
commit
4cb9a671dd
1 zmenil súbory, kde vykonal 75 pridanie a 0 odobranie
  1. 75 0
      lib/frontend/hooks/useDebouncedVisibility.js

+ 75 - 0
lib/frontend/hooks/useDebouncedVisibility.js

@@ -0,0 +1,75 @@
+"use client";
+
+import React from "react";
+
+/**
+ * useDebouncedVisibility
+ *
+ * Delays the "show" transition and optionally enforces a minimum visible time.
+ *
+ * Typical use cases:
+ * - Avoid flicker for fast loading indicators (e.g. show only after 300ms).
+ * - Keep the indicator visible for at least X ms once shown (avoid blink).
+ *
+ * @param {boolean} isActive
+ * @param {{ delayMs?: number, minVisibleMs?: number }} options
+ * @returns {boolean}
+ */
+export function useDebouncedVisibility(
+	isActive,
+	{ delayMs = 200, minVisibleMs = 0 } = {},
+) {
+	const [visible, setVisible] = React.useState(false);
+
+	const showTimerRef = React.useRef(null);
+	const hideTimerRef = React.useRef(null);
+	const visibleSinceRef = React.useRef(0);
+
+	React.useEffect(() => {
+		if (showTimerRef.current) {
+			clearTimeout(showTimerRef.current);
+			showTimerRef.current = null;
+		}
+		if (hideTimerRef.current) {
+			clearTimeout(hideTimerRef.current);
+			hideTimerRef.current = null;
+		}
+
+		if (isActive) {
+			if (!visible) {
+				showTimerRef.current = setTimeout(
+					() => {
+						visibleSinceRef.current = Date.now();
+						setVisible(true);
+					},
+					Math.max(0, Number(delayMs) || 0),
+				);
+			}
+			return;
+		}
+
+		if (!visible) return;
+
+		const now = Date.now();
+		const elapsed = now - (visibleSinceRef.current || now);
+		const minMs = Math.max(0, Number(minVisibleMs) || 0);
+
+		if (elapsed >= minMs) {
+			setVisible(false);
+			return;
+		}
+
+		hideTimerRef.current = setTimeout(() => {
+			setVisible(false);
+		}, minMs - elapsed);
+	}, [isActive, delayMs, minVisibleMs, visible]);
+
+	React.useEffect(() => {
+		return () => {
+			if (showTimerRef.current) clearTimeout(showTimerRef.current);
+			if (hideTimerRef.current) clearTimeout(hideTimerRef.current);
+		};
+	}, []);
+
+	return visible;
+}