Openstatus www.openstatus.dev
at 4c0f4c00a38753a5d0dfd7e7b7b7706dec6f1503 87 lines 2.5 kB view raw
1import * as React from "react"; 2 3/** 4 * A utility to compose multiple event handlers into a single event handler. 5 * Run originalEventHandler first, then ourEventHandler unless prevented. 6 */ 7function composeEventHandlers<E>( 8 originalEventHandler?: (event: E) => void, 9 ourEventHandler?: (event: E) => void, 10 { checkForDefaultPrevented = true } = {}, 11) { 12 return function handleEvent(event: E) { 13 originalEventHandler?.(event); 14 15 if ( 16 checkForDefaultPrevented === false || 17 !(event as unknown as Event).defaultPrevented 18 ) { 19 return ourEventHandler?.(event); 20 } 21 }; 22} 23 24/** 25 * @see https://github.com/radix-ui/primitives/blob/main/packages/react/compose-refs/src/compose-refs.tsx 26 */ 27 28type PossibleRef<T> = React.Ref<T> | undefined; 29 30/** 31 * Set a given ref to a given value. 32 * This utility takes care of different types of refs: callback refs and RefObject(s). 33 */ 34function setRef<T>(ref: PossibleRef<T>, value: T) { 35 if (typeof ref === "function") { 36 return ref(value); 37 } 38 39 if (ref !== null && ref !== undefined) { 40 ref.current = value; 41 } 42} 43 44/** 45 * A utility to compose multiple refs together. 46 * Accepts callback refs and RefObject(s). 47 */ 48function composeRefs<T>(...refs: PossibleRef<T>[]): React.RefCallback<T> { 49 return (node) => { 50 let hasCleanup = false; 51 const cleanups = refs.map((ref) => { 52 const cleanup = setRef(ref, node); 53 if (!hasCleanup && typeof cleanup === "function") { 54 hasCleanup = true; 55 } 56 return cleanup; 57 }); 58 59 // React <19 will log an error to the console if a callback ref returns a 60 // value. We don't use ref cleanups internally so this will only happen if a 61 // user's ref callback returns a value, which we only expect if they are 62 // using the cleanup functionality added in React 19. 63 if (hasCleanup) { 64 return () => { 65 for (let i = 0; i < cleanups.length; i++) { 66 const cleanup = cleanups[i]; 67 if (typeof cleanup === "function") { 68 cleanup(); 69 } else { 70 setRef(refs[i], null); 71 } 72 } 73 }; 74 } 75 }; 76} 77 78/** 79 * A custom hook that composes multiple refs. 80 * Accepts callback refs and RefObject(s). 81 */ 82function useComposedRefs<T>(...refs: PossibleRef<T>[]): React.RefCallback<T> { 83 // eslint-disable-next-line react-hooks/exhaustive-deps 84 return React.useCallback(composeRefs(...refs), refs); 85} 86 87export { composeEventHandlers, composeRefs, useComposedRefs };