e4dcfc49aa
Tests / Import Check (Python 3.13) (push) Has been cancelled
Tests / Import Check (Python 3.14) (push) Has been cancelled
Tests / Python Tests (Python 3.11) (push) Has been cancelled
Tests / Python Tests (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.14) (push) Has been cancelled
Tests / Test Summary (push) Has been cancelled
Tests / Lint and Format (push) Has been cancelled
Tests / Web Node Tests (push) Has been cancelled
Tests / Import Check (Python 3.11) (push) Has been cancelled
Tests / Import Check (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.13) (push) Has been cancelled
57 lines
1.6 KiB
TypeScript
57 lines
1.6 KiB
TypeScript
"use client";
|
|
|
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
|
|
/**
|
|
* Expansion state for the collapse-to-icon toolbar selectors (persona /
|
|
* model). Expands on hover, focus or while the menu is open; on leave or
|
|
* close it LINGERS for ~1.2s before collapsing, so the user can read the
|
|
* (possibly just-changed) selection instead of having it snap shut.
|
|
*/
|
|
export function useLingerExpand(open: boolean, lingerMs = 1200) {
|
|
const [hovered, setHovered] = useState(false);
|
|
const [lingering, setLingering] = useState(false);
|
|
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
|
|
const clearTimer = useCallback(() => {
|
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
timerRef.current = null;
|
|
}, []);
|
|
|
|
/**
|
|
* Hold the expanded state for ``lingerMs`` before collapsing. Fired by
|
|
* mouse-leave automatically; menu owners should also call it wherever
|
|
* they close the menu (selection / outside click) so the just-changed
|
|
* label stays readable for a beat.
|
|
*/
|
|
const linger = useCallback(() => {
|
|
clearTimer();
|
|
setLingering(true);
|
|
timerRef.current = setTimeout(() => setLingering(false), lingerMs);
|
|
}, [clearTimer, lingerMs]);
|
|
|
|
useEffect(() => clearTimer, [clearTimer]);
|
|
|
|
const onMouseEnter = useCallback(() => {
|
|
clearTimer();
|
|
setLingering(false);
|
|
setHovered(true);
|
|
}, [clearTimer]);
|
|
|
|
const onMouseLeave = useCallback(() => {
|
|
setHovered(false);
|
|
linger();
|
|
}, [linger]);
|
|
|
|
return {
|
|
expanded: hovered || open || lingering,
|
|
linger,
|
|
triggerProps: {
|
|
onMouseEnter,
|
|
onMouseLeave,
|
|
onFocus: onMouseEnter,
|
|
onBlur: onMouseLeave,
|
|
},
|
|
};
|
|
}
|