Skip to content

Commit f599f56

Browse files
[7.x] [Security Solution][Resolver] Update the resolver element ref on scroll events if the position of the element has changed within the page (#72461) (#72790)
1 parent 8b5c6b3 commit f599f56

File tree

1 file changed

+49
-5
lines changed
  • x-pack/plugins/security_solution/public/resolver/view

1 file changed

+49
-5
lines changed

x-pack/plugins/security_solution/public/resolver/view/use_camera.ts

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -281,17 +281,61 @@ export function useCamera(): {
281281
* handle that.
282282
*/
283283
export function useAutoUpdatingClientRect(): [DOMRect | null, (node: Element | null) => void] {
284+
// This hooks returns `rect`.
284285
const [rect, setRect] = useState<DOMRect | null>(null);
285-
// Using state as ref.current update does not trigger effect hook when reset
286+
287+
const { ResizeObserver, requestAnimationFrame } = useContext(SideEffectContext);
288+
289+
// Keep the current DOM node in state so that we can create a ResizeObserver for it via `useEffect`.
286290
const [currentNode, setCurrentNode] = useState<Element | null>(null);
287291

292+
// `ref` will be used with a react element. When the element is available, this function will be called.
288293
const ref = useCallback((node: Element | null) => {
294+
// track the node in state
289295
setCurrentNode(node);
290-
if (node !== null) {
291-
setRect(node.getBoundingClientRect());
292-
}
293296
}, []);
294-
const { ResizeObserver } = useContext(SideEffectContext);
297+
298+
/**
299+
* Any time the DOM node changes (to something other than `null`) recalculate the DOMRect and set it (which will cause it to be returned from the hook.
300+
* This effect re-runs when the DOM node has changed.
301+
*/
302+
useEffect(() => {
303+
if (currentNode !== null) {
304+
// When the DOM node is received, immedaiately calculate its DOM Rect and return that
305+
setRect(currentNode.getBoundingClientRect());
306+
}
307+
}, [currentNode]);
308+
309+
/**
310+
* When scroll events occur, recalculate the DOMRect. DOMRect represents the position of an element relative to the viewport, so that may change during scroll (depending on the layout.)
311+
* This effect re-runs when the DOM node has changed.
312+
*/
313+
useEffect(() => {
314+
// the last scrollX and scrollY values that we handled
315+
let previousX: number = window.scrollX;
316+
let previousY: number = window.scrollY;
317+
318+
const handleScroll = () => {
319+
requestAnimationFrame(() => {
320+
// synchronously read from the DOM
321+
const currentX = window.scrollX;
322+
const currentY = window.scrollY;
323+
324+
if (currentNode !== null && (previousX !== currentX || previousY !== currentY)) {
325+
setRect(currentNode.getBoundingClientRect());
326+
}
327+
328+
previousX = currentX;
329+
previousY = currentY;
330+
});
331+
};
332+
333+
window.addEventListener('scroll', handleScroll, { passive: true });
334+
return () => {
335+
window.removeEventListener('scroll', handleScroll);
336+
};
337+
}, [currentNode, requestAnimationFrame]);
338+
295339
useEffect(() => {
296340
if (currentNode !== null) {
297341
const resizeObserver = new ResizeObserver((entries) => {

0 commit comments

Comments
 (0)