fix: calc scrollheight dynamically for tabs change

This commit is contained in:
jialin
2025-11-10 20:40:15 +08:00
parent 285e274818
commit 9eb1dba4a1
14 changed files with 283 additions and 77 deletions
+56 -9
View File
@@ -1,4 +1,5 @@
import { useCallback } from 'react';
import { useMemoizedFn } from 'ahooks';
import { useCallback, useRef, useState } from 'react';
interface ScrollOptions {
wait?: number;
@@ -12,14 +13,27 @@ export default function useScrollAfterExpand({
activeKey,
setActiveKey,
segmentOptions,
defaultWait = 300
defaultWait = 300,
segmentedTop = { top: 0, offsetTop: 96 },
getScrollElementScrollableHeight
}: {
form: any;
activeKey: string[];
setActiveKey: React.Dispatch<React.SetStateAction<string[]>>;
segmentOptions: { value: string; field: string }[];
getScrollElementScrollableHeight?: () => {
scrollHeight: number;
scrollTop: number;
};
defaultWait?: number;
segmentedTop: {
top: number;
offsetTop: number;
};
}) {
const [holderHeight, setHolderHeight] = useState<number>(0);
const boxHeightRef = useRef<number>(0);
const scrollToElement = useCallback(
(
el: HTMLElement,
@@ -46,7 +60,11 @@ export default function useScrollAfterExpand({
[]
);
const scrollToSegment = useCallback(
/**
* due to the scrollheight changes after expanding the segment and including the holder height.
*
*/
const scrollToSegment = useMemoizedFn(
async (val: string, options?: ScrollOptions) => {
if (!activeKey.includes(val)) {
setActiveKey((prev) => [...prev, val]);
@@ -55,19 +73,48 @@ export default function useScrollAfterExpand({
});
}
await new Promise(requestAnimationFrame);
const current = segmentOptions.find((item) => item.value === val);
if (!current?.field) return;
const el = document.querySelector(
await new Promise(requestAnimationFrame);
const el: HTMLElement | null = document.querySelector(
`[data-field="${current.field}"]`
) as HTMLElement | null;
const targetRectTop = el?.getBoundingClientRect().top || 0;
const scroller = getScrollElementScrollableHeight?.() || {
scrollHeight: 0,
scrollTop: 0
};
// remaining scroll height
const remainingScrollHeight = scroller.scrollHeight - scroller.scrollTop;
// total distance from the top of the scroller to the target element
const offsetDistance =
targetRectTop - segmentedTop.offsetTop - segmentedTop.top;
let boxHeight = 0;
// verify boxHeight is correct, if setting the boxHeight causes the element to be hidden, use the previous boxHeight
if (offsetDistance <= 0) {
boxHeight = boxHeightRef.current;
} else {
boxHeight =
offsetDistance - remainingScrollHeight + boxHeightRef.current;
}
// update boxHeightRef
boxHeightRef.current = boxHeight;
setHolderHeight(boxHeight);
await new Promise(requestAnimationFrame);
if (el) scrollToElement(el, options);
},
[activeKey, setActiveKey, segmentOptions, defaultWait, scrollToElement]
}
);
return { scrollToSegment };
return { scrollToSegment, holderHeight };
}