diff --git a/shared/common/ui/customScrollbar/index.tsx b/shared/common/ui/customScrollbar/index.tsx index e0fdcd4c..6d01aa2e 100644 --- a/shared/common/ui/customScrollbar/index.tsx +++ b/shared/common/ui/customScrollbar/index.tsx @@ -18,13 +18,12 @@ export interface CustomScrollbarsProps { innerClass: string | Element | null; headerPadding?: number; scrollIgnoreLength?: number; - bottomScrollBarOffset?: number; reverse?: boolean; hideVertical?: boolean; hideHorizontal?: boolean; } -const defaultScrollSizes: [number, number] = [-1, -1]; +const defaultScrollThumbSizes: [number, number] = [-1, -1]; export function CustomScrollbars({ className, @@ -34,54 +33,20 @@ export function CustomScrollbars({ innerClass, headerPadding = 0, scrollIgnoreLength = 0, - bottomScrollBarOffset = 8, reverse, hideVertical, hideHorizontal, }: PropsWithChildren) { const ref = useRef(null); - const scrollSizes = useRef<[number, number]>(defaultScrollSizes); - const [rawScrollOffset, setRawScrollOffset] = useState(0); + const scrollThumbSizes = useRef<[number, number]>(defaultScrollThumbSizes); const [scrollOffsets, setScrollOffsets] = useState<[number, number]>(() => [ 0, 0, ]); const _scrollOffsets = useRef(scrollOffsets); const [dragging, setDragging] = useState(false); - const scrollBarTopOffset = - rawScrollOffset < scrollIgnoreLength - ? rawScrollOffset - : scrollIgnoreLength; - - const onScroll = useCallback( - (el: HTMLElement) => { - const scrollTop = - Math.max( - 0, - reverse - ? el.scrollHeight + el.scrollTop - el.clientHeight - : el.scrollTop - scrollIgnoreLength - ) / - (el.scrollHeight - scrollIgnoreLength - el.clientHeight); - - _scrollOffsets.current = [ - scrollTop * - (el.clientHeight - - scrollSizes.current[0] + - scrollBarTopOffset - - (scrollSizes.current[1] === -1 ? bottomScrollBarOffset : 14) - - headerPadding), - (el.scrollLeft / (el.scrollWidth - el.clientWidth)) * - (el.clientWidth - - scrollSizes.current[1] - - (scrollSizes.current[0] === -1 ? 8 : 14)), - ]; - setScrollOffsets(_scrollOffsets.current); - setRawScrollOffset(el.scrollTop); - }, - [headerPadding, reverse, scrollBarTopOffset] - ); + const scrollbarTopPadding = useRef(headerPadding); const onResize = useCallback(() => { const scrollEl = ( @@ -93,13 +58,15 @@ export function CustomScrollbars({ const hasV = scrollEl.scrollHeight > scrollEl.clientHeight; const hasH = scrollEl.scrollWidth > scrollEl.clientWidth; - scrollSizes.current = [ + scrollThumbSizes.current = [ hasV ? Math.max( 28, - (scrollEl.clientHeight / scrollEl.scrollHeight) * - (scrollEl.clientHeight - (hasH ? 14 : 8)) - - headerPadding + (scrollEl.clientHeight / + (scrollEl.scrollHeight - scrollIgnoreLength)) * + (scrollEl.clientHeight - + (hasH ? 14 : 8) - + scrollbarTopPadding.current) ) : -1, hasH @@ -110,10 +77,56 @@ export function CustomScrollbars({ ) : -1, ]; - onScroll(scrollEl); + return scrollEl; }, [headerPadding]); - useResize(ref, onResize); + const onScroll = useCallback( + (el: HTMLElement) => { + const newScrollbarTopPadding = scrollIgnoreLength + ? el.scrollTop < scrollIgnoreLength + ? headerPadding - el.scrollTop + : scrollIgnoreLength + : headerPadding; + const thumbSizeNeedsUpdate = + newScrollbarTopPadding != scrollbarTopPadding.current; + scrollbarTopPadding.current = newScrollbarTopPadding; + if (thumbSizeNeedsUpdate) { + onResize(); + } + + const scrollTopFraction = + Math.max( + 0, + reverse + ? el.scrollHeight + el.scrollTop - el.clientHeight + : el.scrollTop - scrollIgnoreLength + ) / + (el.scrollHeight - scrollIgnoreLength - el.clientHeight); + + const scrollableHeight = + el.clientHeight - + scrollbarTopPadding.current - + scrollThumbSizes.current[0] - + (scrollThumbSizes.current[1] === -1 ? 8 : 14); + + _scrollOffsets.current = [ + scrollTopFraction * scrollableHeight, + (el.scrollLeft / (el.scrollWidth - el.clientWidth)) * + (el.clientWidth - + scrollThumbSizes.current[1] - + (scrollThumbSizes.current[0] === -1 ? 8 : 14)), + ]; + setScrollOffsets(_scrollOffsets.current); + }, + [headerPadding, reverse, scrollIgnoreLength] + ); + + const onResizeUpdate = useCallback(() => { + const scrollEl = onResize(); + if (scrollEl) onScroll(scrollEl); + }, [onResize, onScroll]); + + useResize(ref, onResizeUpdate); const innerRef = useMemo( () => @@ -122,7 +135,7 @@ export function CustomScrollbars({ : innerClass) ?? null, [ref.current, innerClass] ); - useResize(innerRef, onResize); + useResize(innerRef, onResizeUpdate); useEffect(() => { const scrollEl = ( @@ -160,21 +173,21 @@ export function CustomScrollbars({ const initialScroll = vScroll ? el.scrollTop : el.scrollLeft; const barSize = vScroll ? ref.current!.clientHeight - - scrollSizes.current[0] - - (scrollSizes.current[1] === -1 ? 8 : 14) - + scrollThumbSizes.current[0] - + (scrollThumbSizes.current[1] === -1 ? 8 : 14) - headerPadding : ref.current!.clientWidth - - scrollSizes.current[1] - - (scrollSizes.current[0] === -1 ? 8 : 14); + scrollThumbSizes.current[1] - + (scrollThumbSizes.current[0] === -1 ? 8 : 14); const rel = initial - (vScroll ? rect.top + headerPadding : rect.left) - 4; if ( vScroll ? rel > _scrollOffsets.current[0] && - rel < _scrollOffsets.current[0] + scrollSizes.current[0] + rel < _scrollOffsets.current[0] + scrollThumbSizes.current[0] : rel > _scrollOffsets.current[1] && - rel < _scrollOffsets.current[1] + scrollSizes.current[1] + rel < _scrollOffsets.current[1] + scrollThumbSizes.current[1] ) { setDragging(true); @@ -205,13 +218,15 @@ export function CustomScrollbars({ if (vScroll) { el.scrollTop += ((rel - - (_scrollOffsets.current[0] + scrollSizes.current[0] / 2)) / + (_scrollOffsets.current[0] + + scrollThumbSizes.current[0] / 2)) / barSize) * (el.scrollHeight - el.clientHeight); } else { el.scrollLeft += ((rel - - (_scrollOffsets.current[1] + scrollSizes.current[1] / 2)) / + (_scrollOffsets.current[1] + + scrollThumbSizes.current[1] / 2)) / barSize) * (el.scrollWidth - el.clientWidth); } @@ -230,32 +245,32 @@ export function CustomScrollbars({ onMouseDownCapture={onMouseDown} > {children} - {!hideVertical && scrollSizes.current[0] !== -1 ? ( + {!hideVertical && scrollThumbSizes.current[0] !== -1 ? (
) : null} - {!hideHorizontal && scrollSizes.current[1] !== -1 ? ( + {!hideHorizontal && scrollThumbSizes.current[1] !== -1 ? (