Newer
Older
gnexus-ui-kit / src / vue / composables / use-overlay-transition.js
@Eugene Sukhodolskiy Eugene Sukhodolskiy 1 day ago 1 KB Extract useOverlayTransition composable
/**
 * Shared CSS-transition cycle for overlay components (modal, drawer, ...).
 *
 * Contract (see CLAUDE.md "CSS transitions + Vue render cycle"): the initial
 * render of an opening overlay must carry the hidden state class ("a-hide")
 * first, and the visible state class ("a-show") must be applied one
 * nextTick + requestAnimationFrame later — otherwise the browser skips the
 * transition entirely.
 *
 * Usage:
 *   const { visible, closing, show, hide, cancel } = useOverlayTransition();
 *   // render nothing while !visible; class = closing ? "a-hide" : "a-show"
 *   // call show()/hide() from your open watcher, cancel() onBeforeUnmount.
 *
 * @param {Object} [options]
 * @param {number} [options.duration=300] ms to keep the element mounted while
 *   the closing transition plays.
 */
import { nextTick, ref } from "vue";

export function useOverlayTransition({ duration = 300 } = {}) {
	const visible = ref(false);
	const closing = ref(false);
	let closeTimer = null;

	const show = () => {
		window.clearTimeout(closeTimer);
		closing.value = true;
		visible.value = true;
		nextTick(() => {
			requestAnimationFrame(() => {
				closing.value = false;
			});
		});
	};

	const hide = () => {
		closing.value = true;
		closeTimer = window.setTimeout(() => {
			visible.value = false;
			closing.value = false;
		}, duration);
	};

	const cancel = () => {
		window.clearTimeout(closeTimer);
	};

	return { visible, closing, show, hide, cancel };
}

export default useOverlayTransition;