@ui5/webcomponents-react
Version:
React Wrapper for UI5 Web Components and additional components
47 lines (45 loc) • 1.81 kB
JavaScript
import { useEffect, useRef, useState } from 'react';
export function useSyncScroll(refContent, refScrollbar, isScrollable, disabled = false) {
const isProgrammatic = useRef(false);
const [isMounted, setIsMounted] = useState(false);
// DOM scrollTop manipulation, not React state
// eslint-disable-next-line react-hooks/immutability
useEffect(() => {
if (disabled || !isScrollable) {
return;
}
const content = refContent.current;
const scrollbar = refScrollbar.current;
if (!content || !scrollbar || !isMounted) {
// Forces a second effect run so refs are populated
// eslint-disable-next-line react-hooks/set-state-in-effect
setIsMounted(true);
return;
}
// Is a React ref
// eslint-disable-next-line react-hooks/immutability
scrollbar.scrollTop = content.scrollTop;
const sync = source => {
const sourceEl = source === 'content' ? content : scrollbar;
const targetEl = source === 'content' ? scrollbar : content;
if (!isProgrammatic.current && targetEl.scrollTop !== sourceEl.scrollTop) {
isProgrammatic.current = true;
targetEl.scrollTop = sourceEl.scrollTop;
// Clear the flag on next frame
requestAnimationFrame(() => isProgrammatic.current = false);
}
};
const onScrollContent = () => sync('content');
const onScrollScrollbar = () => sync('scrollbar');
content.addEventListener('scroll', onScrollContent, {
passive: true
});
scrollbar.addEventListener('scroll', onScrollScrollbar, {
passive: true
});
return () => {
content.removeEventListener('scroll', onScrollContent);
scrollbar.removeEventListener('scroll', onScrollScrollbar);
};
}, [isMounted, refContent, refScrollbar, disabled, isScrollable]);
}