| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475 |
- "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;
- }
|