@codecademy/gamut
68.2.268.2.3-alpha.809fdd.0
dist/PopoverContainer/hooks.js~
dist/PopoverContainer/hooks.jsModified+31−24
Index: package/dist/PopoverContainer/hooks.js
===================================================================
--- package/dist/PopoverContainer/hooks.js
+++ package/dist/PopoverContainer/hooks.js
@@ -1,19 +1,33 @@
import { useEffect, useMemo } from 'react';
import { findAllAdditionalScrollingParents, findResizingParent } from './utils';
+
+/**
+ * Minimal element shape required for popover positioning.
+ * Accepts both HTMLElement and TargetRef so Popover and PopoverContainer can share hooks.
+ */
+
+/** Resolves Ref to current element; returns null for RefCallback or null ref. */
+export function getRefElement(ref) {
+ if (ref == null) return null;
+ if (typeof ref === 'function') return null;
+ return ref.current;
+}
+
+/** Casts minimal target to HTMLElement for utils that need full DOM (e.g. parentElement). */
+export function getTargetAsElement(target) {
+ return target;
+}
export const useScrollingParentsEffect = (targetRef, setTargetRect) => {
useEffect(() => {
- if (!targetRef.current) {
- return;
- }
- const target = targetRef.current;
- const scrollingParents = findAllAdditionalScrollingParents(target);
+ const target = getRefElement(targetRef);
+ if (!target) return;
+ const scrollingParents = findAllAdditionalScrollingParents(getTargetAsElement(target));
const updatePosition = () => {
- setTargetRect(targetRef?.current?.getBoundingClientRect());
+ const el = getRefElement(targetRef);
+ setTargetRect(el?.getBoundingClientRect());
};
const cleanup = [];
-
- // Add listeners to all scrolling parents (window scroll handled by useWindowScroll)
scrollingParents.forEach(parent => {
if (parent.addEventListener) {
parent.addEventListener('scroll', updatePosition, {
passive: true
@@ -27,18 +41,14 @@
}, [targetRef, setTargetRect]);
};
export const useResizingParentEffect = (targetRef, setTargetRect) => {
useEffect(() => {
- // handles movement of target within a clipped container e.g. Drawer
- if (!targetRef.current || typeof ResizeObserver === 'undefined') {
- return;
- }
- const resizingParent = findResizingParent(targetRef.current);
- if (!resizingParent?.addEventListener) {
- return;
- }
+ const target = getRefElement(targetRef);
+ if (!target || typeof ResizeObserver === 'undefined') return;
+ const resizingParent = findResizingParent(getTargetAsElement(target));
+ if (!resizingParent?.addEventListener) return;
const handler = () => {
- setTargetRect(targetRef?.current?.getBoundingClientRect());
+ setTargetRect(getRefElement(targetRef)?.getBoundingClientRect());
};
const ro = new ResizeObserver(handler);
ro.observe(resizingParent);
return () => ro.unobserve(resizingParent);
@@ -46,16 +56,13 @@
};
/**
* Memoizes the list of scrolling parent elements for a target element.
- * This avoids expensive DOM traversals and getComputedStyle calls on every render.
* Returns an empty array if the target element is not available.
*/
export const useScrollingParents = targetRef => {
return useMemo(() => {
- if (!targetRef.current) {
- return [];
- }
- return findAllAdditionalScrollingParents(targetRef.current);
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [targetRef.current]);
+ const target = getRefElement(targetRef);
+ if (!target) return [];
+ return findAllAdditionalScrollingParents(getTargetAsElement(target));
+ }, [targetRef]);
};
\ No newline at end of file