···11+import * as React from "react";
22+33+type PossibleRef<T> = React.Ref<T> | undefined;
44+55+/**
66+ * Set a given ref to a given value
77+ * This utility takes care of different types of refs: callback refs and RefObject(s)
88+ */
99+function setRef<T>(ref: PossibleRef<T>, value: T) {
1010+ if (typeof ref === "function") {
1111+ return ref(value);
1212+ }
1313+1414+ if (ref !== null && ref !== undefined) {
1515+ ref.current = value;
1616+ }
1717+}
1818+1919+/**
2020+ * A utility to compose multiple refs together
2121+ * Accepts callback refs and RefObject(s)
2222+ */
2323+function composeRefs<T>(...refs: PossibleRef<T>[]): React.RefCallback<T> {
2424+ return (node) => {
2525+ let hasCleanup = false;
2626+ const cleanups = refs.map((ref) => {
2727+ const cleanup = setRef(ref, node);
2828+ if (!hasCleanup && typeof cleanup === "function") {
2929+ hasCleanup = true;
3030+ }
3131+ return cleanup;
3232+ });
3333+3434+ // React <19 will log an error to the console if a callback ref returns a
3535+ // value. We don't use ref cleanups internally so this will only happen if a
3636+ // user's ref callback returns a value, which we only expect if they are
3737+ // using the cleanup functionality added in React 19.
3838+ if (hasCleanup) {
3939+ return () => {
4040+ for (let i = 0; i < cleanups.length; i++) {
4141+ const cleanup = cleanups[i];
4242+ if (typeof cleanup === "function") {
4343+ cleanup();
4444+ } else {
4545+ setRef(refs[i], null);
4646+ }
4747+ }
4848+ };
4949+ }
5050+ };
5151+}
5252+5353+/**
5454+ * A custom hook that composes multiple refs
5555+ * Accepts callback refs and RefObject(s)
5656+ */
5757+function useComposedRefs<T>(...refs: PossibleRef<T>[]): React.RefCallback<T> {
5858+ // biome-ignore lint/correctness/useExhaustiveDependencies: we want to memoize by all values
5959+ return React.useCallback(composeRefs(...refs), refs);
6060+}
6161+6262+export { composeRefs, useComposedRefs };