All files / views/webviews/hooks use-vscode-element-ref.ts

0% Statements 0/17
0% Branches 0/8
0% Functions 0/4
0% Lines 0/17

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68                                                                                                                                       
import { useCallback, useEffect, useRef } from 'react';
 
/**
 * React hook for managing element refs with automatic event listener cleanup.
 * Use this for vscode-elements web components that emit custom events, or for
 * native DOM events that need to be attached outside React's synthetic event
 * system (e.g. to control propagation order).
 *
 * @example
 * ```tsx
 * function MyComponent() {
 *   const [selectedIndex, setSelectedIndex] = useState(0);
 *
 *   const tabsRef = useVscodeElementRef<VscodeTabs, { selectedIndex: number }>(
 *     'vsc-tabs-select',
 *     (element, event) => setSelectedIndex(event.detail.selectedIndex)
 *   );
 *
 *   return <vscode-tabs ref={tabsRef}>...</vscode-tabs>;
 * }
 * ```
 *
 * @param eventName The event name to listen for.
 * @param onEvent Callback invoked when the event fires.
 * @returns A ref callback to pass to the element's ref prop.
 */
export function useVscodeElementRef<E extends HTMLElement, V = any>(
	eventName: string,
	onEvent: (element: E, event: CustomEvent<V>) => void
): (element: E | null) => void {
	const elementRef = useRef<E | null>(null);
	const handlerRef = useRef(onEvent);
 
	// Keep handler ref up to date
	useEffect(() => {
		handlerRef.current = onEvent;
	}, [onEvent]);
 
	// The listener registered on the current element. Kept in a ref so detaching
	// removes the exact function that was added — a listener created fresh per
	// callback invocation would never match and silently leak.
	const listenerRef = useRef<((event: Event) => void) | null>(null);
 
	const refCallback = useCallback((element: E | null) => {
		// Cleanup previous element
		if (elementRef.current && listenerRef.current) {
			elementRef.current.removeEventListener(eventName, listenerRef.current);
			listenerRef.current = null;
		}
 
		elementRef.current = element;
 
		// Setup new element
		if (element) {
			const listener = (event: Event) => {
				if (elementRef.current) {
					handlerRef.current(elementRef.current, event as CustomEvent<V>);
				}
			};
 
			listenerRef.current = listener;
			element.addEventListener(eventName, listener);
		}
	}, [eventName]);
 
	return refCallback;
}